Processes & Job Control: Running, Backgrounding & Signaling
- Explain what a process is — a running program with a PID, a parent, and its own memory — and list them with ps
- Run a command in the background with &, list shell jobs with jobs, and move work between foreground and background with fg, bg, and Ctrl-Z
- Send signals to control a process: Ctrl-C (SIGINT), kill (SIGTERM), and kill -9 (SIGKILL), and target by PID or %job
- Connect a process's fate to its exit status: 130 from Ctrl-C, 137 from a forced kill
A process is a program that's actually running
See that every command you run becomes a process with an identity the system tracks.
A program is a file on disk (
/usr/bin/sleep). A process is what exists while that program is running — the live thing, with its own slice of memory, its own open files, and an identity the kernel tracks. Every command you type becomes a process, if only for an instant.Each process has:
- a PID (process ID) — a unique number naming it while it runs,
- a parent (the PPID) — the process that started it; when you launch a command, your shell is the parent,
- and its own memory and file descriptors (that's why one crashing program doesn't take the others down).
The tool for seeing processes is `ps`. Job control — running things in the background, suspending them, signaling them — is just you managing these processes from your shell instead of watching one run to completion.
ps -o pid,ppid,stat,cmd --no-headers $$4123 4108 Ss -bash
- -o pid,ppid,stat,cmd
- choose exactly which columns to show
- $$
- the shell's own PID (a handy self-reference)
$$ is your shell's own PID, so this asks ps to describe the shell you're typing in: its PID (4123), its parent (4108, the terminal/login process that started it), a state code, and the command. Every command you run will have THIS shell as its parent. Foreground, background, and the & operator
Run work in the background and list your shell's jobs.
By default a command runs in the foreground: the shell starts it and waits, handing it your keyboard until it exits. Add `&` at the end and the command runs in the background instead — the shell starts it and immediately gives you the prompt back, so you can keep working.
Background processes started from a shell are its jobs, numbered
%1,%2, … The `jobs` builtin lists them with their state (Running / Stopped). This job number is a convenient handle — you canfg %1,kill %1, and so on without looking up a PID.When you do need the PID (to write it to a file, or to stop the job from a script), the shell has already saved it: right after
command &, the special variable `$!` holds the PID of the job you just backgrounded, soecho $!prints it.sleep 300 &[1] 4210
sleep 300 would normally freeze your terminal for five minutes. With & it runs in the background: the shell prints [1] (job number 1) and 4210 (its PID) and hands the prompt straight back. The work continues; you're free. jobs -l[1]+ 4210 Running sleep 300 &
- -l
- also show each job's PID
jobs lists this shell's jobs; -l adds the PID. [1] is the job number, + marks the 'current' job (the default target of fg/bg), Running is its state. This is your shell's private view of the work it launched — another shell won't see these jobs. Check yourself
You append & to a command. What does the shell do differently?
Suspend and resume: Ctrl-Z, bg, fg
Move a running job between the foreground and background at will.
What if a command is already running in the foreground and you wish you'd backgrounded it? Press `Ctrl-Z`. That suspends (pauses) the foreground job — it stops running and the prompt returns. From there:
- `bg` resumes the suspended job in the background (it keeps running, you keep the prompt),
- `fg` brings a job back to the foreground (you wait on it again).
Both take an optional job spec:
fg %2,bg %1. With no argument they act on the current job (the one marked+injobs). The cycle Ctrl-Z → bg is the everyday move for "oops, this is taking a while — let me keep working."fg %1sleep 300
Bring job 1 back to the foreground. The shell reprints the command and now waits on it again — your terminal belongs to sleep until it finishes or you suspend it. (Press Ctrl-Z now and you'd suspend it; type bg and it resumes running in the background.) Signals: asking a process to stop (nicely, then not)
Use signals to interrupt, terminate, and force-kill processes, and read the resulting exit status.
You don't reach into a process and stop it directly — you send it a signal, a small standard message the kernel delivers. The ones you'll use constantly:
- `SIGINT` (2) — “please interrupt.” This is what `Ctrl-C` sends to the foreground job. A process that exits from it reports status 130 (128 + 2).
- `SIGTERM` (15) — “please terminate.” The polite default of the
killcommand. The process can catch it and clean up first. - `SIGKILL` (9) — “stop now.” Sent by `kill -9`; it cannot be caught or ignored, so it's the last resort for something stuck. A process killed this way reports status 137 (128 + 9).
So
killisn't really about violence — it just sends a signal (SIGTERM by default). Reach for-9only when a normalkilldoesn't work, because the process gets no chance to clean up.kill %1; echo "signalled job 1"signalled job 1 [1]+ Terminated sleep 300
kill %1 sends SIGTERM (the default) to job 1 by its job number — no PID lookup needed. sleep can't do cleanup but it obeys and the shell reports 'Terminated'. You could equally write kill 4210 to target it by PID. Escalate to kill -9 %1 only if a plain kill is ignored. pgrep -a sleep(no output — nothing to kill)
- -a
- also print each match's full command line, not just the PID
- -f
- match the pattern against the full command line, not only the process name
pgrep finds processes by name and prints their PIDs (-a adds the command line); its companion pkill signals them by name. 'pkill sleep' would SIGTERM every sleep at once. Here there's no output because job 1 is already gone — pgrep exits non-zero when nothing matches, which is itself a useful 'is it running?' test. Add -f to match against the whole command line instead of just the process name: pgrep -f backup.shfinds a script by its path even when the process name is just the interpreter.Check yourself
A process is wedged and ignores a normal kill. Which signal is guaranteed to stop it because it can't be caught?
Now do it — drive a job through its whole life
Background, list, foreground, suspend, and kill a job so the cycle becomes muscle memory.
One continuous exercise. Start a long-running job in the background, watch the shell track it, then take control of it. Do these in order in your live terminal.
sleep 600 & sleep 600 & jobsLaunch two background jobs and list them. You'll see [1] and [2], both Running, with [2] marked + as the current job. One shell is now supervising two processes at once — exactly what job control is for. fg %1Pull job 1 to the foreground so it owns your terminal. Now press Ctrl-Z: the shell says 'Stopped' and the prompt returns — you've suspended it. Type bg and it resumes in the background, back to Running. You just moved one job foreground → suspended → background by hand. kill %1 %2; jobsSend SIGTERM to both jobs and list again — they report Terminated and the job table empties. You started, inspected, moved, and cleanly stopped background work without ever leaving this one shell. That's the whole of everyday job control.
- A process is a running program with a PID, a parent (PPID), and its own memory; your shell is the parent of the commands you launch. ps lists them; $$ is the shell's own PID.
- & runs a command in the background and returns the prompt; jobs lists a shell's background jobs as %1, %2, …
- Ctrl-Z suspends (pauses) the foreground job; bg resumes it in the background; fg brings a job to the foreground. Suspended ≠ backgrounded — bg is what gets a paused job running again.
- Signals control processes: Ctrl-C = SIGINT (exit 130), kill = SIGTERM (polite default), kill -9 = SIGKILL (uncatchable, exit 137). Target by PID or %job; pgrep/pkill work by name; use nohup/tmux to survive shell close.
| You do / press | Effect |
|---|---|
| cmd & | run cmd in the background; prompt returns |
| jobs | list this shell's background/suspended jobs |
| Ctrl-Z | suspend (pause) the foreground job |
| bg %n / fg %n | resume job n in background / foreground |
| Ctrl-C | send SIGINT to the foreground job (exit 130) |
| kill %n / kill PID | send SIGTERM (polite) to a job or PID |
| kill -9 … | send SIGKILL (uncatchable, last resort; exit 137) |
| pgrep -a name / pkill name | find / signal processes by name |
