| | {{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. |
| | |
| | ## 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 |
| | 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 -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 |
| | 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. |
| | |
| | ## 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]] |
| | |
| | [[Category:Linux]] |
| | [[Category:Handbook]] |
| + | [[Category:Linux Handbook]] |
| | |