Skip to content
daniel@MacBookPro:~
Shell & TerminalDeep Dive August 29, 2026 3 min read

Job Control: How Shells Manage Foreground and Background Processes

Ctrl-Z, bg, fg, and the & at the end of a command all touch the same underlying mechanism — a shell feature that manages which processes can read your keyboard input at any given moment.

Job control is the shell feature behind Ctrl-Z, bg, fg, and background execution with & — a coherent system for managing which of potentially several running processes gets to read from your terminal at any given moment, built on process groups and specific Unix signals.

What a “job” actually is

A job is a unit the shell tracks — typically one process, or a pipeline of several processes launched together — that can be running in the foreground (actively connected to your terminal’s input), running in the background (executing but not reading terminal input), or stopped (suspended, not currently running at all).

Starting a background job

long-running-command &

The trailing & starts a command in the background immediately, returning control of the terminal to you right away rather than waiting for the command to finish — the shell prints a job number you can reference in subsequent job-control commands.

Suspending and resuming the foreground job

Ctrl-Z            # suspend the current foreground job
fg                # resume the most recently suspended job, in the foreground
bg                # resume the most recently suspended job, in the background

Ctrl-Z doesn’t stop a process permanently — it sends SIGTSTP, a signal requesting the process suspend itself, which the shell traps and uses to mark that job as stopped rather than terminated.

Listing and referencing specific jobs

jobs
fg %2
kill %1

jobs lists every job the current shell session is tracking, each with a job number; %N syntax lets you reference a specific job by that number in fg, bg, or kill, rather than only ever operating on “the most recent” job.

The actual mechanism underneath: process groups

Every job’s processes are placed into a shared process group by the shell — the kernel tracks which process group currently has control of the terminal (the “foreground process group”), and signals like the one generated by Ctrl-C (SIGINT) are delivered to every process in that foreground group at once, not to any single process individually.

Why background jobs don’t receive your Ctrl-C

Because Ctrl-C sends SIGINT specifically to the terminal’s current foreground process group, and a backgrounded job isn’t part of that foreground group, pressing Ctrl-C while a job runs in the background has no effect on it at all — this is a direct, logical consequence of the process-group mechanism, not a separate special case job control has to account for.

What happens to background jobs when you close the terminal

By default, closing a terminal sends SIGHUP (“hangup”) to jobs still attached to that session, which typically terminates them — this is exactly the problem tools like nohup, disown, and terminal multiplexers like tmux solve, each in a different way, by detaching a job’s fate from the specific terminal session that originally launched it.

Why understanding process groups clarifies job control entirely

Job control can feel like a grab-bag of unrelated commands (bg, fg, jobs, Ctrl-Z) until you see they’re all just different ways of manipulating one underlying mechanism — which process group currently owns foreground terminal access, and which processes a given signal actually gets delivered to as a result.