Process = running program + OS state
A process is the OS's unit of CPU virtualization: a program counter, register file, virtual address space, open file descriptors, signal disposition, and metadata (PID, parent, exit status). The kernel schedules processes (really threads), not source files.
The Unix process API
| Syscall | What it does | Production relevance |
|---|---|---|
fork() | Clone current process (CoW address space) | Shell pipelines, prefork workers |
execve() | Replace address space with a new binary | Every systemd service start |
wait() / waitpid() | Reap child exit status | Prevents zombies |
exit() | Terminate, notify parent | Clean shutdown vs _exit on failure |
Typical pattern: fork → child execves target binary → parent waits. The shell does this for every command.
Process states worth knowing
- Running / Ready — on CPU or in the run queue.
- Blocked (S) — waiting on I/O, lock, or
sleep. - Uninterruptible (D) — stuck in kernel I/O;
kill -9won't help. - Zombie (Z) — exited but parent hasn't reaped; harmless unless parent leaks thousands.
- Orphan — parent died; adopted by PID 1 (init/systemd).
Senior-level signal
"Why is my container's PID 1 not reaping zombies?" is a classic production bug — if PID 1 doesn't call wait, zombie count climbs until the namespace hits pid_max. Init systems and proper container entrypoints must reap children.
Where this goes next
Context Switching & Scheduling Basics explains how the kernel decides which process runs next — and what happens to all that saved state when it switches.
Further Reading
Hands-On Tasks (Optional)
Low-setup exercises on your local machine. No autograding — the goal is to build intuition, not pass a test.
- Observe fork() with a tiny C program or Python os.fork()20m
Write (or run) a 10-line program that calls fork() and prints PID on parent and child. Run twice and note PIDs differ but parent/child relationship holds. If uncomfortable with C, use `python3 -c "import os; print('before', os.getpid()); os.fork(); print('after', os.getpid())"`.