Duplicating and Replacing the Process Image
fork() creates a new process as a duplicate of the calling process. After fork(), both the parent and child execute the same code, but with separate memory and resources. fork() returns twice: it returns the child's PID to the parent, and 0 to the child. The child inherits a copy of the parent's file descriptors, environment variables, and signal handlers.
exec() replaces the current process's image with a new program. It loads the executable from disk, initializes its memory (code, heap, stack), and jumps to the entry point. exec() does not create a new process; it overwrites the current process in place. If exec() succeeds, the calling code never runs again. If it fails, it returns an error to the original code.
The Unix Pattern for Spawning Programs
In Unix shells and system services, spawning a new program is implemented as fork+exec. The shell forks, the child process execs the requested program, and the parent waits. This design separates process creation from program loading, and leverages copy-on-write to make fork() cheap when the child will exec() immediately.
Modern alternatives (posix_spawn, Windows CreateProcess) perform fork+exec atomically, avoiding the intermediate state. But fork+exec is still the standard on Unix systems, visible in system() calls, shell script execution, and daemon spawning. Understanding it is essential to understanding process lifecycle in Unix.