The Signal Mask (Blocking Signal Delivery)

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:

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);

Note

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");