What Happens Inside Linux When You Call fork()? A Deep Dive
This article explains how the fork() system call creates a duplicate process in Linux, walks through a simple code experiment, details the kernel steps—including task_struct creation, PID allocation, copy-on-write memory handling, and resource inheritance—and highlights common use cases, optimizations, and pitfalls.
Quick Experiment Demonstrating fork()
Running a minimal C program that prints a line before and after fork() shows that the first line is printed once, while the second line appears twice with different return values, illustrating the creation of a child process.
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Before fork(), I have one process
");
int pid = fork();
printf("After fork(), I have two processes! Return value: %d
", pid);
return 0;
}Typical output:
Before fork(), I have one process
After fork(), I have two processes! Return value: 4814
After fork(), I have two processes! Return value: 0What fork() Does
fork()creates a near‑identical copy of the calling process. The new process is the child; the original becomes the parent. The return value distinguishes them:
Parent: fork() returns the child’s PID.
Child: returns 0.
Failure: returns -1 and sets errno.
Kernel Steps Behind fork()
Step 1 – System Call Entry
user space: fork()
↓
system call: sys_fork()
↓
kernel space: do_fork()Step 2 – Preparing a New Process
The kernel allocates a new task_struct (process control block) and a unique PID, then wakes the new task:
long do_fork(unsigned long clone_flags, ...)
{
struct task_struct *p;
/* 1. Allocate new task_struct */
p = copy_process(clone_flags, ...);
/* 2. Allocate new PID */
pid = get_pid(p);
/* 3. Wake up the new task */
wake_up_new_task(p);
return pid; /* returned to the parent */
}Step 3 – Copying Process State (copy_process)
copy_process()performs three major copies:
struct task_struct *copy_process(...)
{
/* Allocate fresh task_struct for the child */
p = dup_task_struct(current);
/* 1. Duplicate memory space */
copy_mm(clone_flags, p);
/* 2. Duplicate file descriptor table */
copy_files(clone_flags, p);
/* 3. Duplicate signal handling */
copy_sighand(clone_flags, p);
return p; /* child task_struct */
}Memory Copy – Copy‑on‑Write (COW)
Parent and child initially share the same physical pages. When either process writes to a page, the kernel copies that page, preserving isolation while saving memory.
Parent memory: [Page1] [Page2] [Page3]
↓ fork() shares
Child memory: [Page1] [Page2] [Page3] (same physical pages)
When child modifies Page2:
Parent memory: [Page1] [Page2‑original] [Page3]
Child memory: [Page1] [Page2‑new] [Page3] (Page2 copied)File Descriptor Inheritance
All open files, sockets, and other descriptors are duplicated for the child, allowing seamless continuation of I/O.
Signal Handling Copy
The child inherits the parent’s signal dispositions, so both react to signals in the same way.
Step 4 – Establishing Parent‑Child Relationship
The child’s PPID is set to the parent’s PID.
The parent’s task_struct adds the child to its list of children.
Step 5 – Scheduling the New Process
After the structures are ready, the scheduler can select the child for execution on a CPU.
Full Example: Parent and Child Working Together
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int count = 0;
printf("Preparing to create child process...
");
int pid = fork();
if (pid == 0) { /* child */
printf("Child: my PID %d, parent PID %d
", getpid(), getppid());
for (int i = 0; i < 3; i++) {
printf("Child working: %d
", ++count);
sleep(1);
}
printf("Child done!
");
} else if (pid > 0) { /* parent */
printf("Parent: my PID %d, created child %d
", getpid(), pid);
for (int i = 0; i < 3; i++) {
printf("Parent working: %d
", ++count);
sleep(1);
}
printf("Parent waiting for child...
");
wait(NULL);
printf("Parent done!
");
} else {
perror("fork");
return 1;
}
return 0;
}Running this program produces interleaved output from the parent and child, each maintaining its own count variable.
Classic Applications of fork()
Shell Command Execution
A shell forks, the child calls exec() to run a command, and the parent waits for completion:
int pid = fork();
if (pid == 0) {
execv("/bin/ls", (char * const[]){"ls", "-l", NULL});
} else {
wait(NULL);
}Concurrent Server Requests
Servers often accept a connection, fork a child to handle the client, and let the parent continue listening:
while (1) {
int client = accept(server_socket, NULL, NULL);
int pid = fork();
if (pid == 0) {
handle_client(client);
_exit(0);
} else {
close(client);
}
}Performance Tweaks: vfork() and clone()
vfork()
Optimized for the fork()+exec() pattern: it creates the child without copying the address space, but the child must call exec() or _exit() immediately.
clone()
A lower‑level interface that lets the caller specify which resources are shared (e.g., memory, file descriptors) and which are duplicated. fork() is essentially a wrapper around clone() with default flags.
Common Pitfalls
Fork Bomb
An uncontrolled loop that repeatedly calls fork() can exhaust system resources and crash the machine:
// Dangerous! Do NOT run
while (1) {
fork();
}Zombie Processes
If the parent never calls wait(), terminated children remain in the process table as zombies:
int pid = fork();
if (pid == 0) {
printf("Child exiting
");
_exit(0);
} else {
/* Parent forgets to wait – child becomes a zombie */
sleep(100);
}Summary
fork()involves kernel mechanisms such as copy‑on‑write memory management, PID allocation, resource inheritance, and process hierarchy construction. Understanding these details enables writing robust multi‑process programs, avoiding common traps, and appreciating the core of Unix/Linux process management.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
