Ch20.14 โ sigprocmask()
Linux System Programming ยท EmbeddedPathashala
๐ก Topic 14 of 19
๐ฏ Intermediate
๐ป 3 Code Examples
โ Interview Q&A
๐ Key Terms
sigprocmask() SIG_BLOCK SIG_UNBLOCK SIG_SETMASK signal mask critical section oldset SIGKILL SIGSTOP
What is sigprocmask()?
Every process has a signal mask โ a set of signals that are currently blocked from delivery. A blocked signal that is generated does not disappear; it becomes pending and is delivered only after the process removes it from the mask.
sigprocmask() is the system call used to examine and/or change the calling process’s signal mask. It is the standard POSIX mechanism for protecting critical sections of code from being interrupted by specific signals.
๐ Function Prototype
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
/* Returns 0 on success, or -1 on error */
| Parameter | Purpose |
|---|---|
| how | How to change the mask: SIG_BLOCK, SIG_UNBLOCK, or SIG_SETMASK |
| set | Pointer to signal set describing the change. Can be NULL if only retrieving oldset. |
| oldset | Receives the previous signal mask. Can be NULL if not needed. |
โ๏ธ The Three Values of
how| how | Operation | New mask = |
|---|---|---|
| SIG_BLOCK | Add signals in set to current mask | current_mask | set |
| SIG_UNBLOCK | Remove signals in set from current mask | current_mask & ~set |
| SIG_SETMASK | Replace current mask with set entirely | set |
Retrieve only: To get the current mask without changing it, pass
NULL as set โ the value of how is ignored in this case.SIGKILL & SIGSTOP cannot be blocked. If these signals appear in the set passed to sigprocmask(), the request is silently ignored โ no error is returned.
๐ป Code Example 1 โ Block SIGINT, do work, then unblock
/* Protect a critical section from SIGINT
Compile: gcc -o crit_section crit_section.c
Run: ./crit_section (try Ctrl-C during the work) */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
sigset_t block_set, old_set;
/* Build a set containing only SIGINT */
sigemptyset(&block_set);
sigaddset(&block_set, SIGINT);
/* --- Start critical section: block SIGINT, save old mask --- */
if (sigprocmask(SIG_BLOCK, &block_set, &old_set) == -1) {
perror("sigprocmask");
return 1;
}
printf("Critical section: Ctrl-C is blocked now.\n");
printf("Doing important work for 5 seconds...\n");
sleep(5);
printf("Critical section done.\n");
/* --- End critical section: restore previous mask --- */
if (sigprocmask(SIG_SETMASK, &old_set, NULL) == -1) {
perror("sigprocmask restore");
return 1;
}
printf("SIGINT unblocked. Ctrl-C now works again.\n");
pause(); /* wait for a signal */
return 0;
}
Best practice: Always save the old mask with
oldset and restore it with SIG_SETMASK after the critical section. Using SIG_UNBLOCK to reverse a SIG_BLOCK can be fragile if there are nested calls.๐ป Code Example 2 โ Block ALL signals except SIGKILL and SIGSTOP
/* Block every blockable signal using sigfillset
SIGKILL and SIGSTOP are silently ignored */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
sigset_t block_all, old_set;
sigfillset(&block_all); /* all signals */
if (sigprocmask(SIG_BLOCK, &block_all, &old_set) == -1) {
perror("sigprocmask");
return 1;
}
printf("All blockable signals blocked for 5 seconds.\n");
sleep(5);
/* Restore original mask */
sigprocmask(SIG_SETMASK, &old_set, NULL);
printf("Signals unblocked.\n");
return 0;
}
๐ป Code Example 3 โ Read current signal mask without changing it
/* Retrieve and print the current signal mask
Compile: gcc -o show_mask show_mask.c
Run: ./show_mask */
#include <stdio.h>
#include <signal.h>
int main(void)
{
sigset_t current_mask;
int sig;
/* Pass NULL for set to retrieve only */
if (sigprocmask(SIG_BLOCK, NULL, ¤t_mask) == -1) {
perror("sigprocmask");
return 1;
}
printf("Currently blocked signals:\n");
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(¤t_mask, sig))
printf(" Signal %2d (%s)\n", sig, strsignal(sig));
}
printf("(empty if no signals printed)\n");
return 0;
}
๐ Signal Mask Flow Diagram
| Step | Action | Effect on pending signals |
|---|---|---|
| 1 | SIG_BLOCK (add SIGUSR1) | SIGUSR1 now blocked; if sent, becomes pending |
| 2 | Signal SIGUSR1 arrives | Recorded in pending set, NOT delivered yet |
| 3 | SIG_UNBLOCK (remove SIGUSR1) | Pending SIGUSR1 is now delivered immediately |
โ Interview Questions
Q1. What are the three values for the
how argument of sigprocmask(), and what does each do?SIG_BLOCK adds the signals in
set to the current mask (union). SIG_UNBLOCK removes them from the current mask. SIG_SETMASK replaces the entire mask with set.Q2. What happens if you try to block SIGKILL using sigprocmask()?
The request is silently ignored. sigprocmask() returns success (0), but SIGKILL remains unblockable. The same applies to SIGSTOP.
Q3. How do you implement a critical section using sigprocmask()?
Before the critical section, call sigprocmask(SIG_BLOCK, &desired_set, &old_set) to block the signals and save the old mask. After the section, call sigprocmask(SIG_SETMASK, &old_set, NULL) to restore the original mask. Any signals that arrived during the block are then delivered.
Q4. What is the purpose of the
oldset argument?It receives the signal mask that was in effect before the call. This lets you restore the previous mask after the critical section, making the blocking/unblocking safely nestable even if the caller’s mask was not empty.
Q5. What happens to a signal that is sent to a process while it is in the process’s signal mask (blocked)?
The signal becomes pending โ it is recorded in the process’s pending signal set. It is not delivered until it is unblocked. If the same signal is sent multiple times while blocked, it is still only delivered once when unblocked (for standard signals).
Continue Learning
