Linux System Programming · intermediate · ~12 min
Pick the right API for installing a signal handler.
sigaction() supersedes signal(). It takes a struct with explicit flags so you opt into SA_RESTART, SA_SIGINFO, etc. signal() has portability footguns; never use it in new code.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
/* ... */
};
The legacy way to catch a signal is signal(signo, handler). It works, but it has a footgun: on some old Unixes the handler is reset to the default after firing once. You'd then have to re-install it inside the handler — a race window where a second delivery escapes.
POSIX standardises a safer replacement: sigaction(). It takes a struct sigaction with explicit flags so you control behaviour precisely. Three flags you'll meet:
SA_RESTART — make blocking syscalls (read, accept, …) automatically restart instead of returning EINTR.SA_NOCLDSTOP — for SIGCHLD, don't deliver on stop/continue, only on exit.SA_SIGINFO — use the 3-arg sa_sigaction form to receive siginfo_t (signal sender pid, fault address, …).Rule: never use signal() in new code. Always sigaction().
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static volatile sig_atomic_t got_int = 0;
static void on_int(int sig) { (void)sig; got_int = 1; }
int main(void) {
struct sigaction sa = {0};
sa.sa_handler = on_int;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* don't break our read()s */
if (sigaction(SIGINT, &sa, NULL) != 0) { perror("sigaction"); return 1; }
while (!got_int) { pause(); }
puts("caught SIGINT");
return 0;
}
sigemptyset(&sa.sa_mask) — the mask field is not zero-initialised reliably across libc versions.Replace a signal() call in some legacy code you find online with an equivalent sigaction() call. Then add SA_RESTART and observe that your blocking read no longer returns EINTR.
sigaction() is the POSIX-standard, portable, race-free way to install a signal handler. Always set sa_mask via sigemptyset() before passing the struct.