Processes: fork & exec
How Unix creates new processes — and why every program you run starts with fork
Quick check before you start: When you type
lsin the terminal, what actually happens? If you are not sure, read on. If you can describe fork/exec, skip to wait() and Zombie Prevention.Practice this topic: Fork & Exec skill drill
After this lesson, you will be able to:
- Explain what
fork()returns in the parent vs. the child - Use
execlpto replace a process with a new program - Use
wait()to collect a child’s exit status - Describe how zombie processes happen and how to prevent them
System Calls vs Library Functions
Throughout this course, you have used C library functions like printf and calloc. Under the hood, these call system calls — the actual interface to the kernel. Library functions add convenience; system calls do the real work.
| Library function | Underlying system call | Purpose |
|---|---|---|
fopen |
open |
Open a file |
printf |
write |
Write to a file descriptor |
calloc |
sbrk / mmap |
Allocate memory |
fclose |
close |
Close a file descriptor |
exit |
_exit |
Terminate the process |
You can look up either layer in the manual. Man page sections tell you which layer you are reading:
- Section 2 — system calls (
man 2 open) - Section 3 — library functions (
man 3 printf)
If man printf shows the shell builtin instead of the C function, use man 3 printf to get the right page. From this lesson forward, you will work directly with system calls.
How Unix Creates Processes
Every process in Unix is created the same way: an existing process calls fork(). The kernel copies the current process, creating a parent-child pair. Then the child typically calls exec to replace itself with a different program.
This is how your shell runs every command. When you type ls:
- The shell calls
fork()— now there are two copies of the shell - The child calls
execlp("ls", "ls", NULL)— the child becomesls - The parent calls
wait()— it pauses until the child finishes - The shell prints a new prompt
fork() Return Value
fork() returns different values depending on which process you are in:
| Process | Return value |
|---|---|
| Parent | The child’s PID (a positive integer) |
| Child | 0 |
| Error | -1 (fork failed) |
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
} else if (pid == 0) {
// CHILD process
printf("I am the child, PID = %d\n", getpid());
} else {
// PARENT process
printf("I am the parent, child PID = %d\n", pid);
wait(NULL);
}
return 0;
}
After fork(), both processes execute the same code. The return value is how you tell them apart.
execlp: Replacing the Process
fork() gives you a copy. execlp replaces that copy with a different program:
if (pid == 0) {
execlp("ls", "ls", "-l", NULL);
// If we get here, exec failed
perror("execlp");
exit(EXIT_FAILURE);
}
The arguments to execlp:
- The program name (searched in PATH)
argv[0](by convention, the program name again)- Additional arguments
NULLto terminate the list
After a successful exec, the child process is gone — it has been replaced by ls. The code after execlp only runs if exec fails.
wait() and Zombie Prevention
When a child exits, it becomes a zombie — a dead process that still has an entry in the process table. The parent must call wait() to collect the child’s exit status and remove the zombie.
int status;
pid_t child = wait(&status);
if (WIFEXITED(status)) {
printf("Child %d exited with status %d\n",
child, WEXITSTATUS(status));
}
If the parent never calls wait(), zombies accumulate. In a long-running server, this eventually fills the process table and prevents new processes from being created.
The Complete Pattern
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child: run a different program
execlp("ls", "ls", "-la", NULL);
perror("execlp");
exit(EXIT_FAILURE);
} else {
// Parent: wait for child
int status;
wait(&status);
printf("Child finished\n");
}
This is the fundamental pattern for process creation in Unix. Every shell, every server, every process manager uses some variation of fork + exec + wait.
fork(), what does the return value look like in the parent and child?What Comes Next
You can create processes. Next, you will learn how processes communicate — using pipes to connect one program’s output to another’s input, and signals to interrupt running processes.