The system call vfork() is either a truly bletcherous hack for efficiency or a uniquely durable optimization for the common case. It's a BSD thing, really, but SVR4.2 introduced it into the world of commercial UN*Xes. An implementer can always define vfork() to be just the fork() system call. And almost all versions of standards and documentation constantly threaten to remove it in some future version.

A very common case of using fork is to spawn a new, different, process. This is what the standard function system does to run a command; it's also called "fork-then-exec*": the process calls fork, and immediately in the child process calls one of the exec* functions (your choice out of the alphabet soup of suffixes in execv, execvp, execl, execlp or execle...). The UN*X process model splits the task of "system" into "fork" and "exec" because some cases want to put various other system calls between the two. But the most common case is just system. For instance, the program "make" will perform hundreds or thousands of calls to system, one for each command executed.

A process which forks gets two identical but separate copies of its address space. fork used to create a copy of all of the process' memory. A process which is forking only in order to immediately call exec will pay the price of duplicating all its memory -- and immediately overwrite it with the new program's memory after exec'ing! This was considered very wasteful, so vfork was invented. After vfork, both parent and child processes share the same address space. This situation would normally be untenable: If one of the processes changes data, the other will see the change. If one of the processes returns from a function and calls another function, it will likely muck up the other's stack; when the other process tries to use the stack, bad things will happen. There is no safe way to proceed under these circumstances, so vfork adds another restriction: the parent process will not run until the child process executes a system call. So "vfork-then-exec*" is (barely) safe: the child process will safely call its exec before the parent gets a chance to run, so its data is safe until then. And if the child process changes nothing in the shared memory space, then its parent is also safe.

Modern POSIX includes vfork (with the expected (and ignored) warning that it may well be removed "Some Day") with rather terse semantics: The child may only modify the pid_t variable storing the return value of vfork, and it may not return from a function or call any function other than exec* or _exit. (Calling exit is not permitted, as it will try to flush the stdio file buffers, which are shared with the parent!). In other words, you can only really use vfork to implement system. No mention is made of the child running before the parent, presumably to allow systems to treat vfork as an alias to fork (many do, and even more did). But if the shared memory space approach to implementing vfork is used, then presumably this will be guaranteed; consult specific system documentation to make sure.

COW techniques make fork quite fast, and were initially considered to obviate the need for vfork. But fork still takes time linear in the amount of memory space: If a process accesses m words of memory, then its page tables take up Θ(m) space; creating a new copy and setting the COW bit will take Θ(m) time and space. Once the child tries to run, it needs to copy at least one page (the page storing the return value from vfork), so another page fault is generated. All this takes time. The constant involved is quite small but not negligible (typical pages are 4KB or 8KB, so a process using 32MB of RAM for its data will need 8192 or 4096 pages). Mucking about with page tables can be expensive, so some architects say vfork is still useful. Optimizing the quite common case of system is (evidently) deemed worthwhile by The Implementers: A NetBSD page claims it can shave several seconds off a build of libc.

But the optimization it allows is -- inevitably -- always allowed to overrule matters of good taste. BSD 4.4 made it an alias to fork, as above, but e.g. NetBSD revived it in 1.3.

Log in or register to write something here or to contact authors.