Process Creation: fork() and exec()

College Depth 63 in the knowledge graph I know this Set as goal
Unlocks 60 downstream topics
system-calls process-lifecycle unix-api

Core Idea

Processes are created via system calls like fork() (Unix/Linux) or CreateProcess() (Windows). fork() creates a child process as a copy of the parent; exec() replaces the current process image with a new program. Together, they enable process spawning and program execution in Unix-like systems.

Common Misconceptions

fork() returns twice (it does: once in the parent returning the child's PID, once in the child returning 0). exec() returns on error (it never returns on success; the process image is replaced).

Explainer

You know from the process concept that a process is a running program with its own address space, registers, and OS-managed state. But how does a new process come into existence? In Unix-like systems, the answer is surprisingly simple: every process is created by an existing process using the fork() system call. There is no "create process from scratch" operation — even the first user process (init or systemd) is forked by the kernel during boot. This means every process has a parent, forming a tree rooted at the init process.

When a process calls fork(), the kernel creates a new child process that is a near-exact copy of the parent. The child gets a duplicate of the parent's address space (code, data, heap, stack), the same open file descriptors, and the same register state — including the program counter, so the child resumes execution at the exact same point in the code as the parent. The only difference is the return value of fork(): it returns the child's process ID (PID) to the parent, and 0 to the child. This single difference lets the program branch: `if (fork() == 0) { /* child code */ } else { /* parent code */ }`. It may seem strange that one function call produces two return values, but it makes sense once you realize that after fork(), there are two independent processes running the same code — each receives its own return value.

Fork alone is only half the story. A child that is an exact copy of its parent is rarely useful — you usually want the child to run a different program. That is where exec() comes in. The exec family of system calls (execl, execv, execvp, etc.) replaces the current process's entire address space with a new program loaded from an executable file. The process ID stays the same, open file descriptors are preserved (unless marked close-on-exec), but the code, data, stack, and heap are replaced completely. Exec never returns on success because there is nothing to return to — the old program is gone. It only returns if loading the new program fails (e.g., file not found, permission denied).

The fork-then-exec pattern is the standard Unix idiom for launching programs. A shell, for example, forks a child process, and the child calls exec to run the command you typed. The parent (the shell) waits for the child to finish using wait() or waitpid(), then prints the next prompt. This two-step design is deliberate and powerful: the window between fork and exec gives the child a chance to set up its environment — redirect file descriptors (for I/O redirection like `> output.txt`), change the working directory, adjust signal handlers, or drop privileges — before the new program starts. Combining these simple primitives gives Unix its characteristic composability: pipes, background jobs, and process supervision all emerge from fork, exec, and wait.

Practice Questions 5 questions

Prerequisite Chain

Counting to 10Counting to 20Understanding ZeroThe Number ZeroCounting to FiveOne-to-One CorrespondenceCombining Small Groups Within 5Addition Within 10Addition Within 20Two-Digit Addition Without RegroupingTwo-Digit Addition with RegroupingAddition Within 100Repeated Addition as MultiplicationMultiplication Facts Within 100Division as Equal SharingDivision as Grouping (Measurement Division)Division: Grouping (Repeated Subtraction) ModelDivision: Fair Sharing ModelDivision as Equal SharingDivision as GroupingBasic Division FactsDivision Facts Within 100Two-Digit by One-Digit DivisionDivision with RemaindersRemainders and Quotients in DivisionDivision Word ProblemsIntroduction to Long DivisionFactors and MultiplesPrime and Composite NumbersEquivalent FractionsRelating Fractions and DecimalsDecimal Place ValueReading and Writing DecimalsComparing and Ordering DecimalsAdding and Subtracting DecimalsMultiplying DecimalsDividing DecimalsDividing FractionsMixed Number ArithmeticOrder of OperationsOperators and ExpressionsArithmetic Operators and Operator PrecedenceComparison Operators and Boolean TestsLogical Operators and Boolean AlgebraBoolean Algebra and Fundamental LawsCombinational Circuit DesignFlip-Flops and LatchesBinary Counters: Design and AnalysisBinary ArithmeticFixed-Point Number RepresentationTwo's Complement RepresentationOverflow and Underflow DetectionBinary Adders: Half-Adders and Full-AddersFull Adder and Carry PropagationCarry Lookahead Adder DesignHalf Adder Circuit DesignMultiplication Circuit DesignSequential Circuit DesignRegisters and Register FilesInstruction Set Architecture (ISA)Kernel Architecture and OS StructureSystem Calls and User/Kernel ModeProcesses and the Process Control BlockProcess Creation: fork() and exec()

Longest path: 64 steps · 239 total prerequisite topics

Prerequisites (2)

Leads To (4)