Search code examples
forkexec

Is exec() more like a replacement or a subroutine?


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?


Solution

  • 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.