Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
signal() is the simple, historical API for installing signal handlers, but its behaviour varies across UNIX implementations — especially regarding whether the handler is reset to default after invocation, and whether interrupted system calls are automatically restarted.
sigaction() is the POSIX-preferred API. It gives you full control: you can specify which signals to block during the handler, whether to auto-restart syscalls, retrieve the old disposition without changing it, and more. Always use sigaction() in production code.
#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
/* Returns 0 on success, or -1 on error */
struct sigaction {
void (*sa_handler)(int); /* SIG_DFL, SIG_IGN, or handler address */
sigset_t sa_mask; /* signals to block during handler */
int sa_flags; /* SA_RESTART, SA_NODEFER, etc. */
void (*sa_restorer)(void); /* NOT for application use */
};
| Field | Purpose |
|---|---|
| sa_handler | Handler function, SIG_DFL, or SIG_IGN |
| sa_mask | Extra signals blocked while handler runs (in addition to sig itself) |
| sa_flags | Bitmask of options (SA_RESTART, SA_NODEFER, SA_RESETHAND, SA_SIGINFO…) |
| Flag | Effect |
|---|---|
| SA_RESTART | Automatically restart interrupted system calls (read, write, accept…) |
| SA_NODEFER | Do NOT block the signal during its own handler (allows reentrant delivery) |
| SA_RESETHAND | Reset disposition to SIG_DFL after first delivery (one-shot handler) |
| SA_SIGINFO | Use sa_sigaction instead of sa_handler; handler gets siginfo_t details |
| SA_NOCLDSTOP | For SIGCHLD: don’t send when child is stopped/resumed |
| SA_NOCLDWAIT | For SIGCHLD: don’t create zombies when children terminate |
| SA_ONSTACK | Use alternate signal stack (set up via sigaltstack()) |
/* Install a handler for SIGINT using sigaction()
Compile: gcc -o sa_basic sa_basic.c
Run: ./sa_basic then press Ctrl-C */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void sigint_handler(int sig)
{
/* write() is async-signal-safe; printf() is NOT */
const char msg[] = "\nCaught SIGINT. Press Ctrl-C again to exit.\n";
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
}
int main(void)
{
struct sigaction sa;
sa.sa_handler = sigint_handler;
sigemptyset(&sa.sa_mask); /* no extra signals blocked */
sa.sa_flags = SA_RESTART; /* restart interrupted syscalls */
if (sigaction(SIGINT, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
printf("PID %ld — Waiting. Press Ctrl-C.\n", (long)getpid());
for (;;)
pause(); /* sleep until a signal arrives */
return 0;
}
/* Pass NULL for act to retrieve current disposition only */
#include <stdio.h>
#include <signal.h>
int main(void)
{
struct sigaction old;
/* act = NULL → don't change, just retrieve */
if (sigaction(SIGTERM, NULL, &old) == -1) {
perror("sigaction");
return 1;
}
if (old.sa_handler == SIG_DFL)
printf("SIGTERM disposition: SIG_DFL (default)\n");
else if (old.sa_handler == SIG_IGN)
printf("SIGTERM disposition: SIG_IGN (ignored)\n");
else
printf("SIGTERM disposition: custom handler at %p\n",
(void *)old.sa_handler);
return 0;
}
/* Use sa_mask to block SIGUSR2 during the SIGUSR1 handler.
This prevents SIGUSR2 from interrupting our handler mid-execution. */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void usr1_handler(int sig)
{
printf("In SIGUSR1 handler — SIGUSR2 is blocked now\n");
sleep(3); /* SIGUSR2 sent during this sleep will be pending */
printf("SIGUSR1 handler done — SIGUSR2 will be delivered now\n");
}
static void usr2_handler(int sig)
{
printf("SIGUSR2 delivered\n");
}
int main(void)
{
struct sigaction sa1, sa2;
/* Install SIGUSR2 handler first */
sa2.sa_handler = usr2_handler;
sigemptyset(&sa2.sa_mask);
sa2.sa_flags = 0;
sigaction(SIGUSR2, &sa2, NULL);
/* Install SIGUSR1 handler, blocking SIGUSR2 during it */
sa1.sa_handler = usr1_handler;
sigemptyset(&sa1.sa_mask);
sigaddset(&sa1.sa_mask, SIGUSR2); /* block SIGUSR2 inside handler */
sa1.sa_flags = 0;
sigaction(SIGUSR1, &sa1, NULL);
printf("PID %ld — send SIGUSR1, then SIGUSR2 quickly\n", (long)getpid());
pause();
pause();
return 0;
}
/* SA_RESETHAND: handler fires once, then disposition resets to SIG_DFL.
On the second SIGINT, default action (terminate) occurs. */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void once_handler(int sig)
{
printf("\nFirst Ctrl-C caught. Second will terminate the process.\n");
}
int main(void)
{
struct sigaction sa;
sa.sa_handler = once_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND; /* reset to SIG_DFL after first delivery */
sigaction(SIGINT, &sa, NULL);
printf("PID %ld — press Ctrl-C twice\n", (long)getpid());
for (;;)
pause();
return 0;
}
sigaction(SIGTERM, NULL, &old). Passing NULL for the act argument tells sigaction() to only retrieve (not change) the disposition. The current disposition is written into old.Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
