A process starts another with fork
A process is a running program with its own memory, and on Linux one process starts another with the system call fork. Understanding fork, exec and wait explains how a shell runs every command you type, and how a program learns whether its child worked.
When you type ls, your shell does not turn into ls. It makes a copy of itself, and the copy becomes ls.
By the end of this lesson
Note
I can explain how a process starts another with fork, exec and wait.
Key words
| Word | What it means | Example |
|---|---|---|
| Process | a running program with its own memory | |
| fork | a system call that makes a copy of the running process | |
| exec | a system call that loads a new program into the process | |
| wait | a system call that waits for a child to end | |
| Exit status | the number a process gives when it ends, 0 for success | exit(3) |
| Zombie | a child that has ended but has not been waited for |
The cells students fill in are left blank in this preview.
fork makes a copy of a process
Calling fork makes a second process that is a copy of the first, with the same code and a copy of its memory. The original is the parent and the copy is the child.
fork returns twice, once in each process. In the child it returns 0. In the parent it returns the child's process number, so the code can tell which one it is.
What fork returns to each process
| Process | fork returns | So it runs |
|---|---|---|
| The child | 0 | The branch for the child |
| The parent | The child's number | The branch for the parent |
| Either, on failure | -1 | Neither, no process was made |
The cells students fill in are left blank in this preview.
A parent that waits for its child
pid_t pid = fork();
if (pid == 0) {
printf("child running\n");
exit(3); // the child ends with status 3
}
int status;
waitpid(pid, &status, 0); // the parent waits for that child
printf("parent: child exited with %d\n", WEXITSTATUS(status));
Write and build the fork program
printf '%s\n' '#include <stdio.h>' '#include <stdlib.h>' '#include <unistd.h>' '#include <sys/wait.h>' 'int main(void) {' ' pid_t pid = fork();' ' if (pid == 0) {' ' printf("child running\n");' ' exit(3);' ' }' ' int status;' ' waitpid(pid, &status, 0);' ' printf("parent: child exited with %d\n", WEXITSTATUS(status));' ' return 0;' '}' | gcc -Wall -x c - -o forkerShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it and read both lines
./forkerShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
exec replaces the program in a process
exec loads a new program into the current process. The process keeps its number, but its old code and memory are gone.
If exec works, it never returns, so the lines after it do not run. They run only if exec failed. A shell uses fork then exec: the copy of the shell becomes the program you typed.
Write and build the exec program
printf '%s\n' '#include <stdio.h>' '#include <unistd.h>' 'int main(void) {' ' printf("before exec\n");' ' fflush(stdout);' ' execlp("echo", "echo", "hello from exec", (char *)NULL);' ' printf("never printed\n");' ' return 1;' '}' | gcc -Wall -x c - -o execerShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
See the old program replaced
./execerShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A child that ends with status 3
bash -c 'sh -c "exit 3"; echo $?'Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think fork starts a different program. It starts a copy of the same one. Only exec loads a new program, so a shell calls fork first and exec in the child.
wait collects a child's exit status
When a child ends, it leaves its exit status, and the system keeps that little record until the parent asks for it with wait. A status of 0 means success. Any other number means a problem.
A child that has ended but not been waited for is a zombie. It uses no processor, but it still takes a place in the process table.
Worked example: a trace of four events
| Step | Trace line | What it means |
|---|---|---|
| 1 | fork 1 2 | Process 1 starts process 2 |
| 2 | fork 2 3 | Process 2 starts process 3 |
| 3 | exit 3 0 | Process 3 ends with status 0 and is a zombie |
| 4 | wait 2 3 | Process 2 collects it, so the zombie goes |
The cells students fill in are left blank in this preview.