Signal Mask and Blocking in Linux Explained
Signal Mask and Blocking in Linux Explained
Linux System Programming · EmbeddedPathashala
Every Linux process has a signal mask — a set of signals whose delivery is currently blocked. When a signal is in the mask, the kernel will not deliver it, even if the signal is generated. The signal just waits as pending.
Think of the signal mask like a “do not disturb” list. Signals on the list are held at the door until you remove them from the list.
The signal mask is a per-process (and in multithreaded programs, per-thread) property. You manipulate it with sigprocmask().
| how value | Effect on Signal Mask |
|---|---|
| SIG_BLOCK | ADD the signals in set to the current mask (union) |
| SIG_UNBLOCK | REMOVE the signals in set from the current mask |
| SIG_SETMASK | REPLACE the entire mask with set |
The classic use case: block a signal before entering code that must not be interrupted, then restore the old mask after.
/* Block SIGINT during a critical section
Compile: gcc -o mask_demo mask_demo.c
Run: ./mask_demo — try Ctrl+C during the "critical work" */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
sigset_t block_set, prev_mask;
/* Build a set containing only SIGINT */
sigemptyset(&block_set);
sigaddset(&block_set, SIGINT);
/* Block SIGINT; save previous mask in prev_mask */
sigprocmask(SIG_BLOCK, &block_set, &prev_mask);
printf("Entering critical section (SIGINT blocked)...\n");
printf("Press Ctrl+C — it will be ignored until we exit.\n");
/* === CRITICAL SECTION — signal cannot interrupt here === */
int i;
for (i = 0; i < 5; i++) {
printf("Critical work step %d\n", i + 1);
sleep(1);
}
/* === END CRITICAL SECTION === */
printf("Exiting critical section. Restoring signal mask.\n");
/* Restore the old mask — if SIGINT was pending it fires NOW */
sigprocmask(SIG_SETMASK, &prev_mask, NULL);
printf("Signal mask restored. Normal operation.\n");
sleep(3);
return 0;
}
/* Block both SIGINT and SIGTERM simultaneously
Compile: gcc -o multi_block multi_block.c */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, SIGINT); /* Ctrl+C */
sigaddset(&block_set, SIGTERM); /* kill command default */
sigaddset(&block_set, SIGHUP); /* terminal hangup */
sigprocmask(SIG_BLOCK, &block_set, NULL);
printf("SIGINT, SIGTERM, SIGHUP are all blocked.\n");
printf("Try: kill -SIGTERM %d from another terminal.\n", getpid());
printf("Sleeping 8 seconds...\n");
sleep(8);
/* Unblock all three */
sigprocmask(SIG_UNBLOCK, &block_set, NULL);
printf("Signals unblocked. Pending ones fire now.\n");
sleep(2);
return 0;
}
/* Block every blockable signal using sigfillset()
SIGKILL and SIGSTOP cannot be blocked — kernel ignores
attempts to block them silently.
Compile: gcc -o block_all block_all.c */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
sigset_t full_set;
/* sigfillset fills set with ALL signals */
sigfillset(&full_set);
/* Attempt to block all — SIGKILL and SIGSTOP are ignored */
sigprocmask(SIG_BLOCK, &full_set, NULL);
printf("All blockable signals are now blocked.\n");
printf("Ctrl+C (SIGINT) won't work. SIGTERM won't work.\n");
printf("But kill -9 (SIGKILL) WILL still kill this process!\n");
printf("Try: kill -9 %d\n", getpid());
sleep(10);
printf("Done. Unblocking all signals.\n");
sigprocmask(SIG_UNBLOCK, &full_set, NULL);
return 0;
}
Signal Dispositions – What actions can a process take when a signal arrives?
