Ch20.4 โ Signal Mask & Blocking
Linux System Programming ยท EmbeddedPathashala
๐ก Topic 4 of 19
๐ฏ Intermediate
๐ป 3 Code Examples
โ Interview Q&A
๐ Key Terms
Signal Mask sigprocmask() SIG_BLOCK SIG_UNBLOCK SIG_SETMASK Critical Section SIGKILL SIGSTOP
What is the Signal Mask?
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().
๐ง sigprocmask() โ Three Operations
| 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 |
Note: SIGKILL and SIGSTOP can never be added to the signal mask. If you try, sigprocmask() silently ignores those signals with no error.
๐ป Code Example 1 โ Block SIGINT During a Critical Section
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;
}
๐ป Code Example 2 โ Block Multiple Signals at Once
/* 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;
}
๐ป Code Example 3 โ Block ALL Signals (except SIGKILL/SIGSTOP)
/* 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;
}
Key rule: SIGKILL and SIGSTOP can NEVER be blocked, ignored, or caught. They are the “sure kill” and “sure stop” signals โ the kernel always handles them directly.
โ Interview Questions
Q1. What is the signal mask of a process?
The signal mask is a set of signals whose delivery is currently blocked for a process. When a signal is in the mask, the kernel will not deliver it even if it is generated โ the signal becomes pending instead.
Q2. What are the three operations of sigprocmask() and what does each do?
SIG_BLOCK: adds signals to the current mask (union). SIG_UNBLOCK: removes signals from the mask. SIG_SETMASK: replaces the entire mask with the new set.
Q3. Why would you block a signal before a critical section of code?
To prevent the signal handler from interrupting code that must run atomically โ for example, code modifying shared data structures. Without blocking, the handler could run in the middle and leave data in an inconsistent state.
Q4. Which two signals can never be blocked? Why?
SIGKILL and SIGSTOP. They cannot be blocked, ignored, or caught. This is by design โ they give the system administrator (or kernel) an unconditional way to terminate or stop any process, regardless of what the process is doing.
Q5. What happens when you unblock a signal that has been pending?
The pending signal is delivered immediately. SUSv3 specifies that at least one pending signal from the unblocked set will be delivered before sigprocmask() returns.
Q6. How do you safely block a signal and restore the old mask afterward?
Pass the oldset argument to sigprocmask(SIG_BLOCK, …, &prev_mask) to save the old mask. After the critical section, call sigprocmask(SIG_SETMASK, &prev_mask, NULL) to restore it exactly as it was.
Next Topic โ
Signal Dispositions โ What actions can a process take when a signal arrives?
