For each process, the kernel maintains a signal mask—a set of signals whose delivery to the process is currently blocked. If a signal that is blocked is sent to a process, delivery of that signal is delayed until it is unblocked by being removed from the process signal mask. (In How the UNIX Signal Model Maps to Threads, we’ll see that the signal mask is actually a per-thread attribute, and that each thread in a multithreaded process can independently examine and modify its signal mask using the pthread_sigmask() function.)
A signal may be added to the signal mask in the following ways:
When a signal handler is invoked, the signal that caused its invocation can be automatically added to the signal mask. Whether or not this occurs depends on the flags used when the handler is established using sigaction().
When a signal handler is established with sigaction(), it is possible to specify an additional set of signals that are to be blocked when the handler is invoked.
The sigprocmask() system call can be used at any time to explicitly add signals to, and remove signals from, the signal mask.
We delay discussion of the first two cases until we examine sigaction() in Changing Signal Dispositions: sigaction(), and discuss sigprocmask() now.
#include <signal.h>
int sigprocmask
(int how, const sigset_t *set, sigset_t *oldset);
Returns 0 on success, or -1 on error
We can use sigprocmask() to change the process signal mask, to retrieve the existing mask, or both. The how argument determines the changes that sigprocmask() makes to the signal mask:
SIG_BLOCK
The signals specified in the signal set pointed to by set are added to the signal mask. In other words, the signal mask is set to the union of its current value and set.
SIG_UNBLOCK
The signals in the signal set pointed to by set are removed from the signal mask. Unblocking a signal that is not currently blocked doesn’t cause an error to be returned.
SIG_SETMASK
The signal set pointed to by set is assigned to the signal mask.
In each case, if the oldset argument is not NULL
, it points to a sigset_t buffer that is used to return the previous signal mask.
If we want to retrieve the signal mask without changing it, then we can specify NULL
for the set argument, in which case the how argument is ignored.
To temporarily prevent delivery of a signal, we can use the series of calls shown in Example 20-5 to block the signal, and then unblock it by resetting the signal mask to its previous state.
Example 20-5. Temporarily blocking delivery of a signal
sigset_t blockSet, prevMask; /* Initialize a signal set to contain SIGINT */ sigemptyset(&blockSet); sigaddset(&blockSet, SIGINT); /* Block SIGINT, save previous signal mask */ if (sigprocmask(SIG_BLOCK, &blockSet, &prevMask) == -1) errExit("sigprocmask1"); /* ... Code that should not be interrupted by SIGINT ... */ /* Restore previous signal mask, unblocking SIGINT */ if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1) errExit("sigprocmask2");
SUSv3 specifies that if any pending signals are unblocked by a call to sigprocmask(), then at least one of those signals will be delivered before the call returns. In other words, if we unblock a pending signal, it is delivered to the process immediately.
Attempts to block SIGKILL
and SIGSTOP
are silently ignored. If we attempt to block these signals, sigprocmask() neither honors the request nor generates an error. This means that we can use the following code to block all signals except SIGKILL
and SIGSTOP
:
sigfillset(&blockSet); if (sigprocmask(SIG_BLOCK, &blockSet, NULL) == -1) errExit("sigprocmask");