open atlas
↑ Back to track
Command line CLI · 07 · 02

Signals and kill

Signals are asynchronous notifications the kernel delivers to processes. SIGTERM (15) asks politely and can be caught; SIGKILL (9) cannot be caught or ignored and skips all cleanup. Reach for SIGTERM first — SIGKILL is the last resort.

CLI Middle ◷ 22 min
Level
FoundationsJuniorMiddleSenior

A signal is not a message with a payload — it is a tap on the shoulder from the kernel. The kernel queues a small integer at the process’s door, and the process either handles it with a registered function, ignores it, or lets the default action run. Most defaults are “terminate.” The four signals you will use daily are SIGTERM (15), SIGKILL (9), SIGINT (2), and SIGHUP (1). They look similar but behave very differently, and choosing the wrong one costs you data integrity, open connections, and clean log entries.

Goal

After this lesson you can send SIGTERM, SIGKILL, SIGINT, and SIGHUP by name and number using kill, pkill, and killall; explain why SIGKILL cannot be caught; describe what a signal handler does; and know when each of the four signals is appropriate.

1

Signals are integers. Each has a default action and can be overridden. The kernel delivers a signal by setting a pending-signal bit in the process’s task structure. On the next trip through the kernel scheduler, the pending signal is checked and dispatched.

A process can register a signal handler — a function that runs instead of the default action. It can also ignore a signal (SIG_IGN). The exceptions are SIGKILL (9) and SIGSTOP (19/17): these can never be caught, blocked, or ignored. The kernel always handles them directly.

Key signals and their defaults:

SignalNumberDefaultCan catch?Typical use
SIGHUP1TerminateYesReload config
SIGINT2TerminateYesCtrl-C
SIGQUIT3Core dumpYesCtrl-\
SIGKILL9TerminateNoForce kill
SIGTERM15TerminateYesGraceful stop
SIGSTOP19/17StopNoPause process
SIGTSTP20/18StopYesCtrl-Z
SIGCONT18/19ContinueYesResume stopped
2

kill PID sends SIGTERM (15) — the polite shutdown request. Despite the name, kill does not forcibly terminate anything by default. It sends SIGTERM, which the process can catch to run cleanup: flush write buffers, close database connections, finish in-flight requests, write a PID file tombstone.

kill 1843          # send SIGTERM to PID 1843
kill -15 1843      # same, explicit signal number
kill -SIGTERM 1843 # same, explicit signal name
kill -TERM 1843    # same, short name

If the process has registered a SIGTERM handler, it runs that function. If not, the default action (terminate) runs immediately. Well-behaved daemons (nginx, postgres, redis) always catch SIGTERM and shut down cleanly.

3

kill -9 PID sends SIGKILL — the unconditional terminate. The kernel delivers SIGKILL directly without ever entering user space. No handler can intercept it. The process gets no opportunity to flush buffers, close connections, or clean up temp files.

kill -9 1843       # SIGKILL — immediate, no cleanup
kill -KILL 1843    # same, by name

When to use SIGKILL: only after SIGTERM has failed and the process is genuinely stuck (blocking in uninterruptible kernel code, ignoring SIGTERM, or already a zombie). The standard sequence:

kill 1843          # SIGTERM — give it time to clean up
sleep 5
kill -0 1843 2>/dev/null && kill -9 1843   # SIGKILL only if still alive

kill -0 does not send a signal — it checks whether the process exists and you have permission to send it signals. Exit code 0 means it still exists.

4

pkill and killall send signals by process name, not PID. Useful when you don’t want to look up the PID first.

pkill nginx         # SIGTERM to all processes named "nginx"
pkill -9 nginx      # SIGKILL to all processes named "nginx"
pkill -u alice      # SIGTERM to all processes owned by user alice
pkill -f 'python train.py'  # match against the full command line

killall nginx       # same as pkill nginx on Linux (BSD behaviour differs)

pkill matches against the process name field (truncated to 15 chars on some kernels). -f matches the entire command line, which is more precise. Always test what pkill -l nginx (or pgrep nginx) matches before sending a signal to avoid surprises.

pgrep nginx         # list PIDs that would be hit — no signal sent
pgrep -l nginx      # list PIDs + names
5

SIGHUP (1) is the reload signal. Originally it meant “the terminal hung up.” Daemons repurpose it: when they receive SIGHUP they re-read their configuration file without restarting. This is how you tell nginx, sshd, or rsyslogd to pick up config changes without dropping connections.

kill -HUP $(pgrep nginx)       # tell nginx to reload config
kill -1 $(cat /var/run/nginx.pid)   # same via PID file

SIGINT (2) is what Ctrl-C sends to the foreground process group. The shell generates it and the kernel delivers it to every process in the foreground group simultaneously. A process that ignores SIGINT will not respond to Ctrl-C.

Worked example

Gracefully stop a stuck web server, falling back to SIGKILL.

# Step 1: find the PID
pgrep -a gunicorn

# Step 2: try SIGTERM
kill $(pgrep gunicorn)

# Step 3: wait and check
sleep 10
pgrep gunicorn && echo "still alive"

# Step 4: if still alive, SIGKILL
kill -9 $(pgrep gunicorn)

In practice, a well-written gunicorn catches SIGTERM, finishes in-flight requests, closes database connection pools, and exits cleanly. If it does not exit within 10–30 seconds (depending on your SLA), then SIGKILL is justified. The key point: never start with SIGKILL.

Why this works

On macOS, signal numbers differ for SIGSTOP and SIGCONT (SIGSTOP is 17, SIGCONT is 19 on macOS; reversed on Linux). Signal names always work portably. The pkill and pgrep commands are available on macOS but may not accept all the same flags. killall on macOS sends SIGTERM by default but behaves identically to Linux for basic usage.

Common mistake

kill -9 as the first and only tool is the most common signal mistake. SIGKILL skips graceful shutdown: open files may be left in an inconsistent state, database write-ahead logs may be left uncommitted, network connections are reset rather than closed gracefully (TCP FIN is never sent), and temp files accumulate. Always give a process a chance to clean up with SIGTERM first. If your service routinely requires SIGKILL to stop, that is a bug in the service — it is not handling SIGTERM.

Check yourself
Quiz

Why can't a process catch or ignore SIGKILL?

Recap

Signals are integer notifications delivered by the kernel. SIGTERM (15) is the polite shutdown request — it can be caught, allowing graceful cleanup. SIGKILL (9) cannot be caught or ignored; the kernel terminates the process immediately with no cleanup. SIGINT (2) is what Ctrl-C sends to the foreground process group. SIGHUP (1) tells daemons to reload their configuration. Use kill PID for SIGTERM, kill -9 PID for SIGKILL; pkill NAME targets by process name; pgrep NAME shows which PIDs would be hit without sending anything. Always try SIGTERM first.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.