Process Lifecycle

What is it: the sequence of states a process goes through: created → running → waiting → terminated.

sequenceDiagram participant P as Parent participant C as Child P->>C: fork() — CoW copy created Note over C: exec() replaces child's<br/>address space with new program Note over P,C: both run independently C->>C: process exits Note over C: zombie — PCB stays until parent wait()s P->>P: wait() — reaps child, PCB removed

fork(): Creates a child process as a CoW copy of the parent. Child inherits: address space (shared pages until written), fd table, signal handlers, env vars, working directory. New PID. Returns 0 in child, child's PID in parent.

exec(): Replaces current process image with a new program. PID unchanged. Address space replaced. FDs inherited unless FD_CLOEXEC set. Typical pattern: fork() + exec() to run a new program in a child.

wait(): Parent collects child's exit status. Removes child's PCB entry from process table.

Zombie process: Child has exited but parent hasn't called wait(). PCB entry (PID, exit status) remains in process table. Wastes one process table slot. Accumulates if parent never calls wait(). Detection: ps aux | grep Z.

Orphan process: Parent exits before child. Linux re-parents orphan to init (PID 1). init calls wait() for all its children — orphans are cleaned up automatically. Useful for daemons: fork, parent exits, child is re-parented to init and continues as a background daemon.

Real-world usage: Redis uses fork() for RDB snapshots. Go's os/exec uses fork+exec. Proxel launches Puppeteer subprocesses via exec.Command with FD_CLOEXEC to prevent fd leaks.

Common pitfall

A long-running parent process that spawns many short-lived children but never calls wait() (or waitpid() in a SIGCHLD handler) accumulates zombies indefinitely — each is small (just a PCB entry), but the process table itself has a finite size, and enough accumulated zombies can eventually block new processes from being created system-wide, not just within the leaking application. This is a common gap in code that spawns subprocesses via a library that doesn't automatically reap them; ps aux | grep Z catching zombies in production is the direct symptom to watch for.