How the fork System Call Clones Processes in C

The fork() system call in the C programming language creates a new process by duplicating the existing calling process in Unix-like operating systems. This article explains how the operating system duplicates execution state, manages memory through Copy-on-Write, distinguishes between the parent and child processes using return values, and manages system resources during this operation.

The Mechanism of Process Duplication

When a C program invokes fork(), defined in the <unistd.h> header, the operating system halts the calling process—known as the parent—and creates a near-identical copy called the child process. The kernel allocates a new Process Control Block (PCB) and assigns a unique Process Identifier (PID) to the child.

The child inherits exact copies of the parent’s execution context, including:

  • Virtual memory segments (text/code, initialized data, BSS, heap, and stack)
  • CPU register values, including the instruction pointer (program counter)
  • Open file descriptors, pointing to the same underlying file table entries
  • Environment variables and current working directory

Because the child inherits the instruction pointer, both parent and child processes resume execution starting from the exact point where fork() returns, rather than starting from the main() function.

Differentiating Parent and Child Execution

Although the processes share identical code and state at the moment of creation, they execute independently. The fork() function provides a return value that allows the program logic to branch based on which process is running:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        // Fork failed
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // Child process
        printf("Child process executing. PID: %d\n", getpid());
    } else {
        // Parent process
        printf("Parent process executing. Child PID: %d\n", pid);
    }

    return 0;
}

The system call returns:

  • 0 to the child process.
  • The child's PID (a positive integer) to the parent process.
  • -1 to the parent if process creation fails, setting errno.

Memory Efficiency via Copy-on-Write (CoW)

Duplicating an entire address space physically in RAM for every fork() call would be computationally expensive. Modern operating systems implement an optimization called Copy-on-Write (CoW).

Instead of creating a physical copy of the parent’s memory pages immediately:

  1. The kernel copies only the page tables from the parent to the child.
  2. The physical memory pages are marked as read-only and shared between both processes.
  3. If either process attempts to modify a page (such as writing to a variable or allocating memory), the hardware triggers a page fault.
  4. The kernel intercepts the fault, allocates a new physical page, copies the original data into it, and updates the modifying process’s page table with read-write permissions.

This ensures that only modified memory pages consume additional physical RAM, making fork() fast and resource-efficient, especially when paired with subsequent exec() family functions to launch new binaries.