Man pages are usually terse reference documents. Wikipedia is a better place to turn to for conceptual explanations.
Fork duplicates a process: it creates a child process which is almost identical to the parent process (the most obvious difference is that the new process has a different process ID). In particular, fork (conceptually) must copy all the parent process's memory.
As this is rather costly, vfork was invented to handle a common special case where the copy is not necessary. Often, the first thing the child process does is to load a new program image, so this is what happens:
if (fork()) {
# parent process …
} else {
# child process (with a new copy of the process memory)
execve("/bin/sh", …); # discard the process memory
}
The execve
call loads a new executable program, and this replaces the process's code and data memory by the code of the new executable and a fresh data memory. So the whole memory copy created by fork
was all for nothing.
Thus the vfork
call was invented. It does not make a copy of the memory. Therefore vfork
is cheap, but it's hard to use since you have to make sure you don't access any of the process's stack or heap space in the child process. Note that even reading could be a problem, because the parent process keeps executing. For example, this code is broken (it may or may not work depending on whether the child or the parent gets a time slice first):
if (vfork()) {
# parent process
cmd = NULL; # modify the only copy of cmd
} else {
# child process
execve("/bin/sh", "sh", "-c", cmd, (char*)NULL); # read the only copy of cmd
}
Since the invention of vfork, better optimizations have been invented. Most modern systems, including Linux, use a form of copy-on-write, where the pages in the process memory are not copied at the time of the fork
call, but later when the parent or child first writes to the page. That is, each page starts out as shared, and remains shared until either process writes to that page; the process that writes gets a new physical page (with the same virtual address). Copy-on-write makes vfork mostly useless, since fork
won't make any copy in the cases where vfork
would be usable.
Linux does retain vfork. The fork
system call must still make a copy of the process's virtual memory table, even if it doesn't copy the actual memory; vfork
doesn't even need to do this. The performance improvement is negligible in most applications.
It's to simplify the interface. The alternative to fork
and exec
would be something like Windows' CreateProcess function. Notice how many parameters CreateProcess
has, and many of them are structs with even more parameters. This is because everything you might want to control about the new process has to be passed to CreateProcess
. In fact, CreateProcess
doesn't have enough parameters, so Microsoft had to add CreateProcessAsUser and CreateProcessWithLogonW.
With the fork/exec
model, you don't need all those parameters. Instead, certain attributes of the process are preserved across exec
. This allows you to fork
, then change whatever process attributes you want (using the same functions you'd use normally), and then exec
. In Linux, fork
has no parameters, and execve
has only 3: the program to run, the command line to give it, and its environment. (There are other exec
functions, but they're just wrappers around execve
provided by the C library to simplify common use cases.)
If you want to start a process with a different current directory: fork
, chdir
, exec
.
If you want to redirect stdin/stdout: fork
, close/open files, exec
.
If you want to switch users: fork
, setuid
, exec
.
All these things can be combined as needed. If somebody comes up with a new kind of process attribute, you don't have to change fork
and exec
.
As larsks mentioned, most modern Unixes use copy-on-write, so fork
doesn't involve significant overhead.
Best Answer
As seen earlier,
vfork
does not allow the child process to access the parent's memory.exit
is a C library function (that's why it's often written asexit(3)
). It performs various cleanup tasks such as flushing and closing C streams (the files open through functions declared instdio.h
) and executing user-specified functions registered withatexit
. All these tasks involve reading and writing to the process memory._exit
exits without cleanup. It's directly a system call (which is why it's written as_exit(2)
), typically implemented by placing the system call number in a processor register and executing a particular processor instruction (branching to the system call handler). This doesn't need to access the process memory, so it's safe to do aftervfork
.After
fork
, there is no such restriction: the parent and child process are now completely autonomous.