Module 8 — Processes

Updated 2 September 2026

Module 8 — Processes

Everything that happens on Linux happens inside a process — every command you have run since Module 1 was one. This module makes them visible and controllable: watching them, signalling them, running them in the background, and understanding the strange ones (zombies, orphans, daemons) that interviews adore. It is also the module where "why did my program die when I closed the terminal?" finally gets its full answer.

Legend used throughout: 🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)

Before you start

You need Modules Module 1 — What Linux IsModule 7 — Text Surgery: sed and awk — especially the environment/child model (Module 5) and pipelines (grep/awk will dissect ps output constantly). Nothing to install; sleep — a command that does nothing for N seconds, then exits successfully — will be our lab rat throughout: harmless, killable, visible.

Goes deeper: this module has two dedicated theory companions in the Operating Systems track — Module 02 — Processes, fork/exec & Process States and Module 04 — Signals, Sessions & Job Control. Read them after this page for the mechanism beneath every command here.

This page uses Mermaid diagram blocks. Notion shows them as code by default — click the block and set it to Preview to see the diagram. This reminder appears once per page.

Part A — What a process is

A1. Programs run as processes — PID, PPID, and the tree

Official docs: ps(1) · proc(5)

🧠 A program is a file on disk (Module 3's ELF executables). A process is that program running: the kernel's live bookkeeping for one execution — its memory, its open files, its identity (Module 4's UID), and two numbers: the PID (its own process ID) and PPID (its parent's). Every process is created by another — your commands are children of your shell, your shell is a child of the terminal or login machinery — forming one tree, rooted at PID 1: on modern Linux, systemd (historically named init), started by the kernel at boot and ancestor of everything.

The shell tells you its own PID via a special variable: echo $$.

Real-world analogy — the recipe and the cooking

A program is a recipe card; a process is one act of cooking it — pans out, timers running, ingredients half-used. Three cooks can work the same recipe simultaneously: one card, three independent cooking sessions, each with its own mess and its own timer. Three processes, one program.

Where the analogy stops working. A cooking session is the cook's affair. A process is really the kernel's dossier about the cooking — identity, allowances, opened drawers — and the kernel can consult, suspend, or terminate the session from outside at any moment. The cook does not own the kitchen; the kernel does.

🧪 Exercise A1.1 — Find yourself in the tree
bash
echo $$                 # this shell's PID
ps                      # processes in THIS terminal session (a short list)
ps -e --forest | head -25   # the whole tree, drawn — look for systemd at the top
Expected result — click to reveal
javascript
51442
    PID TTY          TIME CMD
  51442 pts/0    00:00:00 bash
  51503 pts/0    00:00:00 ps
      1 ?        00:00:04 systemd
      2 ?        00:00:00 kthreadd
      3 ?        00:00:00  \_ pool_workqueue_release
...

What to read out of it (PIDs are assigned in order at boot — every machine's numbers differ):

  • Plain ps showed just two processes: your shell, and — pleasingly — ps itself, which was of course a running process while it looked. The observer appears in its own photograph.
  • The forest view: systemd as PID 1, and a second tree under kthreadd (PID 2) — the kernel's own worker threads, names in [brackets] in other views. Everything else descends from PID 1. (Inside containers and WSL, PID 1 may be something else entirely — the init of that little world; the structure holds.)
  • Scroll the full ps -e --forest output: find your terminal, your bash under it, and this very ps as bash's child. You have located yourself in the ancestry of everything.

A2. Where children come from — fork and exec

Official docs: proc(5)

Goes deeper: Module 02 — Processes, fork/exec & Process States — the full mechanism, with experiments.

🧠 When you type date, the shell does not "run date" in one step. It forks — asks the kernel to clone itself, producing a child that is a near-copy (same environment: Module 5's snapshot semantics, explained at last) — and the child then execs: replaces its own program with /usr/bin/date, keeping PID and environment. The parent shell waits for the child to exit and collects its exit code — which is exactly the $? you have been reading since Module 5. Fork, exec, wait: three verbs, and every command you have ever run was this trio.

Why you care operationally: it explains environment inheritance (copies at fork), why a child can never alter its parent (separate processes), where exit codes travel (wait), and — in Part D — what happens when the wait step goes missing.

Real-world analogy — the photocopied briefing

Dispatching an errand, the manager photocopies their own briefing folder (fork — the copy is the child's environment), then swaps the top sheet for the errand's instructions (exec), and files the runner's receipt on return (wait — the exit code). Nothing the runner scribbles on their copy reaches the manager's folder.

Where the analogy stops working. A manager dispatches a runner other than themselves. fork is stranger: the shell briefly becomes two identical shells, and one of them transforms into date. There is no hiring — only cloning and metamorphosis. It sounds baroque; it is why Unix process creation is one cheap, uniform trick rather than a dozen special cases.

🧪 Exercise A2.1 — Watch the ancestry in the columns
bash
ps -o pid,ppid,comm      # this session, with parent PIDs shown
bash                     # start a CHILD shell, inside your shell
ps -o pid,ppid,comm      # look again — who is whose parent?
exit                     # leave the child; you are back in the original
Expected result — click to reveal
javascript
  PID   PPID COMMAND
51442  51440 bash
51612  51442 ps
  PID   PPID COMMAND
51442  51440 bash
51619  51442 bash
51655  51619 ps

What to read out of it:

  • First snapshot: ps's PPID is your shell's PID — child of bash, as promised. Second: a bash whose PPID is the first bash, and a ps parented by the second. Three generations in one column.
  • The inner exit returned you to the parent — nesting shells is safe and ordinary (it is what su - and sudo -i did in Module 4: child shells with different identities).
  • -o chose exactly the columns to print — ps composing with your Module 7 habits: ps -e -o pid,ppid,comm | awk '$2 == 1' lists everything whose parent is PID 1. Try it; Part D explains why that list is interesting.

A3. /proc — the window into every process

🧠 Module 2's tour deferred /proc; the debt comes due. /proc is a pseudo-filesystem: it looks like files and directories, but nothing is stored anywhere — the kernel generates the contents at the moment you read them. One numbered directory per process (/proc/51442/ for PID 51442, /proc/self/ for "whoever is asking"), containing that process's live state as readable files: status (identity, state, memory), cmdline (how it was started), environ (its environment — Module 5's snapshot, inspectable!), cwd (a symlink to its working directory), fd/ (its open files). Every tool in this module — ps, top, all of them — is a presenter of /proc; nothing they show is unavailable to you directly.

Real-world analogy — the live CCTV wall dressed as filing cabinets

/proc looks like an archive room — drawers, folders, labels. Open any folder and it is actually a live monitor: the "document" is composed the instant you look, showing this second's truth. Close and reopen it: new truth.

Where the analogy stops working. Archives store; /proc stores nothing. ls -l shows size 0 on files that read back pages of content — generated, not kept. And your intuition about "old files" inverts: a /proc read is never stale, but it is also never a record — by the time you act on it, reality may have moved. Snapshots, not history.

🧪 Exercise A3.1 — Read a process's dossier by hand
bash
sleep 300 &                       # a lab rat (& = run in background; formal treatment in C2)
cat /proc/$!/status | head -6     # $! = PID of the most recent background command (Module 5 family)
cat /proc/$!/cmdline; echo        # how it was invoked (fields are NUL-separated — hence the glued look)
ls -l /proc/$!/cwd                # where it is standing
kill $!                           # dismiss the rat (kill: Part C — on loan for cleanup)
Expected result — click to reveal
javascript
Name:	sleep
Umask:	0002
State:	S (sleeping)
Tgid:	51710
Ngid:	0
Pid:	51710
sleep300
lrwxrwxrwx 1 zaeem zaeem 0 Sep  2 16:50 /proc/51710/cwd -> /home/zaeem

What to read out of it:

  • State: S (sleeping) — the rat is waiting (for its timer), not running. A4 gives you the full alphabet.
  • cmdline printed sleep300 glued together: the arguments are separated by NUL bytes, which the terminal doesn't show. (Module 6's -print0 used the same trick — NUL as the one safe separator.)
  • cwd is a live symlink to the process's working directory — Module 2's "each shell stands somewhere" generalized to every process, and readable from outside. Debugging "which directory is that service actually running in?" is one ls -l away.

A4. Process states — the STAT alphabet

🧠 At any instant, each process is in exactly one state. The ones that matter, with their ps letters:

  • R — running/runnable: on a CPU, or in the queue wanting one.
  • S — interruptible sleep: waiting for something (a timer, input, a network packet) — the healthy resting state; most processes on any machine are S.
  • D — uninterruptible sleep: waiting on I/O (usually disk) at a level where even signals don't reach it. Rare, brief — unless storage is sick, when D-state pileups become the smoking gun (Module 14 hunts them).
  • T — stopped: paused by a signal (Ctrl+Z — C2).
  • Z — zombie: finished, but its exit status awaits collection (Part D's star).
Diagram source
flowchart LR
    N["created<br>(fork)"] --> R["R<br>running"]
    R -->|"waits for event"| S["S<br>sleeping"]
    S -->|"event arrives"| R
    R -->|"disk I/O"| D["D<br>uninterruptible"]
    D --> R
    R -->|"SIGTSTP / Ctrl+Z"| T["T<br>stopped"]
    T -->|"fg / bg"| R
    R -->|"exit"| Z["Z<br>zombie"]
    Z -->|"parent collects status"| G["gone"]
Counter-intuitive: kill -9 cannot touch a D-state process. Uninterruptible means uninterruptible — the process is inside a kernel operation that must complete; signals queue politely outside. When a process ignores even SIGKILL, it is not defying you: it is stuck in D, almost always on broken storage (a dead NFS server is the classic), and the storage is the thing to fix. Everyday intuition says the boss can always fire anyone; D-state is the employee mid-surgery — not refusing, unreachable.
Real-world analogy — the staff status board

The office board: at desk working (R), waiting on a delivery (S), in the clean-room, do not disturb (D), on pause (T), left the company, badge not yet returned (Z). A glance at the board — ps's STAT column — tells you where everyone is.

Where the analogy stops working. People linger between statuses; processes never do — the kernel flips them atomically, millions of times a second, and the board you read is one frozen frame. Also: a whole office "waiting on deliveries" is normal for processes (nearly everything is S nearly always); a human manager would panic at that board.

🧪 Exercise A4.1 — Catch three states live
bash
sleep 300 &
ps -o pid,stat,comm $!          # the sleeper: expect S
ps -o pid,stat,comm $$          # your shell right now: expect S+... but wait, it's running you?
kill $!
Expected result — click to reveal
javascript
  PID STAT COMMAND
51890 S    sleep
  PID STAT COMMAND
51442 Ss   bash

What to read out of it:

  • The sleeper: S, as designed — waiting on its timer.
  • Your shell: also S (plus modifier letters: s = session leader; you may see + = foreground). But you're using it! — yes, and at the instant ps looked, bash was waiting for ps to finish (the fork-exec-wait of A2, visible as a state). The process that was R was ps itself.
  • Modifier letters after the state (s, +, l, <, N) are footnotes, decoded in ps(1)'s PROCESS STATE CODES section — the N will make sense after C4.

Part A — Interview questions

🎯 "Name the first process that is started by the kernel in Linux and what is its process id?" — asked verbatim in InterviewBit's Linux Interview Questions (2025)

PID 1 — traditionally init, on virtually all modern distributions systemd (the name init often survives as a symlink). The kernel starts it at boot; it starts everything else, directly or through descendants, and it inherits (adopts) any process whose parent dies. It cannot be killed; its exit is a kernel panic.

The details that separate candidates: saying "systemd, historically init" shows current knowledge; the adoption role (previewing orphans) shows depth; and noting that in containers PID 1 is whatever the container was started with — often your application itself, with real consequences for signal handling (a Module 16 story) — shows production experience.

🎯 "What are the Process states in Linux?" — asked verbatim in Turing's 100+ Linux interview questions (2025); InterviewBit words it "What do you mean by a Process States in Linux?"

By ps letters: R runnable (on CPU or queued for it), S interruptible sleep (waiting for an event — the normal state of nearly everything), D uninterruptible sleep (mid-kernel-I/O; signals wait outside), T stopped (paused via Ctrl+Z/SIGSTOP), Z zombie (exited; status awaiting the parent's collection). Textbooks add New and Terminated around the edges.

The details that separate candidates: leading with the ps letters (shows you read states, not just recite them); the two operational stars — D-state pileups as a storage-sickness signature, and Z as "already dead, unkillable, fix the parent"; and knowing S vs D is precisely "can a signal reach it".

Part B — Watching processes

B1. ps — the snapshot, and reading its columns

Official docs: ps(1)

🧠 Two dialects coexist (history again): BSD-style ps aux and POSIX-style ps -ef — both mean "everything, with detail", and every engineer settles on one. This track uses ps aux. Its columns, left to right: USER (Module 4), PID, %CPU and %MEM (shares of processor and physical memory), VSZ/RSS (virtual/resident memory — Module 14 dwells here), TTY (owning terminal, ? for none — remember that ?; it defines daemons in Part D), STAT (A4's alphabet), START, TIME (accumulated CPU time — not wall-clock age!), COMMAND. The workhorse composition: ps aux | grep thing — with the classic wrinkle that grep finds itself (its own process's command line contains the pattern); grep -v grep or Part B3's pgrep solves it.

Real-world analogy — the office photograph

ps walks in, takes one flash photograph of the whole office, and hands it to you. Everyone is in it — including the photographer, mid-flash, which is why ps aux | grep x catches the grep and plain ps catches the ps.

Where the analogy stops working. A photo is of a moment, and moments age: the process you spotted may be gone before your next command, and TIME is not "how long employed" but "total minutes actually working" — an idle year-old daemon shows 0:00. Photos answer "what was"; for "what is, continuously", B2's live window.

🧪 Exercise B1.1 — Read full rows fluently
bash
sleep 300 &
ps aux | head -3          # header + the top of the list
ps aux | grep sleep       # find the rat — and the observer effect
ps aux | grep sleep | grep -v grep
kill $!
Expected result — click to reveal
javascript
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.4  22540  6300 ?        Ss   15:14   0:04 /sbin/init
zaeem    51950  0.0  0.0   2716  1764 pts/0    S    16:52   0:00 sleep 300
zaeem    51952  0.0  0.0   6420  2260 pts/0    S+   16:52   0:00 grep --color=auto sleep
zaeem    51950  0.0  0.0   2716  1764 pts/0    S    16:52   0:00 sleep 300

What to read out of it (your PIDs, values, and PID-1 spelling vary):

  • Read the sleeper's row aloud, column by column, until it takes five seconds. Interviewers put a ps aux row in front of candidates exactly as they do an ls -l row.
  • The grep found itself — its command line (grep --color=auto sleep) contains "sleep". The three-stage pipe removed it. Knowing why this happens beats memorizing the workaround.
  • PID 1's TTY is ?: no terminal — it outlived (never had) one. Hold that thought for Part D.

B2. top — the live window

Official docs: top(1)

🧠 top redraws the picture every few seconds: a header of machine-wide vitals, then processes ranked by CPU. It is the answer to GfG's interview question "a server is slow — which command first?", precisely because one screen triages what kind of slow: the header's load average (three numbers — 1, 5, 15-minute pressure gauges; Module 14 calibrates them), memory and swap lines, and the %CPU/%MEM ranking below. Keys inside: M sort by memory, P back to CPU, k kill by PID (politely, by default), h help, q quit — and htop, where installed, is the same idea with colour and mouse.

Counter-intuitive: %CPU routinely exceeds 100. The column is per core: a process saturating four cores shows 400%. A machine with 8 cores at load average 6 is busy-but-coping; the same load on 2 cores is drowning. Neither number means anything without knowing the core count (nproc prints it) — the dial's maximum depends on the machine, which is unlike any dashboard dial civilians meet.
Real-world analogy — mission control

top is the wall at mission control: vital signs across the top, the noisy subsystems ranked below, refreshed on a heartbeat. You do not stare at it all day; you pull it up when something feels wrong and read the room in ten seconds.

Where the analogy stops working. Mission control keeps telemetry history — the wall shows trends. top shows only now, redrawn and discarded; the spike that ended four seconds ago is simply gone. That gap — live view versus recorded history — is the entire reason monitoring systems (Module 14) exist.

🧪 Exercise B2.1 — Ten seconds of triage
bash
top            # inside: watch one refresh; press M (memory sort); press P (cpu sort); press q
nproc          # how many cores calibrate those percentages?
Expected result — click to reveal

A full-screen live display headed by something like:

javascript
top - 16:54:01 up  1:40,  1 user,  load average: 0.08, 0.12, 0.10
Tasks: 123 total,   1 running, 122 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.7 us,  0.3 sy,  0.0 ni, 98.9 id,  0.0 wa, ...
MiB Mem :   3921.3 total,   2103.9 free, ...

then the ranked process table; q returns your prompt, and nproc prints a small integer.

What to read out of it (every number is your machine's own):

  • Second line: the state census — A4's alphabet, counted machine-wide. 122 sleeping is healthy normality; a nonzero zombie count is Part D calling.
  • %Cpu(s) decodes as: us user programs, sy kernel work, id idle, and wawaiting on disk I/O — the number that, when large, says "the CPU is fine; storage is the bottleneck" (D-state's machine-wide echo).
  • Load average versus nproc is the ratio to carry: load ≈ cores means fully used; load ≫ cores means queued work. Module 14 turns this into a method.

B3. pgrep and pkill — processes by name, honestly

🧠 pgrep pattern prints PIDs whose process name matches; pgrep -a adds the command line; pgrep -u alice filters by user. pkill is the same matcher that signals what it finds — select-then-act, like Module 6's find/-exec, for processes. Together they retire the ps aux | grep | grep -v grep | awk '{print $2}' chain — and introduce one sharp edge worth respecting immediately.

Trap — pkill matches substrings of process names. pkill ssh matches ssh, sshd, and ssh-agent — on a remote server, that trio includes the very connection you are typing over (Module 15 will make that mistake vivid). Defenses: test first with pgrep -a pattern (see the full list before signalling it), use -x for exact-name match, and prefer full patterns (pkill -x sshd never touches ssh). pgrep-before-pkill is the process world's -print-before--delete.
Real-world analogy — paging by name

ps+grep is photographing the office and squinting for a face. pgrep is the tannoy: "would all staff named Sleep come to reception" — direct, current, by name. pkill is the same tannoy dismissing them.

Where the analogy stops working. The tannoy calls exact names; pgrep calls everyone whose name contains the sound — page "Ann" and Anna, Hannah, and Joanne stand up. With pkill, they're all dismissed. -x restores exact-name paging; without it, always read the list first.

🧪 Exercise B3.1 — Page, verify, dismiss
bash
sleep 300 &
sleep 400 &
pgrep sleep            # PIDs only
pgrep -a sleep         # with command lines — ALWAYS this before pkill
pkill -x sleep         # exact name, both rats dismissed
pgrep -a sleep; echo "exit=$?"
Expected result — click to reveal
javascript
52101
52103
52101 sleep 300
52103 sleep 400
exit=1

What to read out of it:

  • Two matches, verified with -a (you can tell the 300 from the 400), then one exact-match pkill for both. The empty final check with exit 1 is grep's convention (Module 6): "nothing found" — here, exactly what success looks like.
  • Your shell will also print two Terminated job notices between commands — that is C2's machinery announcing the deaths; read on.

Part B — Interview questions

🎯 "How do you view all running processes?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026); Turing (2025) asks "What is the ps command in Linux? How can you display a hierarchical view of processes using the ps command?"

ps aux (BSD style) or ps -ef (POSIX style) — every process, with owner, PID, resource shares, state, and command. Hierarchical: ps -e --forest draws the parent-child tree (Turing's own cited answer), and pstree where installed. For one process by name, pgrep -a name beats the grep chain. Live and ranked rather than a snapshot: top.

The details that separate candidates: knowing both dialects exist and why (ps aux vs ps -ef is BSD-vs-POSIX history, Module 7's dialect lesson recurring); reading a full row fluently on request; and -o for custom columns feeding scripts — ps -e -o pid,ppid,stat,comm is monitoring in embryo.

🎯 "A server is slow. Which command do you run first and why?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)

top (or htop) — one screen answers the triage questions in order: is the CPU actually busy (%Cpu line), busy with what kind of work (us vs sy vs wa — user code, kernel, or waiting on disk), is memory exhausted (Mem/Swap lines), is load queued beyond the core count (load average vs nproc), and who is responsible (the ranked table). Each answer routes the investigation differently — high wa sends you to storage, high %MEM plus swap churn to memory, one process at 400% to that process.

The details that separate candidates: narrating the decision tree, not just naming the tool; the load-average-per-core calibration; and the honest caveat that top shows now, not history — "I'd confirm against monitoring for when it started" separates operators from tool-namers. (Module 14 builds the full method this answer sketches.)

Part C — Controlling processes

C1. Signals — and why kill rarely kills

🧠 A signal is a small numbered notification the kernel delivers to a process. kill — mistitled by history — sends signals; which one decides everything:

  • SIGTERM (15) — the default. "Please shut down." The process can catch it and clean up: flush buffers, close connections, remove lock files, then exit. Every well-written service handles TERM.
  • SIGKILL (9) — not deliverable to the process at all: the kernel simply destroys it. No cleanup, no goodbye. Uncatchable, unblockable — and therefore a last resort, because whatever TERM-cleanup would have done remains undone.
  • SIGHUP (1) — "your terminal hung up." Kills terminal-attached processes by default (C3's whole story); many daemons repurpose it as "reload your config".
  • SIGSTOP (pause) and SIGCONT (resume) — SIGSTOP is uncatchable like SIGKILL; SIGCONT's resume is unconditional too, though a program may catch it to notice it was resumed. C2's Ctrl+Z uses their catchable, polite sibling SIGTSTP.

kill -l lists all of them. The professional escalation ladder is fixed: TERM, wait, verify, then — reluctantly — KILL.

Counter-intuitive: kill is a request, and "kill -9 first" is malpractice. kill PID sends TERM — a note the process may act on in a millisecond, a minute (finishing a transaction), or never (bug). Impatience-with-a-9 skips every safeguard the program's authors wrote: half-written files, orphaned locks, corrupted queues are the classic aftermath. And when even -9 "fails", nothing was defied — the process is a zombie (already dead, Part D) or in D-state (unreachable, A4). The 9 never needs to be first; it only ever needs to be last.
Real-world analogy — closing time

TERM is the barman's "last orders, please" — patrons finish drinks, settle tabs, leave in order. KILL is cutting the power and carrying everyone out: effective, instant, and the tabs never get settled. HUP is the phone-era operator saying "your caller hung up" — most staff go home at that news; some (daemons) treat it as "shift change: re-read your instructions".

Where the analogy stops working. A barman sees whether last-orders is working. kill returns instantly, telling you only that the note was delivered — never what the process did about it. Verification is a separate act (pgrep, and the wait in the ladder), and forgetting it is how "I killed it" and "it's still running" coexist in one incident.

🧪 Exercise C1.1 — The ladder, rung by rung
bash
sleep 300 &
kill $!                    # rung 1: TERM (default)
sleep 1
pgrep -x sleep; echo "exit=$?"   # rung 2: VERIFY
sleep 300 &
kill -9 $!                 # the last resort, demonstrated on a rat that deserved better
sleep 1
jobs
Expected result — click to reveal
javascript
[1]+  Terminated              sleep 300
exit=1
[2]+  Killed                  sleep 300

What to read out of it:

  • The shell's job notices name the manner of death: Terminated (TERM, honoured) versus Killed (KILL, destroyed). The vocabulary is precise everywhere it appears — including in Module 14's logs when the kernel's OOM killer writes Killed into a service's obituary.
  • The verify rung returned exit 1 — no survivors — before any escalation was contemplated. sleep dies politely to TERM; real services may need a grace period between rungs (deploy systems formalize this: TERM, wait 30, KILL — Module 11 shows systemd doing exactly that on every stop).

C2. Job control — &, Ctrl+Z, jobs, fg, bg

🧠 Your shell can run several things at once. command & starts it in the background — you get the prompt back immediately, plus a job number and PID. Ctrl+Z pauses (SIGTSTP → state T) the foreground command and returns your prompt; jobs lists this shell's jobs; fg %1 brings job 1 back to the foreground; bg %1 lets a paused job continue in the background. The classic save: you ran an editor or a long command, need your prompt urgently — Ctrl+Z, do the thing, fg.

Real-world analogy — the desk and the side table

One document is open on your desk (foreground — it has your keyboard). Ctrl+Z sets it on the side table mid-sentence (paused, exactly as you left it); bg hands it to an assistant to keep working off-desk; fg pulls it back under your hands. jobs is a glance at the side table.

Where the analogy stops working. Side tables are visible to the whole office. Jobs belong to one shell: another terminal's jobs shows nothing, %1 means nothing there — the job table is your desk's private drawer, and it vanishes with the desk (close the shell, and what that implies for the jobs is C3's subject).

🧪 Exercise C2.1 — Juggle three states of work
bash
sleep 300              # foreground — your prompt is gone. Now press Ctrl+Z
jobs                   # it's on the side table, Stopped
bg %1                  # let it continue in the background
jobs                   # now Running
fg %1                  # back to the foreground — prompt gone again. Ctrl+C to end it (M3's interrupt)
Expected result — click to reveal
javascript
[1]+  Stopped                 sleep 300
[1]+  sleep 300 &
[1]+  Running                 sleep 300 &
sleep 300
^C

What to read out of it:

  • One process, driven through T → running-in-background → foreground → interrupted, with jobs narrating each transition. A4's state diagram, played on instruments.
  • Ctrl+Z versus Ctrl+C, permanently: Z pauses (resumable — nothing is lost), C interrupts (SIGINT — a politer cousin of TERM, and the process usually exits). Confusing them costs either a lost process or a stuck terminal, both weekly classics.

C3. nohup — surviving the hangup

Official docs: nohup(1)

🧠 Module 1 promised this answer. When a terminal closes — window closed, SSH dropped, laptop slept — its processes receive SIGHUP, and the default response to HUP is death. That is the entire mystery of "my long job died when I disconnected." nohup command & starts the command immune to HUP, its output redirected to nohup.out (a terminal-less process needs somewhere for stdout — Module 5's plumbing, applied). The job now survives your departure; find it again later with pgrep -a, not jobs (new shell — empty drawer).

Real-world analogy — work that survives your shift

Normally, contractors down tools when the site office closes (HUP at terminal-close). nohup is written authorization to keep working after hours, with instructions to slide reports under the door (nohup.out) since nobody is at the desk to receive them.

Where the analogy stops working. After-hours authorization does not survive the building burning down: nohup outlives your session, not a reboot, and nobody restarts it if it crashes at 3 a.m. Work that must always be running is not a backgrounded command but a service — supervised, restarted, logged — and that is Module 11's entire subject. nohup is for one-off long jobs; treating it as a service manager is the anti-pattern interviewers probe for.

🧪 Exercise C3.1 — Immortal (for one session)
bash
cd ~
nohup sleep 300 &
cat nohup.out            # empty so far — sleep says nothing; the file exists as the catch-basin
ps -o pid,ppid,tty,comm $!
kill $!                  # tidy up; rm nohup.out too
Expected result — click to reveal
javascript
nohup: ignoring input and appending output to '/home/zaeem/nohup.out'
    PID   PPID TTY      COMMAND
  52488  51442 pts/0    sleep

What to read out of it:

  • nohup announced its arrangements: input ignored, output appended to the named file. On a real long job, tail -f nohup.out (Module 3) is how you check on it from any later session.
  • The proof of immunity you can't easily see in one exercise: close this terminal entirely, open a new one, and pgrep -a sleep — still there, now with PPID 1 (adopted — Part D explains by whom and why). Try it; it is the module's best two-minute experiment.

C4. nice and renice — sharing the CPU politely

Official docs: nice(1)

🧠 Every process has a niceness from −20 to 19: higher is humbler — a nicer process yields the CPU to less-nice ones when they compete. Default 0. nice -n 10 command starts a command humbler; renice -n 5 -p PID adjusts a runner. Two rules with Module 4 echoes: any user may make their own processes nicer; only root may make anything less nice (raise its priority). The everyday use: batch work — backups, compression, report jobs — started at nice -n 19 so it soaks up idle CPU without ever elbowing the service traffic.

Real-world analogy — the buffet queue

Niceness is queue manners at the office buffet: a very nice colleague (19) waves everyone ahead and eats only when the line is empty; a priority visitor (−20, management-approved only) is ushered to the front. When food is abundant, manners are invisible — everyone eats at once.

Where the analogy stops working. That last sentence is the trap intuition misses: niceness matters only under contention. On an idle machine, a nice-19 job runs at full speed — nice is not a throttle, not a speed limit, and benchmarking "nice makes it slower" on a quiet box shows nothing. It re-orders the queue; it does not shrink anyone's plate when there's no queue at all.

🧪 Exercise C4.1 — Humility, visible in the columns
bash
nice -n 10 sleep 300 &
ps -o pid,ni,stat,comm $!     # NI column: the niceness; note the state modifier
renice -n 15 -p $!
ps -o pid,ni,stat,comm $!
kill $!
Expected result — click to reveal
javascript
    PID  NI STAT COMMAND
  52560  10 SN   sleep
52560 (process ID) old priority 10, new priority 15
    PID  NI STAT COMMAND
  52560  15 SN   sleep

What to read out of it:

  • NI shows 10 then 15 — renice adjusted a live process. The STAT modifier N (A4.1's promised footnote) marks any nice-valued process at a glance in full ps aux listings.
  • Try renice -n 5 -p on it (less nice than 15): as a normal user, Permission denied — the kernel refusing to let you raise priority, even on your own process. Down freely, up by privilege only. (Renicing another user's process fails differently — Operation not permitted — but the lesson is the same ladder from Module 4.)

Part C — Interview questions

🎯 "How do you terminate a process in Linux?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)

kill PID sends SIGTERM — a catchable shutdown request that lets the process clean up; verify with pgrep, and only after a grace period escalate to kill -9 PID (SIGKILL — uncatchable, no cleanup). By name: pkill -x name (after a pgrep -a preview). From top: the k key. The ladder — TERM, wait, verify, KILL — is the answer; the tools are details.

The details that separate candidates: explaining why -9 is last (skipped cleanup: locks, buffers, half-written state); the two cases where -9 "fails" and what each really means (zombie: already dead, reap via parent; D-state: storage problem, not a process problem); and mentioning that service processes should be stopped via the service manager (Module 11), not raw kill — which is what interviewers running production actually want to hear.

🎯 "What are jobs, bg and fg?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)

Shell job control: jobs lists the current shell's background/stopped jobs; Ctrl+Z stops (pauses) the foreground job; bg %n resumes a stopped job in the background; fg %n brings a job to the foreground; command & starts backgrounded from the outset. Job numbers (%1) are per-shell handles over the underlying PIDs.

The details that separate candidates: the per-shell scope (another terminal sees nothing — jobs are not a system-wide list); the signal names beneath the keys (Ctrl+Z = SIGTSTP, Ctrl+C = SIGINT); and the boundary — jobs die with their shell's terminal via SIGHUP, so anything that must outlive the session graduates to nohup, or properly to a service.

🎯 "What are nice and renice?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026)

Niceness (−20 to 19, default 0, higher = humbler) biases the CPU scheduler among competing processes. nice -n 10 cmd launches humbler; renice -n 15 -p PID adjusts a live one. Users may only increase niceness of their own processes; decreasing (raising priority) is root's. Canonical use: nice -n 19 for batch jobs sharing a box with latency-sensitive services.

The details that separate candidates: "only under contention" — an idle machine runs nice-19 at full speed, so nice is queue ordering, not throttling; the N flag in ps STAT as the visual tell; and the neighbouring tool for the disk dimension of the same courtesy, ionice (Module 14 territory), which shows you think in resources, not just CPU.

Part D — Zombies, orphans, and daemons

D1. Zombies — dead, and unkillable because of it

Official docs: proc(5) · signal(7)

🧠 A2's trio was fork, exec, wait — and the wait is load-bearing. When a child exits, the kernel keeps one small record — PID, exit code — until the parent collects it (the wait). Between death and collection, the child is a zombie: state Z, <defunct> in ps, occupying nothing but a process-table slot. A prompt parent collects within milliseconds; you never see those. Persistent zombies mean one thing: a buggy parent that never waits. They consume no CPU, no memory of consequence — their only real threat is at volume, when thousands of leaked slots exhaust the PID space.

Counter-intuitive: a zombie cannot be killed — it is already dead. kill -9 a zombie and nothing changes: signals act on running processes, and there is nothing running — only an uncollected death certificate. The fix is always the parent: signal it to make it reap, or terminate it — whereupon the zombies are adopted by PID 1, which reaps compulsively, and they vanish. Every instinct says "kill the zombies"; the mechanism says "there is nothing there; deal with the negligent next of kin."
Real-world analogy — the uncollected death certificate

The registry keeps a certificate until next of kin collects it. A zombie is the certificate: the person is gone; only paperwork remains, one slot in one filing cabinet. A pile of uncollected certificates says nothing about the deceased — it indicts the family that never came.

Where the analogy stops working. Registries purge old records eventually. The kernel never discards an unreaped zombie while its parent lives — the exit code might yet be asked for, and losing it would break the fork/wait contract. Only reparenting to PID 1 (parent's death) triggers cleanup. Bureaucratically perfect memory, forever, by design.

🧪 Exercise D1.1 — Manufacture a zombie, fail to kill it, then dissolve it
bash
# A parent that spawns a child and never waits: the child backgrounds, then
# `exec` replaces the shell with sleep 300 — a program that will never wait() for anything.
bash -c 'sleep 2 & exec sleep 300' &
sleep 3                              # let the child die, unreaped
ps -o pid,ppid,stat,comm --ppid $!   # children of our negligent parent
kill -9 $(ps -o pid= --ppid $!)      # try to kill the zombie
ps -o pid,ppid,stat,comm --ppid $!   # still there?
kill $!                              # terminate the PARENT instead
sleep 3                              # adoption + reaping take a beat
ps -e -o stat= -o comm= | grep '^Z'; echo "exit=$?"
Expected result — click to reveal
javascript
    PID   PPID STAT COMMAND
  52710  52708 Z    sleep <defunct>
    PID   PPID STAT COMMAND
  52710  52708 Z    sleep <defunct>
exit=1

What to read out of it:

  • The short-lived sleep 2 died; its parent — now being sleep 300 thanks to the exec (A2's metamorphosis, used deliberately), a program that waits for nothing — left it in Z. Some ps versions dress the row as sleep <defunct>, others just show the bare name with STAT Z; the Z is the diagnosis either way.
  • The kill -9 changed nothing — the second listing is identical. Dead already; point made.
  • Killing the parent dissolved it: orphaned zombies get adopted by PID 1 and reaped (allow a few seconds — hence the second sleep). Final check: no Z-state processes anywhere, exit 1 on the grep. The whole zombie lifecycle, in ten lines of terminal.

D2. Orphans — adopted, instantly

Official docs: proc(5)

🧠 The mirror image: a parent dies while its child runs on. The child — an orphan — is reparented immediately and automatically to PID 1 (or, on modern systemd machines, a designated "subreaper"), which dutifully waits on it thereafter. No harm, no drama: orphaning is routine — and, unlike zombiehood, sometimes deliberate: C3's nohup experiment left an orphan on purpose, and classical daemons orphan themselves as a rite of birth (D3).

Real-world analogy — the ward of the state

When a guardian dies, the state assumes guardianship — instantly, by law, no gap. PID 1 is the state: every process abandoned by its parent becomes its ward, and it performs the one duty that matters here (collecting death certificates) with perfect diligence.

Where the analogy stops working. Human guardianship transfers care — feeding, housing, attention. PID 1's adoption transfers only the bookkeeping duty: nobody restarts an orphan that crashes, watches its output, or knows what it was for. Adoption prevents zombies; it provides nothing else — which is precisely the gap between "my process survives" (nohup) and "my process is managed" (Module 11).

🧪 Exercise D2.1 — Watch an adoption
bash
bash -c 'sleep 300 &'      # parent starts a child... and immediately exits
sleep 1
pgrep -a -x sleep
ps -o pid,ppid,comm $(pgrep -x sleep)
kill $(pgrep -x sleep)
Expected result — click to reveal
javascript
52820 sleep 300
    PID   PPID COMMAND
  52820      1 sleep

What to read out of it:

  • PPID 1: the child's parent (that transient bash -c) died between two of your commands, and adoption had already happened before you could look. There is no orphanage queue; there is not even a discernible moment.
  • (On desktop Ubuntu you may see a different adopter PID — a per-session systemd --user acting as subreaper; same mechanism, delegated. The --forest view shows who.)

D3. Daemons — the processes with no terminal

Official docs: ps(1) · proc(5)

🧠 A daemon is a long-running background process providing a service — sshd (Module 15), cron, web servers, databases. The visible signature you already met in B1: TTY = ? — no controlling terminal, hence immune to the whole HUP drama, no one's foreground, nobody's job. Classically a daemon made itself this way at startup (fork twice, orphan yourself into PID 1's care, detach from the terminal — the "double fork" ritual, C3+D2 composed deliberately); on modern Linux, systemd starts services daemon-shaped from the first instant, and the self-daemonizing ritual survives mainly in old software and interview questions. The convention of the trailing d — sshd, cron... — names the tribe.

Real-world analogy — the building's night staff

Daemons are the boiler-room crew: no desk in any office (no TTY), on duty regardless of who is logged in upstairs, noticed only when the heating stops. The trailing-d names on the roster — sshd, crond — are the uniform.

Where the analogy stops working. Night staff are hired, supervised, and replaced by management. Classical daemons hired themselves (the double-fork ritual) and answered to nobody — which is exactly the operational problem: who restarts a crashed self-employed boiler-man at 4 a.m.? Modern Linux's answer — put management (systemd) in charge of hiring, watching, and restarting — is Module 11, and this analogy's gap is that module's reason to exist.

🧪 Exercise D3.1 — Census of the terminal-less
bash
ps -e -o pid,tty,comm | awk '$2 == "?"' | head -12    # Module 7 earning rent
ps -e -o tty | grep -c '?'                            # how many daemons-and-kin altogether?
Expected result — click to reveal
javascript
  1 ?        systemd
  2 ?        kthreadd
...
412 ?        cron
498 ?        sshd

then a count — typically dozens on a server, low hundreds on a desktop.

What to read out of it (names and count are your machine's roster):

  • Almost everything real on a server is in this list: the kernel's own threads (kthreadd's clan), then the service tribe — cron, sshd, journald, and whatever the machine is for. The terminal-attached processes you have been studying are, numerically, a rounding error.
  • Each named daemon here becomes personally familiar across Modules 11–15. Today's takeaway is the shape: TTY ?, parented near 1, long TIME uptimes, and none of them dies when you log out.

Part D — Interview questions

🎯 "What are zombie and orphan processes?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026); Turing (2025) asks "What is Zombie Process?" and notes zombies "do not cause performance problems" by themselves

Zombie: exited, but its exit status awaits collection by its parent's wait() — state Z, <defunct>, consuming only a process-table slot; persistent zombies indict a parent that never reaps. Orphan: still running after its parent died — instantly adopted by PID 1 (or a subreaper), which reaps it properly on exit. Opposite pathologies of the same fork/wait contract: the zombie's parent lives and neglects; the orphan's parent is gone and the system compensates.

The details that separate candidates: "you can't kill a zombie — it's already dead; fix the parent" delivered as one breath; the escalation for real cases (signal the parent to reap — some apps honour SIGCHLD handling fixes — else restart the parent and let PID 1 clean up); and the volume threat (PID-space exhaustion) as the reason monitoring counts Z-states at all. Turing's own note — harmless in ones — is the right calibration.

🎯 "What is a daemon in Linux?" — asked verbatim in GeeksforGeeks' Linux Interview Questions (70+ questions, updated July 2026); InterviewBit (2025) asks "What do you mean by the daemons?" and "Name daemon that controls the print spooling process."

A long-running background service process with no controlling terminal (TTY ? in ps) — sshd, cron, journald, database and web servers; conventionally suffixed d. Classically self-daemonized at birth (double-fork to orphan into PID 1, detach from the terminal, redirect stdio); on systemd machines, services are simply started detached and supervised, making the ritual historical. InterviewBit's trivia answer: the line printer daemon, lpd (its modern descendant is CUPS's cupsd).

The details that separate candidates: the TTY-? signature as the observable definition; why detachment matters (immunity to session HUP — daemons must outlive every login); and the modern framing — "under systemd, daemonizing is the manager's job, not the program's" — which turns a definition question into evidence you have operated services this decade.

Part E — Toolkit

Official docs: Part E is reference material — every source it draws on is linked in the E3 documentation table below.

E1. Production practice — symptoms and fixes

SymptomWhat is really happeningWhat to runThe fix
Server sluggish, cause unknownCPU, memory, or I/O saturation — top's header discriminatestop — read %Cpu (us/sy/wa), Mem/Swap, load vs nprocRoute by the answer: high wa → storage; swap churn → memory; one process at 400% → that process
kill PID did nothing — process still thereIt caught TERM and is mid-cleanup, OR ignores it (bug), OR is a zombie/D-stateps -o pid,stat,comm PID — read STATZ: fix the parent. D: fix the storage. Running: wait, then escalate to -9
kill -9 "fails"Nothing running to signal: zombie (already dead) or D-state (unreachable)ps -o stat= PID → Z or DZombie → signal/restart the PARENT; D → investigate disk/NFS, not the process
Long job died when the SSH session droppedTerminal close sent SIGHUP; default action is death(after the fact) pgrep -a finds nothingRe-launch under nohup … &, tmux/screen, or — properly — as a service (Module 11)
pkill name killed more than intended (maybe your own session)Substring matching hit siblings (ssh → sshd, ssh-agent)Always pgrep -a name firstpkill -x exactname; preview before signalling, every time
Monitoring alerts on growing zombie countA parent process leaks children — never reapsps -e -o ppid,stat | awk '$2 ~ /Z/' → find the common PPIDFix or restart that parent; PID 1 then reaps the backlog
Batch job starves the live service on a shared boxEqual CPU priority under contentionps -o ni,comm on the batch jobrenice 19 it (and ionice for disk); next time launch with nice -n 19

E2. Capstone — four tickets

Work these like real tickets: read the ticket, write the commands and the explanation you would send, then open the worked answer. Everything needed was taught in this module.
🎫 Ticket 1 — "Web node pegged at 100%+ CPU — identify and calm it without an outage"

Ticket text: "vm-web-3 alerting on CPU. Find what's eating it, confirm it's safe to touch, and reduce impact — do NOT hard-kill a request-serving process."

Worked answer: top first — read the header (is it genuinely CPU, or is high wa misleading us toward storage?), then the ranked table for the offender and its %CPU against nproc (450% on 8 cores is heavy, not fatal). Identify it properly before acting: ps -o pid,ppid,user,ni,etime,comm PID — is it the app, a runaway cron report, a stuck log-rotation? If it is a batch interloper on a web box, renice 19 -p PID calms it without killing anything (nice matters under exactly this contention). If it is the web app itself spinning, do not kill — that is an outage; capture evidence (a top snapshot, /proc/PID/status) and route to the app owners / restart via the service manager (Module 11) with a grace period. The ticket's own constraint — no hard-kill of a serving process — is the senior instinct being tested; honour it out loud.

🎫 Ticket 2 — "Deploy script's kill -9 corrupts the database on stop"

Ticket text: "Our stop step is pkill -9 -f ourdb. Roughly one stop in ten, the DB comes back needing recovery. Explain the causal link and rewrite the stop procedure."

Worked answer: causal link — SIGKILL is uncatchable, so the database gets no chance to flush its write buffers, finish the in-flight transaction, and checkpoint; one stop in ten catches it mid-write, hence recovery on restart. The -9 is the bug. Rewrite as the escalation ladder: send SIGTERM (pkill -TERM -x ourdb — and note -x, because -f ourdb substring-matches every command line mentioning ourdb, including your deploy script itself), wait for a real grace period (databases can take tens of seconds to checkpoint), verify with pgrep, and only if it is genuinely stuck escalate to -9 as a logged exception, not a default. Better still: stop it through its service manager (Module 11), which encodes exactly this TERM-wait-KILL sequence with a configured timeout — the reason services exist instead of raw kills.

🎫 Ticket 3 — "Thousands of <defunct> processes — is the box compromised / out of memory?"

Ticket text: "Monitoring shows 4,000+ processes in Z state on vm-batch-1 and climbing. Are we under attack? Are we leaking memory? What do we actually do?"

Worked answer: neither attack nor memory leak — zombies hold no memory and run no code; they consume PID-table slots, and 4,000 climbing means the box is heading for PID exhaustion (new forks will start failing with "resource temporarily unavailable" — the real outage on the horizon). Root cause is a single negligent parent not reaping its children: find it — ps -e -o ppid,stat | awk '$2 ~ /Z/ {print $1}' | sort | uniq -c | sort -rn | head (Module 7's tally, pointed at PPIDs) — the top PPID is the culprit. Fix: signal that parent to reap if it can be nudged, otherwise restart it; its zombies reparent to PID 1 and vanish. Then the real fix: file a bug — the parent needs to wait() on its children (or handle SIGCHLD) — because it will recur. Reassure the ticket: no compromise, no memory issue, but genuine urgency before PID exhaustion.

🎫 Ticket 4 — "The migration job vanishes every time the ops engineer goes home"

Ticket text: "A 6-hour data migration is started over SSH and keeps dying around the time the engineer disconnects for the evening. They swear they leave it running. Explain, and give them a reliable way."

Worked answer: the disconnect is the cause — closing the SSH session (or losing it to a sleeping laptop) delivers SIGHUP to its child processes, and the migration, foreground in that session, dies. "I left it running" and "the session ended" are the same event. Reliable options, in ascending order of correctness: nohup ./migrate.sh > migrate.log 2>&1 & (immune to HUP, output captured, checked later with tail -f migrate.log) — fine for a one-off; better, run it inside tmux/screen (a persistent session you can detach from and reattach to from anywhere — the tool built for exactly this); best, for anything recurring, make it a service or a scheduled unit (Module 11), which also gives restart-on-failure that nohup and tmux do not. Name the boundary explicitly: nohup and tmux keep a job alive; only a service keeps it managed.

E3. Documentation reference

TopicAuthoritative sourceVerified link
Viewing processesps(1), top(1), pgrep(1)ps(1) · top(1) · pgrep(1)
Signals and terminationsignal(7), kill(1)signal(7) · kill(1)
Priority and hangup-immunitynice(1), nohup(1)nice(1) · nohup(1)
The window into processesproc(5)proc(5)
The theory beneath this moduleCompanion trackUntitled · Untitled

E4. Self-assessment

Answer out loud, without notes. The section number tells you where to re-read.

  1. Program versus process — define each precisely, and give the three-verb story of how a command becomes a running process. (A1–A2)
  2. What is PID 1, what two duties does it perform, and what happens if it exits? (A1, D2)
  3. /proc/self/status — what is it, why does ls -l show it as size 0, and name three things you can learn from a process's /proc directory. (A3)
  4. The STAT letters R, S, D, T, Z — meaning of each, and which one kill -9 cannot touch and why. (A4)
  5. Read a ps aux row aloud: what is TIME actually measuring, and what does TTY ? signify? (B1, D3)
  6. top header triage: name the four questions its first lines answer, and how %Cpu wa and load-vs-nproc each redirect the investigation. (B2)
  7. Why does pkill ssh on a remote server risk disaster, and what two habits prevent it? (B3)
  8. The kill escalation ladder in full, and the specific harm kill -9 skips. (C1)
  9. Ctrl+Z vs Ctrl+C: signals, effects, and the scope of the jobs list. (C2)
  10. Why did your job die at SSH disconnect, what does nohup change, and where is nohup the wrong tool? (C3)
  11. Niceness in one sentence including its one crucial precondition; who may lower niceness and who may only raise it? (C4)
  12. Zombie vs orphan: define both via the fork/wait contract, state why you cannot kill a zombie, and give the fix. (D1–D2)

E5. Sources

Interview-question sources used in this module (fetched and quoted verbatim during research, September 2026):

GeeksforGeeks — Linux Interview Questions (70+) (updated July 2026) · Turing — 100+ Linux Interview Questions (2025) · InterviewBit — Linux Interview Questions (2025).

Corpus honesty note: processes are among the best-covered interview topics in print — ps, top, kill, jobs/bg/fg, nice/renice, zombies, orphans, daemons, and PID 1 all appear as dedicated questions, quoted verbatim above. The published corpus is thinner on the mechanism (fork/exec/wait, /proc, D-state, signal catchability) — this module teaches those at interview-relevant depth because they are the follow-up questions the good interviewers ask after the definition.

All documentation links on this page were fetched and confirmed reachable on 2 September 2026.

Next: you can see and steer what runs. Time to learn where programs come fromModule 9 — Package Management.
Spotted a mistake or want something added? Send me a note.