Team BBL
Previous Page Next Page

10.11. Signal Sets

We need a data type to represent multiple signalsa signal set. We'll use this with such functions as sigprocmask (in the next section) to tell the kernel not to allow any of the signals in the set to occur. As we mentioned earlier, the number of different signals can exceed the number of bits in an integer, so in general, we can't use an integer to represent the set with one bit per signal. POSIX.1 defines the data type sigset_t to contain a signal set and the following five functions to manipulate signal sets.

#include <signal.h>

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signo);

int sigdelset(sigset_t *set, int signo);

All four return: 0 if OK, 1 on error

int sigismember(const sigset_t *set, int signo);

Returns: 1 if true, 0 if false, 1 on error


The function sigemptyset initializes the signal set pointed to by set so that all signals are excluded. The function sigfillset initializes the signal set so that all signals are included. All applications have to call either sigemptyset or sigfillset once for each signal set, before using the signal set, because we cannot assume that the C initialization for external and static variables (0) corresponds to the implementation of signal sets on a given system.

Once we have initialized a signal set, we can add and delete specific signals in the set. The function sigaddset adds a single signal to an existing set, and sigdelset removes a single signal from a set. In all the functions that take a signal set as an argument, we always pass the address of the signal set as the argument.

Implementation

If the implementation has fewer signals than bits in an integer, a signal set can be implemented using one bit per signal. For the remainder of this section, assume that an implementation has 31 signals and 32-bit integers. The sigemptyset function zeros the integer, and the sigfillset function turns on all the bits in the integer. These two functions can be implemented as macros in the <signal.h> header:

   #define sigemptyset(ptr)   (*(ptr) = 0)
   #define sigfillset(ptr)    (*(ptr) = ~(sigset_t)0, 0)

Note that sigfillset must return 0, in addition to setting all the bits on in the signal set, so we use C's comma operator, which returns the value after the comma as the value of the expression.

Using this implementation, sigaddset turns on a single bit and sigdelset turns off a single bit; sigismember tests a certain bit. Since no signal is ever numbered 0, we subtract 1 from the signal number to obtain the bit to manipulate. Figure 10.12 shows implementations of these functions.

Figure 10.12. An implementation of sigaddset, sigdelset, and sigismember
#include     <signal.h>
#include     <errno.h>

/* <signal.h> usually defines NSIG to include signal number 0 */
#define SIGBAD(signo)   ((signo) <= 0 || (signo) >= NSIG)

int
sigaddset(sigset_t *set, int signo)
{
    if (SIGBAD(signo)) { errno = EINVAL; return(-1); }

    *set |= 1 << (signo - 1);       /* turn bit on */
    return(0);
}

int
sigdelset(sigset_t *set, int signo)
{
    if (SIGBAD(signo)) { errno = EINVAL; return(-1); }

    *set &= ~(1 << (signo - 1));    /* turn bit off */
    return(0);
}

int
sigismember(const sigset_t *set, int signo)
{
     if (SIGBAD(signo)) { errno = EINVAL; return(-1); }

     return((*set & (1 << (signo - 1))) != 0);
}

We might be tempted to implement these three functions as one-line macros in the <signal.h> header, but POSIX.1 requires us to check the signal number argument for validity and to set errno if it is invalid. This is more difficult to do in a macro than in a function.

    Team BBL
    Previous Page Next Page