Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

How fork, exec and wait work

After this lesson: I can explain how a process starts another with fork, exec and wait.

55 min lesson5 commandsCompilers and computer architecture lab: gcc, flex, bison and RISC-V in one VM

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

WordWhat it meansExample
Processa running program with its own memory
forka system call that makes a copy of the running process
execa system call that loads a new program into the process
waita system call that waits for a child to end
Exit statusthe number a process gives when it ends, 0 for successexit(3)
Zombiea 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

Processfork returnsSo it runs
The child0The branch for the child
The parentThe child's numberThe branch for the parent
Either, on failure-1Neither, no process was made

The cells students fill in are left blank in this preview.

A parent that waits for its child

Code: c
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

Command
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 forker

Shown 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

Command
./forker

Shown 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

Command
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 execer

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

See the old program replaced

Command
./execer

Shown 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

Command
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

StepTrace lineWhat it means
1fork 1 2Process 1 starts process 2
2fork 2 3Process 2 starts process 3
3exit 3 0Process 3 ends with status 0 and is a zombie
4wait 2 3Process 2 collects it, so the zombie goes

The cells students fill in are left blank in this preview.

About this lab and its source: Compilers and computer architecture lab: gcc, flex, bison and RISC-V in one VM

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.