Skip to content
Lesson 13 of 16

Step 1 of 5 · Reading · ~3 min

Read

Production Concerns

Signals and the Terminal

After this lesson your shell handles Ctrl-C and Ctrl-Z the way a real one does: the keystroke reaches the command that is running in the foreground and not the shell, a background job is left alone, and a child never inherits a signal setting that would make it immune to Ctrl-C. You will model the rules that decide what a delivered signal does.

What a signal is

A signal is a small numbered message the kernel delivers to a process. Each process has, for every signal, a disposition that says what to do when one arrives:

  • default: take the signal's default action. For most signals that is to terminate; SIGCHLD defaults to ignoring; SIGTSTP stops the process; SIGCONT resumes a stopped one.
  • ignore: discard it.
  • handler: run a function the program registered. The process is not killed.

Two signals cannot be changed at all: SIGKILL always terminates and SIGSTOP always stops. That is why they are the last resort.

SignalNumberDefault actionTyped as
SIGHUP1terminate
SIGINT2terminateCtrl-C
SIGQUIT3terminateCtrl-\
SIGKILL9terminate, uncatchable
SIGUSR110terminate
SIGPIPE13terminate
SIGTERM15terminate
SIGCHLD17ignore
SIGCONT18continue
SIGSTOP19stop, uncatchable
SIGTSTP20stopCtrl-Z

A process killed by signal n reports status 128 + n, as the previous lesson showed.

Who receives Ctrl-C

The terminal does not know about your shell's jobs. When you press Ctrl-C it sends SIGINT to every process in the terminal's foreground process group. A process group is a set of processes with one group number. The shell gives each job its own group and then tells the terminal which group is in the foreground. Background jobs are in other groups and never see the keystroke.

Two inheritance rules

When a process forks, the child gets a copy of every disposition and the same process group. When a process calls exec, caught signals go back to the default (the handler code no longer exists) but ignored signals stay ignored. The second rule is the trap.

An interactive shell ignores SIGINT and SIGTSTP so that Ctrl-C does not kill the shell itself. If it forks and execs a program without changing that, the program inherits the ignore, and the user can no longer interrupt it. The correct sequence is: fork, then in the child put the dispositions back to default and move into the job's group, then exec.

Delivery rules for this lesson

For a live process, a signal produces one outcome:

  1. SIGKILL: terminated. SIGSTOP: stopped.
  2. SIGCONT: if the process is stopped it continues; otherwise it is caught if it has a handler and has no effect if not.
  3. Any other signal sent to a stopped process stays queued: report pending and change nothing.
  4. Otherwise an ignored signal gives ignored, a handled one gives caught, and the default action gives terminated, stopped or ignored.

Common mistakes

  • Resetting handlers across exec but also resetting ignores, which hides the inheritance bug.
  • Delivering Ctrl-C to every process instead of only the foreground group.
  • Letting SIGKILL or SIGSTOP be ignored or caught.
  • Forgetting that a stopped process cannot act on SIGTERM until it is continued.

Your exercise

The starter reads commands and runs the process table, the terminal and the dispatch. You implement the three rules in the middle: what fork copies, what exec resets, and what a delivered signal does. The most likely mistake is resetting ignored signals on exec.

Up nextHistory ExpansionProduction Concerns

Discussion

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

Sign in to post a comment or reply.

Loading…