Simply put, on UNIX you have the concept of processes and programs. A process is what the program runs in.
The simple idea of ββa UNIX "execution model" is that you can perform two operations.
The first is fork() , which creates a completely new process that contains a duplicate of the current program, including its state. There are several differences between the processes that allow them to find out who is the parent and who is the child.
The second is exec() , which replaces the program in the current process with a new program.
From these two simple operations, you can build the entire UNIX runtime model.
To add some details to the above:
Using fork() and exec() illustrates the spirit of UNIX in the sense that it provides a very simple way to start new processes.
The fork() call creates almost a duplicate of the current process, identical in almost all respects (not all are copied, for example, due to resource limitations in some implementations, but the idea is to create the closest possible copy). One process calls fork() , while two processes return from it - it sounds weird, but it's really pretty elegant
The new process (called the child) receives a different process identifier (PID) and has the PID of the old process (parent) as the parent PID (PPID).
Since the two processes are currently running exactly in the same code, they should be able to say that there is something - the fork() return code provides this information - the child gets 0, the parent gets the PID of the child (if fork() fails, the child is not created, and the parent code receives the error code). Thus, the parent knows the PID of the child and can communicate with it, destroy it, wait for it, etc. (A child process can always find its parent process by calling getppid() ).
The call to exec() replaces all the current contents of the process with a new program. It loads the program into the current process space and runs it from the entry point.
Thus, fork() and exec() are often used sequentially to start a new program as a child of the current process. Shells usually do this whenever you try to run a program such as find - the shell branches, then the child loads the find program into memory, setting up all the command line arguments, standard I / O, etc.
But they are not required to be used together. It is perfectly acceptable for a program to call fork() without the following exec() if, for example, the program contains both parent and child code (you need to be careful what you do, each implementation may have limitations). This has been used quite a bit (and is still in use) for daemons that simply listen on the TCP port and design their copy to process a specific request, while the parent returns to listening. In this situation, the program contains both parent and child code.
Similarly, programs that know they have finished and just want to run another program do not need fork() , exec() , and then wait()/waitpid() for the child. They can simply load the child directly into the current process space using exec() .
Some UNIX implementations have optimized fork() , which uses what they call copy-on-write. It is a trick to defer copying the process space to fork() until the program tries to change something in that space. this is useful for programs that use only fork() , not exec() , because they do not need to copy the entire process space. On Linux, fork() only copies page tables and a new task structure; exec() does the bulk of the work of "dividing" the memory of two processes.
If exec is invoked according to fork (and this is what happens mostly), it invokes a write to the process space and then is copied to the child process.
Linux also has vfork() , even more optimized, that shares almost everything between the two processes. Because of this, there are certain restrictions on what the child can do, and the parent stops until the child calls exec() or _exit() .
The parent must be stopped (and the child is not allowed to return from the current function), since the two processes even share the same stack. This is slightly more efficient for the classic fork() use case, followed immediately by exec() .
Note that there is a whole family of exec calls ( execl , execle , execve , etc.), but exec in the context here means any of them.
The following diagram illustrates a typical fork/exec operation in which the bash used to list a directory using the ls :
+--------+ | 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