ipc · medium
IPC: Signals & sigaction — Async Notifications, Masks, and Handler Safety
POSIX signals are asynchronous notifications from the kernel or other processes. Each signal has a number, a default disposition (terminate/ignore/stop/core), and can be blocked or caught. SIGKILL (9) and SIGSTOP cannot be caught, blocked, or ignored — kernel handles them directly. Always use sigaction() over signal(): sigaction is persistent, supports sa_mask (block signals during handler), and SA_RESTART (auto-retry interrupted slow syscalls, avoiding EINTR handling). Handlers must use only async-signal-safe functions (write(), not printf()). Communicate handler results to main thread via volatile sig_atomic_t. Block signals during critical sections with sigprocmask(). Server idioms: SIG_IGN SIGPIPE (broken connections), SIGCHLD→waitpid() (reap zombies), SIGHUP→reload-config (daemon convention).
Signals: async kernel notifications; use sigaction() not signal(); SIGKILL/SIGSTOP are uncatchable; sa_mask blocks signals during handler; SA_RESTART resumes interrupted syscalls; only async-signal-safe functions in handlers; use volatile sig_atomic_t for handler→main communication.
The code
// POSIX signals — async notification between kernel/processes#include <signal.h>
// Custom handler — ONLY async-signal-safe functions!volatile sig_atomic_t g_shutdown = 0;void sigint_handler(int signum) { g_shutdown = 1; // safe: sig_atomic_t assignment // write() is async-signal-safe; printf() is NOT}
int main() { // Always use sigaction, not signal() struct sigaction sa{}; sa.sa_handler = sigint_handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGTERM); // block during handler sa.sa_flags = SA_RESTART; // restart slow syscalls sigaction(SIGINT, &sa, nullptr);
// Graceful shutdown loop while (!g_shutdown) { do_work(); // interrupted by signal, SA_RESTART resumes } cleanup(); // guaranteed to run return 0;}
// Block signals during critical sectionsigset_t block_set;sigemptyset(&block_set);sigaddset(&block_set, SIGINT);sigaddset(&block_set, SIGTERM);sigprocmask(SIG_BLOCK, &block_set, &old_set);// critical section — signals deferred to pending setsigprocmask(SIG_SETMASK, &old_set, nullptr); // unblock
// Self-signal: raise(SIGALRM) — same as kill(getpid(), SIGALRM)// Child death: signal(SIGCHLD, SIG_IGN) to auto-reap// Ignore broken pipe: signal(SIGPIPE, SIG_IGN) in serverWhat this lesson walks through
- 01Signal table — common signals and their defaults
- 02SIGINT delivered — kernel interrupts the process
- 03sigaction() — always prefer over signal()
- 04Signal mask — blocking signals during critical sections
- 05SIGKILL — cannot be caught, blocked, or ignored
- 06Server idioms: SIGPIPE, SIGCHLD, SIGHUP
Each signal has a number, a default disposition (terminate, ignore, stop, core-dump), and a catchable flag. SIGKILL (9) and SIGSTOP are special: they cannot be caught or ignored — the kernel handles them unconditionally. All others can be redirected to a custom handler or ignored.
See it animated — step by step, at your own pace
Unlock the full interactive walkthrough of IPC: Signals & sigaction — Async Notifications, Masks, and Handler Safety and 100+ animated C++ interview lessons.