Asynchronous notifications: generation/pending/blocked/delivery, the signal catalogue and the uncatchable SIGKILL/SIGSTOP, standard (unqueued) vs real-time (queued) signals, sigaction/SA_RESTART/EINTR, async-signal-safety & the self-pipe/signalfd pattern, and signals vs threads.
Signals are the kernel's asynchronous notifications to a process — the software-interrupt layer of Unix. Simple to send, subtle to handle correctly. Original, interview-focused notes.
A signal is generated (by the kernel, another process, or the process itself), pending until it can be delivered, and delivered when the target next returns to user mode — running its default action or a registered handler. Two per-process bitmaps drive it: pending and blocked (the mask).
| Signal | Default | Note |
|---|---|---|
SIGTERM (15) | Terminate | Polite shutdown — catchable, the default kill. |
SIGKILL (9) | Terminate | Uncatchable, unblockable — the kernel acts, the process never sees it. |
SIGSTOP / SIGCONT | Stop / continue | STOP is also uncatchable. |
SIGSEGV / SIGBUS | Core dump | Bad memory access — a synchronous fault turned into a signal. |
SIGCHLD | Ignored | A child stopped/exited — the reaping hook. |
SIGHUP (1) | Terminate | Terminal hangup; by convention "reload config". |
SIGPIPE | Terminate | Wrote to a closed socket/pipe. |
SIGUSR1/2 | Terminate | Application-defined. |
SIGKILL and SIGSTOP can be neither caught, blocked, nor ignored — the one guarantee that lets you always stop a (schedulable) process.
SIGRTMIN…SIGRTMAX) are queued,
delivered in order, and can carry a payload (sigqueue with siginfo). Use these when you
need reliable, counted, or data-bearing signals.sigaction() registers a handler (prefer it over the older signal()).
Flags matter: SA_RESTART auto-restarts interrupted syscalls; SA_SIGINFO gives the
handler siginfo context.sigprocmask() (per-thread) blocks signals during a critical section;
they stay pending and deliver when unblocked.-EINTR unless
SA_RESTART is set — you must retry. A perennial bug source.malloc, no printf. The safe pattern is the
self-pipe / signalfd: the handler just sets a flag or writes a byte; the real work
happens in the main loop.sighand), but the mask is
per-thread.sigwait() them.signalfd / pidfd_send_signal turn signals into file-descriptor events — far easier to
integrate into an event loop than handlers.sigaction preferred over signal?