devutils/ATFQ/book ATFQ git

Part I · How it works

1.5 How a program starts: fork and exec

alsoforkexecexecvecloneprocess creationcopy on writehow a program starts

TL;DR

  • A new process starts as a copy of an existing one: fork() duplicates the process that calls it.
  • exec() then replaces the copy's program with another: the kernel loads the new ELF file and its loader (23.2).
  • Your shell does both for every command you type. Every process descends from PID 1.
pstree -p $$       # your shell and what it started
strace -f -e trace=clone,clone3,execve bash -c 'ls; true'

On your machine

alice@laptopexample
$ atfq 1.5
  Kernel    6.12.48-amd64, built with gcc 14.2.0
  Build     #1 SMP PREEMPT_DYNAMIC Debian 6.12.48-1 (2025-09-20)
  Work      in the 3 days, 4 h since boot: 912.3 million context switches
            (3,330 per second), 1.2 million processes started, 51.2 million
            interrupts
  Now       3 tasks running or ready, 1 waiting on I/O
  Disk FS   ext4, vfat, btrfs

An example: Alice's laptop, one of the test machines. In your terminal, this block shows your own machine, read live.

Pitfalls

Deep dive 2 questions

Why two steps instead of one?

Between fork and exec, the child still runs the parent's code, so it can prepare its own setup: open a file as its output, change folder, drop privileges, set limits. Then it becomes the new program, which inherits all of that. That's how the shell handles cmd > file without cmd knowing anything about it.

What does the kernel do in exec?

It reads the start of the file. For an ELF program, it builds a fresh address space, maps the program's code and data into it, also maps the dynamic loader when the program needs one, puts the arguments and the environment on a new stack, and jumps to the entry point. For a file starting with #!, it runs the interpreter named there instead.

Commands
pstree strace
See also
16. Processes & scheduling
23.2 ELF and "command not found"
16.1 Process states and zombies
27.3 Redirections and file descriptors