Skip to content
Job Control: &, jobs, fg, bg
step 1/5

Reading — step 1 of 5

Read

~2 min readProduction Concerns

Job Control

Job control lets one shell session manage multiple "jobs" — pipelines backgrounded with & or suspended with Ctrl-Z (SIGTSTP).

$ sleep 30 &              # background; shell prints "[1] 12345"
[1] 12345
$ make build &
[2] 12346
$ jobs
[1]-  Running                 sleep 30 &
[2]+  Running                 make build &
$ fg %1                   # bring job 1 to foreground; shell waits
sleep 30
^Z                        # SIGTSTP from kernel via terminal
[1]+  Stopped                 sleep 30
$ bg %1                   # resume job 1 in background
[1]+ sleep 30 &

Three states a job can be in

StateHow you got thereHow to leave
ForegroundStarted normally (no &)Exits, or you press Ctrl-Z (-> Stopped)
Background-runningStarted with &, or bg after Ctrl-ZExits, or fg brings to fg
StoppedCtrl-Z while in fg, or SIGSTOPbg (-> background), fg (-> foreground), or kill -CONT

Process groups and the controlling terminal

This is where it gets real:

  1. Each pipeline you start becomes one process group (pgid = pid of the group leader). setpgid(0, 0) in the first child creates the group; the shell then setpgid(pid, pgid) for each sibling.
  2. The terminal has one "foreground process group" at a time. The shell sets it with tcsetpgrp(tty_fd, pgid). Only that group is allowed to read from the terminal — others get SIGTTIN.
  3. When you press Ctrl-Z, the kernel sends SIGTSTP to the entire foreground process group. Default handler stops every process.
  4. When the foreground job exits or stops, the shell calls tcsetpgrp(tty_fd, shell_pgid) to put itself back in charge.

Signal cheat sheet

SignalDefaultWho sends itWhat it means
SIGINT (2)TerminateCtrl-C in fg"Politely interrupt me"
SIGQUIT (3)Terminate + coreCtrl-\ in fg"Crash with core dump"
SIGTSTP (20)StopCtrl-Z in fg"Suspend me"
SIGCONT (18)Continuekill -CONT, fg, bg"Resume"
SIGTTIN/SIGTTOUStopKernel"Background tried to use the terminal"

bg works by sending SIGCONT to the stopped job (which restarts it) without putting it back in the foreground pgid. fg sends SIGCONT AND calls tcsetpgrp to make it the foreground job again.

Why jobs aren't just background processes

Without job control:

  • Ctrl-Z does nothing useful (the kernel sends SIGTSTP, but bash never takes back the terminal).
  • Background processes that touch the terminal are killed by SIGTTIN with no warning.
  • You can't have an editor in fg, suspend it, run a test, and resume.

Job control is what makes the shell feel like a window manager.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…