我们需要有一个能表示多个信号——信号集(signal set)的数据类型。
我们将在sigprocmask (下一节中说明)类函数中使用这种数据类型,以便告诉内核不允许发生该信号集中的信号。如前所述,不同的信号的编号可能超过一个整型量所包含的位数,所以一般而言,不能用整型量中的一位代表一种信号,也就是不能用一个整型量表示信号集。POSIX.1定义数据类型sigset_t以包含一个信号集,并且定义了下列5个处理信号集的函数。
#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);
//4个函数返回值:若成功,返回0;若出错,返回−1
int sigismember(const sigset_t *set, int signo);
//返回值:若真,返回1;若假,返回0
函数sigemptyset初始化由set指向的信号集,清除其中所有信号。
函数sigfillset初始化由set指向的信号集,使其包括所有信号。
所有应用程序在使用信号集前,要对该信号集调用sigemptyset或sigfillset一次。这是因为C编译程序将不赋初值的外部变量和静态变量都初始化为0,而这是否与给定系统上信号集的实现相对应却并不清楚。
一旦已经初始化了一个信号集,以后就可在该信号集中增、删特定的信号。
函数 sigaddset将一个信号添加到已有的信号集中。
sigdelset 则从信号集中删除一个信号。对所有以信号集作为参数的函数,总是以信号集地址作为向其传送的参数。
实现
?: 如果实现的信号数目少于一个整型量所包含的位数,则可用一位代表一个信号的方法实现信号集。例如,本书的后续部分都假定一种实现有31种信号和32位整型。
sigemptyset函数将整型设置为0, sigfillset函数则将整型中的各位都设置为1。这两个函数可以在<signal.h>头文件中实现为宏:
#define sigemptyset(ptr) (*(ptr) = 0)
#define sigfillset(ptr) (*(ptr) = ~(sigset_t)0, 0)
注意,除了设置信号集中各位为1外,sigfillset必须返回0,所以使用C语言的逗号算符,它将逗号算符后的值作为表达式的值返回。
使用这种实现,sigaddset 开启一位(将该位设置为 1),sigdelset 则关闭一位(将该位设置为0);sigismember测试一个指定的位。因为没有信号编号为0,所以从信号编号中减1以得到要处理位的位编号数。图10-12给出了这些函数的实现。
#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);
}
图10-12 sigaddset、sigdelset和sigismember的实现
也可将这3个函数在<signal.h>中实现为各一行的宏,但是POSIX.1要求检查信号编号参数的有效性,如果无效则设置errno。在宏中实现这一点比函数要难。