a signal handler and signal disposition
When a signal arrives, the process has to do something with it — but what? That decision is the signal's disposition, and there are exactly three choices for each signal. The default action: let the kernel do whatever that signal normally does (often terminate). Ignore it: throw it away, as if it never came. Or catch it: run a small function of your own, called a signal handler, the moment the signal lands. The disposition is a per-process setting you choose ahead of time, one per signal.
You set it with signal() or, more robustly, sigaction(). For example, to make Ctrl-C run cleanup instead of killing you, you install a handler for SIGINT: sigaction(SIGINT, &act, NULL) where act.sa_handler points at your function. From then on, when SIGINT arrives, the kernel pauses your normal code, jumps into your handler, runs it, and returns to exactly where it interrupted you. To ignore a signal you set its disposition to SIG_IGN; to restore normal behavior you set SIG_DFL. A common pattern: catch SIGTERM to flush buffers and close files, then exit — so the program shuts down cleanly when asked, rather than dropping data.
The catch — and it is a serious one — is that a handler runs in a borrowed moment, interrupting whatever your program was doing, possibly in the middle of malloc() or printf(). So a handler may only safely call a short list of 'async-signal-safe' functions, and the cleanest pattern is for the handler to do almost nothing: set a single volatile sig_atomic_t flag and return, letting your main loop notice the flag and do the real work later. Two dispositions can never be changed: SIGKILL and SIGSTOP always take their default action, so no handler you write will ever run for them.
volatile sig_atomic_t stop = 0; void on_term(int sig) { stop = 1; } /* tiny, safe handler */ /* in setup: */ struct sigaction a = {0}; a.sa_handler = on_term; sigaction(SIGTERM, &a, NULL); /* main loop checks: */ while (!stop) { do_work(); }
Best practice: the handler just sets a flag; the main loop does the real shutdown work safely.
Prefer sigaction() over the older signal(), whose behavior is historically inconsistent across systems. And never assume a handler runs to completion uninterrupted — another signal can arrive while it is running.