LinuxVI · ProcessesSignals
Signals — SIGTERM, SIGKILL, SIGHUP, and the rest
What you'll learn
- Send and receive signals correctly
- Distinguish catchable from uncatchable signals
- Write a signal handler that handles SIGTERM gracefully
- Avoid the common signal-handling bugs
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
Signals are Linux’s asynchronous notification mechanism. A signal is a small integer (1-31 on Linux, with real-time signals above) that the kernel delivers to a process. The process can ignore it, catch it (with a handler), or accept the default action.
The signal list
| Signal | Number | Default action | Catching? |
|---|---|---|---|
| SIGHUP | 1 | Terminate | Yes — many daemons reload config |
| SIGINT | 2 | Terminate | Yes — Ctrl-C handler |
| SIGQUIT | 3 | Core dump | Yes |
| SIGILL | 4 | Core dump | Yes |
| SIGTRAP | 5 | Core dump | Yes |
| SIGABRT | 6 | Core dump | Yes |
| SIGBUS | 7 | Core dump | Yes |
| SIGFPE | 8 | Core dump | Yes |
| SIGKILL | 9 | Terminate (immediately) | NO — uncatchable |
| SIGUSR1 | 10 | Terminate | Yes — application-defined |
| SIGSEGV | 11 | Core dump | Yes |
| SIGUSR2 | 12 | Terminate | Yes — application-defined |
| SIGPIPE | 13 | Terminate | Yes — write to closed pipe |
| SIGALRM | 14 | Terminate | Yes — timer |
| SIGTERM | 15 | Terminate | Yes — graceful shutdown |
| SIGSTKFLT | 16 | Terminate | Yes |
| SIGCHLD | 17 | Ignore | Yes — parent notification of child exit |
| SIGCONT | 18 | Continue | Yes |
| SIGSTOP | 19 | Stop (suspend) | NO — uncatchable |
| SIGTSTP | 20 | Stop | Yes — Ctrl-Z in a terminal |
| SIGTTIN | 21 | Stop | Yes |
| SIGTTOU | 22 | Stop | Yes |
| SIGURG | 23 | Ignore | Yes |
| SIGXCPU | 24 | Core dump | Yes |
| SIGXFSZ | 25 | Core dump | Yes |
| SIGVTALRM | 26 | Terminate | Yes |
| SIGPROF | 27 | Terminate | Yes |
| SIGWINCH | 28 | Ignore | Yes — terminal window resize |
| SIGIO/SIGPOLL | 29 | Terminate | Yes |
| SIGPWR | 30 | Terminate | Yes |
| SIGSYS | 31 | Core dump | Yes |
$ kill -l 2>&1 | head -5 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTPIllustrative output
Sending signals
$ kill 1234; kill -TERM 1234; kill -KILL 1234; kill -HUP $(pgrep nginx)...Illustrative output
$ pkill -TERM -f nginx; killall -HUP sshd...Illustrative output
Catching signals
A process catches a signal by registering a handler via signal()
or sigaction(). When the signal arrives, the kernel interrupts
the process’s normal flow and calls the handler.
$ cat /tmp/sigterm-demo.sh#!/bin/bash
LOCK=/tmp/sigterm-demo.lock
cleanup() {
echo "received SIGTERM, cleaning up"
rm -f "$LOCK"
exit 0
}
trap cleanup TERM
trap 'echo "received SIGINT, exiting"; rm -f "$LOCK"; exit 130' INT
echo "PID: $$"
touch "$LOCK"
echo "waiting..."
while true; do
sleep 1 & wait $!
doneIllustrative output
Two details in that script are the whole lesson.
The trap argument is a single word or a quoted string.
trap takes the handler as one argument. Writing
trap echo cleaning up; rm -f "$LOCK"; exit 0 TERM
does not install that pipeline as a handler. Bash reads
trap echo cleaning up as the trap command, then runs the
rest immediately. The safe forms are a function name, as
with trap cleanup TERM, or a single-quoted string, as with
the SIGINT line. A function name is easier to read and easier
to test, because you can call cleanup directly.
A foreground sleep delays the handler. Bash does not
interrupt a foreground child to run a trap. It waits for the
child to exit, then runs the handler. With a plain
sleep 1 the delay is invisible; with sleep 300 in a
polling loop the process appears to ignore SIGTERM for five
minutes, systemd hits TimeoutStopSec, and SIGKILL arrives
with the cleanup never having run. Backgrounding the sleep
and calling wait fixes it: wait is interruptible, so
the handler fires immediately.
$ ./sigterm-demo.sh & echo $!; sleep 5; kill -TERM $!; wait $!PID: 12345
waiting...
received SIGTERM, cleaning up
Illustrative output
Catching vs blocking vs ignoring
Three options when a signal arrives:
| Option | Effect |
|---|---|
| Default | The kernel applies the signal’s default action (terminate, core dump, ignore, stop, continue) |
Ignore (SIG_IGN) | The kernel discards the signal; the process never knows |
| Handler | The kernel runs a function the process registered; on return, the process resumes |
$ trap '' TERM; sleep 60...Illustrative output
Real-time signals
Linux supports real-time signals from SIGRTMIN (typically 32) to SIGRTMAX. Real-time signals:
- Carry a
sigval_tpayload (an integer or a pointer). - Are queued, not coalesced — multiple instances of the same signal are delivered separately.
- Have a defined delivery order (lower-numbered first; for the same number, by sender’s PID).
$ kill -RTMIN+3 1234...Illustrative output
Production discipline
| Practice | Why |
|---|---|
| Every daemon handles SIGTERM by cleaning up and exiting. | systemd escalates to SIGKILL after TimeoutStopSec. A handler that does not exit in time triggers SIGKILL. |
| SIGHUP reloads configuration. | Convention; nginx, sshd, rsyslog, and most daemons follow it. |
| SIGUSR1 / SIGUSR2 are application-specific. | “Rotate logs”, “dump state”, “reopen file”; document what each means. |
| Never block SIGKILL or SIGSTOP. | The kernel enforces this regardless. Trying to do so is a bug. |
| Always test signal handlers. | Many production daemons have been killed by SIGKILL because their SIGTERM handler never returned. |
Knowledge check
Knowledge check · 5 questions
Q1. Which of the following signals cannot be caught, blocked, or ignored?
Q2. Real-time signals (SIGRTMIN+) carry a payload and are queued individually.
Q3. Which of the following are correct signal-handling practices for production daemons? Select all that apply.
Q4. A bash daemon installs a SIGTERM trap that removes its lock file, then loops on a foreground `sleep 300`. On `systemctl stop` the unit takes the full TimeoutStopSec, is SIGKILLed, and the lock file is left behind. What is wrong?
Q5. Writing `trap cleanup-and-exit TERM`, where cleanup-and-exit is meant as a description of what to do, installs a working handler.
Passing score: 75%. Answers are checked in this browser.