0

In the following link, and in many others like it, exec is often described as:

Differences between fork and exec

The exec call is a way to basically replace the entire current process with a new program. It loads the program into the current process space and runs it from the entry point.

Is exec() actually replacing the entire program or is it more like executing a subroutine that includes the main() of the selected program, and then returning to its original context and operations?

Wouldn't it be more accurate to describe exec() as a subroutine that includes the main() of the chosen process, as opposed to completely replacing it, by incorporating the chosen process's code into the current process?

# original example taken from the aforementioned link
+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V

# wouldn't the following technically be more accurate?
# or am I misunderstanding some details with exec()?
+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | bash   |
    |                  | ls()   |
    |                  +--------+
    |                      |
    |                      | ls completes
    |                      | and the program context returns to bash's
    |                      |
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | bash   |
    |                  +--------+
    |                      |
    V                      |
+--------+                 | the child branch of bash calls exit/return
| pid=7  |                 | that follows the exec statement in the part
| ppid=4 | <---------------+ "if (fork() == 0) {...}" code branch
| bash   |
+--------+
    |
    | continues
    V

I realize there is an error in my understanding, because if what I have said is true, all spawned processes should be instances of bash or init when viewed in htop, yet they are not (each process has its own name, such as ls or htop).

So does exec() truly replace the entire program that calls it with a new one? Does calling exec() terminate the program that calls it? What is the mechanism behind this?

AlanSTACK
  • 5,525
  • 3
  • 40
  • 99
  • It looks like the link you provided is wrong. – Anton Savin Feb 18 '16 at 18:49
  • From the man page: "The exec() family of functions replaces the current process image with a new process image." Also, "The exec() functions return only if an error has occurred." – Fred Larson Feb 18 '16 at 18:51

4 Answers4

6

exec does replace the current program with a new one. The process is completely overwritten. There is no state left to return to. But it also isn't accurate to say that exec "implicitly kills" the process that calls it. The process continues to execute; it's just that it's now running a different program. It retains its old process ID.

The way to execute a new process as a "subroutine" is to first fork, then have the child process do exec, then have the parent process wait for the child process's termination. That's what happens when you type a command at the shell. It's also what the system library function does under the hood.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
3

Your understanding is incorrect. The exec family of functions completely replaces the calling process with the new process. In fact, if an exec function returns, then an error occured while starting the new process.

The new process keeps the process ID of the old process as well as any open file descriptors. Everything else is replaced.

It is not the same as "calling" the new program. When the new program returns from main (or calls exit) the process ends. It does not "return" to the program that called it.

dbush
  • 205,898
  • 23
  • 218
  • 273
2

Does it really replace the entire program though? Yes, it does. It's possible to replace that entire program with itself, thus restarting the same program from the beginning (and partially giving you the conception you seem to have).

Isnt it just more like executing a subroutine/function call that just happens to embody the main() of the selected program? - before returning back into its own context and operations? No. When the exec() family of functions do their job, the function never returns. Any return value indicates a failure.

Both fork() and the exec family are separate operations and have valid reasons to stand alone. That said, they're often used together, where fork() creates a new process such that:

  • both are now executing starting from (effectively) the return of the fork() statement (actually somewhere inside the kernel, which causes two processes to return inside the fork() statement)
  • one of them returns 0 from fork() while the other returns the process id of the new process
  • one of them, keyed on the return from fork(), calls exec() because the intention was just to spawn a new process/program without stopping the first one.
mah
  • 39,056
  • 9
  • 76
  • 93
1

Does it really replace the entire program though?

Yes.

Isnt it just more like executing a subroutine/function call that just happens to embody the main() of the selected program? - before returning back into its own context and operations?

No.

so wouldnt a more appropriate description be, just a subroutine that embodies main()? (e.g. incorporating the chosen processes code into the current proccesses? As opposed to full on "replacing it"?

No.

Now obviously there is a flaw in my understanding. Since if what I have said above is correct, then shouldnt all spawned proccesses be all just instances of bash or init when I look into htop? They arent (they all have their own names e.g. ls or htop)

Exactly.

So does exec() actually replace the entire program that calls it w/ a new one?

Yes.

So like calling exec() implicitly kills the program which calls it?

Not quite. It replaces the program with a different one, exactly what you read, "[i]t loads the program into the current process space and runs it from the entry point".

This is kind of a strange question. You've described everything accurately and then asked whether or not it actually works a completely different way that you appear to have made up. But you don't explain why you think it works that other way.

Yes, it works the way it was described to you, not the way you seem to have made up. If there's some reason you think the description is incorrect and it actually works some other way, you haven't told us what it is.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • I forgot the point where exec() only returns on failure. So I was tripping over myself with partially composed views – AlanSTACK Feb 18 '16 at 19:30