Skip to content

Signals and Signal Handlers

First PublishedByAtif Alam

Signals are a form of inter-process communication (IPC): the kernel or another process can send a signal to a process to notify it of an event (e.g. terminal interrupt, child exited, segmentation fault). Each signal has a default action (terminate, ignore, core dump, etc.); a process can ignore the signal, use the default, or install a custom handler. SIGKILL (9) and SIGSTOP (19) cannot be caught or ignored — the kernel enforces them. When a child terminates, the kernel sends SIGCHLD to the parent; the parent should call wait() or waitpid() to reap the child and avoid zombies (see Process Lifecycle).

The default signal sent by kill <pid> is SIGTERM (15) — a request to terminate gracefully. To send another signal: kill -9 <pid> (SIGKILL) or kill -l to list signal names/numbers.

  1. Signal is sent — Via kill(), killpg(), or by the kernel (e.g. SIGSEGV on invalid access, SIGCHLD when child exits).
  2. Kernel records it — Pending signals are stored per process. If the same signal is sent again before delivery, it may be merged (standard signals are not queued).
  3. Process is interrupted — When the process returns to user mode from the kernel (syscall return, timer interrupt, etc.), the kernel checks for pending signals.
  4. Delivery — The kernel invokes the process’s action for that signal: ignore (discard), default (e.g. terminate, core dump), or handler (call the user-registered function). Delivery happens at a “safe” point (e.g. when the process is about to return to user space).
  5. Handler runs — If a custom handler is installed, it runs in the process’s context; when it returns, execution resumes (unless the handler longjmp’s or exits).
ToolPurpose
kill, killall, pkillSend signals to processes.
trapShell builtin to set signal handlers in scripts.
straceTrace syscalls and signal delivery (strace -e signal).
ps, top, htopFind PIDs; show state.
/proc/<pid>/statusSigBlk, SigIgn, SigCgt (blocked, ignored, caught).
gdbAttach and handle signals (e.g. handle SIGINT print).
dmesgKernel messages (e.g. after crash).
  • List signals — Use kill -l to list signal names (and numbers on some systems).
  • Send signalskill <pid> (SIGTERM), kill -9 <pid> (SIGKILL), killall -9 name, pkill -9 -f pattern.
  • Shelltrap 'echo Caught; exit 1' SIGINT to handle Ctrl+C in a script.
  • Process signal maskscat /proc/<pid>/status and check SigBlk, SigIgn, SigCgt.
  • Trace signalsstrace -e signal -p <pid> to see signals received.
  • Crashes — Use gdb to attach or run the program; inspect core dumps or handle signals.

When you cannot modify the program, you can still observe or influence signals:

  1. Wrapper script — Run the program in a script that sets trap and then execs the binary. The wrapper can log or forward signals (though SIGKILL/SIGSTOP cannot be caught).
  2. gdb — Attach with gdb -p <pid>. Use handle SIGINT print stop to intercept and print (or pass to the program). Useful for debugging crashes.
  3. stracestrace -e trace=signal -p <pid> shows when signals are delivered. Does not change behavior, only observes.
  4. LD_PRELOAD — Load a shared library that overrides signal() or sigaction() and installs your own handler before calling the original. Advanced; use with care.
SignalNumberPurposeDefault actionCatchable?
SIGINT2Interrupt (e.g. Ctrl+C)TerminateYes
SIGQUIT3Quit (e.g. Ctrl+\)Core dump + terminateYes
SIGTERM15Request graceful shutdownTerminateYes
SIGKILL9Force killTerminateNo
SIGSTOP19Stop process (suspend)StopNo
SIGCHLD17Child terminated/stoppedIgnoreYes
SIGSEGV11Invalid memory accessCore dump + terminateYes (for debugging)

SIGCHLD — Sent to the parent when a child changes state (exits, stops, continues). The parent should call wait() or waitpid() to reap the child and free its exit status; otherwise the child can remain a zombie (see Process Lifecycle).

SignalNumberPurposeDefaultCatchable?
SIGINT2InterruptTerminateYes
SIGTERM15Graceful shutdownTerminateYes
SIGKILL9Force killTerminateNo
SIGCHLD17Child state changeIgnoreYes
SIGSEGV11Invalid memory accessCore + terminateYes