Skip to content
ALB Wiki

Diff — Processes

Comparing rev 6 → rev 7

{{infobox
| title = Processes
| chapter = Linux Handbook, chapter 13 — fork/exec, signals, ps, job control, /proc
| core | A process = a running program with a pid, a memory map, and open files
| skills | ps · top · kill · jobs · nice · sessions · /proc inspection
| the_big_idea = Every process descends from PID 1 — a family tree of forks
| next | Shell scripting — programming the process layer
}}
-
**A process is a running program** — a pid, an address space, open file descriptors, a parent, a state. Every process on Linux descends from **PID 1** (init/systemd) through `fork()` — the whole system is one family tree. This chapter teaches you to see it: `ps`, `top`, signals, job control, and the `/proc` filesystem where the kernel exposes every process's innards as readable files.
+
**A process is a running program** — a pid, an address space, open file descriptors, a parent, a state. Every process on Linux descends from **PID 1** (init/[[systemd]]) through `fork()` — the whole system is one family tree. This chapter teaches you to see it: `ps`, `top`, signals, job control, and the `/proc` filesystem where the kernel exposes every process's innards as readable files.
## The lifecycle — fork, exec, wait, exit
```c
pid = fork();        // clone — parent and child both continue
if (pid == 0)        // child branch
    execvp("prog", args);   // replace this process's image
else
    wait(&status);   // parent collects the child's exit code
```
**fork()** duplicates; **exec()** replaces the copy with the new program; **wait()** reaps. Every process is born a clone of its parent, then re-skinned as its real program. The **exit code** (0–255) the child leaves is the contract the parent collects — `$?` in the shell.
When a child dies before its parent waits, it becomes a **zombie** — `Z` in `ps`, dead but unreaped, holding only its exit status. An orphan whose parent dies is **re-parented to PID 1**.
## Seeing them — ps and friends
```sh
ps aux                  # the classic full listing
# USER  PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
ps -ef                  # SysV style — shows PPID
ps -ejH                 # the family tree
ps --forest -e          # visual tree
pgrep -l sshd           # pids by name
pidof bash              # single name → pids
top                     # the live view — sort by M(em), P(cpu), T(ime)
htop                    # the better live view
pstree -p               # the tree with pids
```
**STAT codes** — `R` running, `S` sleeping, `D` uninterruptible (usually I/O), `T` stopped, `Z` zombie, `s` session leader, `+` foreground, `l` multithreaded. `D`-state processes can't be killed — they're mid-syscall in the kernel.
## Signals — the messaging system
```sh
kill PID            # SIGTERM (15) — please exit politely
kill -9 PID         # SIGKILL — the kernel kills it NOW (no cleanup)
kill -HUP PID       # SIGHUP — hangup → reload config for daemons
kill -INT PID       # SIGINT (2) — Ctrl-C
kill -TSTP PID      # SIGTSTP — Ctrl-Z
kill -CONT PID      # resume a stopped one
kill -USR1 PID      # user-defined — daemons use as triggers
-
kill -l             # all 31 signals
+
kill -l             # all 64: 1–31 standard, 32–64 realtime
killall name        # by name
pkill -f pattern    # by cmdline pattern
```
The two uncatchables: **SIGKILL** and **SIGSTOP** — the kernel's guarantees that nothing can refuse death or suspension. Everything else a process may `trap`/handle/ignore.
## Job control — the shell's own processes
```sh
cmd &               # background
jobs                # the job table — %1 %2 ...
fg %1               # bring to foreground
bg %1               # resume suspended in background
Ctrl-Z; bg          # the suspend-and-background combo
disown %1           # remove from job table — survives shell exit
nohup cmd &         # immune to SIGHUP — survives logout
```
`Ctrl-C` = SIGINT to the foreground group; `Ctrl-Z` = SIGTSTP; `Ctrl-\` = SIGQUIT (with core dump). `jobs -l` shows pids.
## Sessions and process groups
A **session** groups process groups under a controlling terminal — `setsid` starts a new one (how daemons detach from terminals). The login shell leads a session; `pkill -u alice` kills all her processes. Daemonization = fork, setsid, detach tty, chdir `/`, close fds, redirect stdio to `/dev/null` — the classic dance systemd now does for services.
## The fork/exec/exec family — the C-level anatomy
```c
pid_t pid = fork();          // clone — parent+child both continue, differ in return
if (pid == 0) {              // child
    execlp("prog", "prog", "arg", NULL);   // PATH-searched exec
    _exit(127);                            // only reached if exec fails
}
// parent continues with pid = child's pid
int status;
waitpid(pid, &status, 0);    // collect the exit code
WIFEXITED(status); WEXITSTATUS(status);    // decode it
```
The exec family: `execl`/`execlp`/`execle`/`execv`/`execvp`/`execvpe` — `l`=arg-list, `v`=argv-array, `p`=PATH-search, `e`=custom env. After exec the process's whole image is replaced — same pid, new program. The zombie/orphan logic: a child exits → SIGCHLD to parent → parent wait()s → reaped; parent dies first → child reparents to PID 1.
## The process tree's structure
```sh
pstree -p            # the visual family — from systemd/PID1 down
ps -ejH              # the same tree as a list
ps -o pid,ppid,pgid,sid,tty,cmd -e
# PID 1: systemd (or init)
#   ├─ sshd ──── bash ──── vim
#   ├─ cron
#   ├─ getty (×6)
#   └─ daemons...
```
Every process descends from PID 1 — the whole system is one fork tree. Session = login context; process-group = a pipeline's jobs; controlling-tty = what Ctrl-C targets.
## The signal-send grammar
```sh
kill PID             # SIGTERM 15 — ask politely
kill -9 PID          # SIGKILL — uncatchable
kill -HUP PID        # reload — daemons' convention
kill -INT PID        # Ctrl-C equivalent
kill -TSTP PID       # Ctrl-Z equivalent
kill -CONT PID       # resume
kill -USR1 PID       # app's private trigger — nginx reloads, dd prints progress
kill -- -PGID        # a process GROUP
-
kill -l              # the 31
+
kill -l              # 1–31 standard + SIGRTMIN..SIGRTMAX realtime
kill -0 PID          # "does it exist?" — the probe
killall name         # by name
pkill -f pattern     # by cmdline
pkill -u alice       # all of alice's
```
`kill -0` is the existence probe — returns the error without killing. `-USR1` is the convention apps use for reload/status — `dd` prints its progress on USR1.
## /proc — the process's innards
```sh
ls /proc/$$         # YOUR shell's process, as files
cat /proc/$$/status     # state, memory, uids
cat /proc/$$/cmdline | tr '\0' ' '   # its argv
ls -l /proc/$$/fd       # its open files — the fds!
cat /proc/$$/environ | tr '\0' '\n'  # its environment
cat /proc/loadavg       # 1/5/15-min load + running/total
cat /proc/meminfo       # the memory ledger
cat /proc/cpuinfo       # the CPUs
```
`/proc/PID/fd/` alone is worth the chapter — every open file, socket, and pipe as a symlink. `lsof` and `ls -l /proc/*/fd` show who holds what.
## Priorities — nice and the scheduler
```sh
nice -n 10 cmd          # run it nicer (lower priority, -20..19)
renice +5 -p PID        # change a running one
ps -o pid,ni,comm       # see nice values
```
Higher nice = *lower* priority — "nicer" to others. `chrt`, `ionice`, `taskset` reach the deeper schedulers (RT classes, I/O priority, CPU pinning).
## Resource limits
```sh
ulimit -a               # this shell's limits — fds, core size, procs, memory
ulimit -n 4096          # raise fd ceiling
cat /proc/PID/limits    # a running process's limits
timeout 30 cmd          # kill after 30s — the blunt instrument
systemd's LimitNPROC/LimitNOFILE — the service-level equivalents
```
`ulimit -n` (max open files) is the one you'll actually hit — busy servers bump it constantly.
## Signals — the complete taxonomy
```sh
-
kill -l                    # all 31
+
kill -l                    # 64 total — 1–31 standard, 32–64 realtime (SIGRTMIN..SIGRTMAX)
SIGHUP  (1)   # hangup — terminal closed → daemons use as "reload config"
SIGINT  (2)   # Ctrl-C — interrupt
SIGQUIT (3)   # Ctrl-\ — quit + core dump
SIGILL  (4)   # illegal instruction
SIGTRAP (5)   # debugger breakpoint
SIGABRT (6)   # abort() — the core dump
SIGFPE  (8)   # arithmetic fault — div by zero
SIGKILL (9)   # UNCATCHABLE — kernel kills it now
SIGSEGV (11)  # segfault — bad memory access
SIGPIPE (13)  # wrote to a closed pipe — the "broken pipe"
SIGALRM (14)  # alarm() timer
SIGTERM (15)  # polite terminate — the default kill
SIGCHLD (17)  # a child died — parent reaps via wait()
SIGCONT (18)  # resume a stopped process
SIGSTOP (19)  # UNCATCHABLE suspend
SIGTSTP (20)  # Ctrl-Z — terminal stop (catchable)
SIGUSR1/2 (10/12)  # user-defined — daemons' private triggers
```
The semantics: SIGTERM asks (catchable — a process can trap it and clean up); SIGKILL executes (uncatchable — no cleanup possible); SIGHUP is overloaded as "reload your config" for daemons; SIGSTOP/SIGCONT are the debugger's and job control's pause/resume. `trap` catches the catchables — `trap 'cleanup' TERM INT` is the daemon-shutdown pattern.
+
Numbers 32–64 are the **realtime range**: 32–33 are reserved for [[glibc]]'s threading internals, and 34–64 are `SIGRTMIN`..`SIGRTMAX` (C: `SIGRTMIN+n`) — same delivery, but they queue (multiple instances aren't coalesced) and can carry an integer payload — POSIX timers, AIO, and `mq_notify` use them. `kill -RTMIN+5 PID` sends one; `kill -l` lists all 64 slots.
+
## Sessions, groups, and the controlling terminal
```sh
ps -o pid,ppid,pgid,sid,tty,cmd -e | head   # the whole hierarchy
setsid cmd                 # new session — detach from the terminal
disown %1                  # drop from job table — survive exit
nohup cmd &                # SIGHUP-immune
```
A **session** = a login or detached context; a **process group** = a pipeline's jobs — `kill -PID` targets a pid, `kill -- -PGID` a group. The controlling terminal (`TTY` in ps) is what Ctrl-C signals hit — daemons shed it via setsid.
## cgroups + namespaces — the container primitives
```sh
ls /sys/fs/cgroup/           # the cgroup hierarchy — the real container layer
cat /proc/PID/cgroup         # a process's cgroup
systemd-cgls                 # systemd's cgroup tree
unshare -u -n -p bash        # a bash in fresh namespaces — a proto-container
lsns                         # list namespaces
```
Namespaces (mnt, pid, net, ipc, uts, user) isolate; cgroups meter (cpu, memory, io limits). Containers are these two primitives arranged — `docker run` = namespaces + cgroups + an image, nothing more.
## The scheduler's deeper knobs
```sh
nice -n 10 cmd               # -20..19 — higher = LOWER priority
renice -n 5 -p PID           # live-tune
chrt -f 50 cmd               # SCHED_FIFO realtime
chrt -r 50 cmd               # SCHED_RR
ionice -c3 cmd               # io priority — idle-class
taskset -c 0,1 cmd           # CPU pinning
```
Realtime classes beat nice — `SCHED_FIFO`/`RR` preempt everything else; `ionice` and `taskset` shape I/O and CPU affinity.
## Monitoring beyond top
```sh
vmstat 1              # memory + IO + CPU, per second
iostat -x 1           # disk I/O
mpstat -P ALL 1       # per-CPU
dstat                 # the everything-monitor
free -h               # memory now
watch -n1 'ps aux --sort=-%cpu | head'   # your own live monitor
```
Load average (`uptime`) = runnable processes averaged over 1/5/15 min — `4.00` on 4 CPUs = saturated; on 8 CPUs = half-loaded. Interpret relative to core count.
## Where process work goes wrong
| Symptom | Cause |
| --- | --- |
| `kill` didn't kill it | Process ignored SIGTERM — escalate to `-9` |
| `kill -9` didn't kill it | `D` state — mid-syscall, often dead NFS/disk I/O |
| Zombie piling up | Parent not wait()ing — a parent bug; kill the parent |
| Daemon dies at logout | Needed `nohup`/`setsid`/disown — SIGHUP |
| `Ctrl-C` doesn't work | Process traps or ignores SIGINT |
| Can't find the process | `ps -ef | grep name`; `pgrep -f` for cmdline |
| "Too many open files" | `ulimit -n` — the fd ceiling |
## Exercises
1. `sleep 300 &` — job control: jobs, fg, Ctrl-Z, bg, kill.
2. `ls -l /proc/$$/fd` — inspect your shell's own open files.
3. `bash -c 'sleep 99'` then `ps -ejH` — find the fork relationship.
4. `kill -TSTP` a process then `-CONT` — freeze and thaw it.
5. `watch -n1 cat /proc/loadavg` — watch load breathe.
6. `ulimit -n` — your fd ceiling; then raise it and note where it matters.
## Timeline
| Concept | Mechanism |
| --- | --- |
| Birth | fork() → exec() |
| Death | exit() → zombie → wait() |
| PID 1 | The ancestor of all — systemd/SysVinit |
| Signals | Inter-process interrupts |
| /proc | The kernel's process window |
## See also
-
- [[Linux]] · [[Shell]] · [[Core utilities]] · [[LFS Kernel and Boot]]
+
- [[Linux]] · [[Shell]] · [[Core Utilities]] · [[LFS Kernel and Boot]]
[[Category:Linux]]
[[Category:Handbook]]
[[Category:Linux Handbook]]