Linux System Programming · intermediate · ~12 min
Learn which functions are safe to call from inside a signal handler, and understand why common functions like `printf` are not on that list.
A signal handler can run at any moment. It interrupts your normal code at an arbitrary point.
That point might be in the middle of:
malloc() — allocating memoryprintf() — writing buffered outputpthread_mutex_lock() — holding a lockIf your handler then calls one of those same functions, you can hit serious problems:
The Linux manual page signal-safety(7) lists the async-signal-safe functions. These are the functions you are allowed to call from a handler. "Async-signal-safe" means a function is safe to call even when it interrupts other code.
The main safe functions include:
write, read_exitkill, signal, sigaction, sigprocmaskFunctions that are not safe include:
printf and anything else that uses stdio buffersmalloc, free, and anything that allocates memoryKeep the handler tiny. Let the main program do the real work.
Use a volatile sig_atomic_t for the flag. This is the only type the C standard guarantees can be read and written atomically with respect to signal delivery, so the value is never seen half-updated.
#include <signal.h>
#include <unistd.h>
#include <string.h>
static volatile sig_atomic_t want_quit = 0;
static void on_term(int sig) {
(void)sig;
want_quit = 1;
/* write() IS async-signal-safe; printf() is not. */
const char msg[] = "got SIGTERM, will exit soon\n";
write(STDERR_FILENO, msg, sizeof msg - 1);
}
int main(void) {
struct sigaction sa = {0};
sa.sa_handler = on_term;
sigemptyset(&sa.sa_mask);
sigaction(SIGTERM, &sa, NULL);
while (!want_quit) { sleep(1); }
/* All the real cleanup happens out here. */
return 0;
}
printf, fprintf, puts, or sprintf from a handler. None of these are async-signal-safe.malloc or free, or anything that allocates. This includes most logging libraries.volatile sig_atomic_t for handler flags.write, _exit, kill, and sigaction.printf, malloc, and free are not safe inside a handler.volatile sig_atomic_t flag in the handler, then react to it in the main loop.