信号集是表示多个信号的数据类型,这里的信号集数据类型是 sigset_t,包含五个处理信号集的函数:
/* 信号集 */ #include <signal.h> int sigemptyset(sigset_t *set);//初始化由set所指向的信号集,清空信号集; int sigfillset(sigset_t *set);//初始化由set所指向的信号集,使其包括所有信号; int sigaddset(sigset_t *set, int signo);//把指定的信号signo添加到由set所指的信号集中; int sigdelset(sigset_t *set, int signo);//把指定的信号signo从由set所指定的信号集中删除; //前面四个函数返回值:若成功则返回0,若出错则返回-1; int sigismember(const sigset_t *set, int signo);//判断指定的信号signo是否在由set所指的信号集中; //返回值:若为真则返回1,若为假则返回0,若出错则返回-1; /* * 说明: * 所有应用程序使用信号集之前,要对该信号集调用sigemptyset或sigfillset一次; */
在前面我们提到,task_struct 结构有一个blocked 成员(我们称之为“信号屏蔽字”),它指定了进程阻塞的信号,被阻塞的信号将不能被递送给进程,直到进程解除阻塞。在信号被阻塞时,内核将其放置到待决列表上。如果同一个信号在阻塞期间被发送了多次,则在待决列表中只放置一次。也就是说,不管发送了多少相同的信号,在进程删除阻塞后,都只会接收到一个信号。调用函数sigprocmask 可以检测或更改其信号屏蔽字。在调用 sigprocmask 后如果有任何未决的、不再阻塞的信号,则在 sigprocmask 返回前,至少会将其中一个信号递送给该进程。
/* sigprocmask 函数 */ /* * 函数功能:检查或更改信号屏蔽字,也可同时执行这两个操作; * 返回值:若成功则返回0,若出错则返回-1; * 函数原型: */ #include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oset); /* * 说明: * 若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回; * 若set是非空指针,则参数how指示如何修改当前信号屏蔽字; * 若set是空指针,则不改变该进程的信号屏蔽字,how的值就没有意义; * 参数how可选以下值: * (1)SIG_BLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号; * (2)SIG_UNBLOCK 该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集补集的交集;set包含我们希望解除阻塞的附加信号; * (3)SIG_SETMASK 该进程新的信号屏蔽字将被set指向的信号集的值所代替; * SIG_BLOCK是"或"操作,而SIG_SETMASK则是赋值操作; * 注意:SIGKILL 和 SIGSTOP 信号是不能阻塞的; */
sigpending 函数返回信号集,其中的各个信号对于调用进程是阻塞的而不能传递,该信号集通过set参数返回。
/* sigpending函数 */ /* * 函数功能:返回信号集; * 返回值:若成功则返回0,若出错则返回-1; * 函数原型: */ #include <signal.h> int sigpending(sigset_t *set);
#include "apue.h" #include <sys/wait.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <signal.h> static void sig_quit(int signo); int main() { sigset_t newmask,oldmask,pendmask; if(signal(SIGQUIT,sig_quit) == SIG_ERR) { err_sys("signal() error"); exit(-1); } //初始化信号集 sigemptyset(&newmask); //添加一个SIGQUIT信号 sigaddset(&newmask,SIGQUIT); //将newmask信号集设置为阻塞,原信号集保存在oldmask中 if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) == -1) { err_sys("SIG_BLOCK error"); exit(-1); } sleep(5); //获取阻塞的信号集 if(sigpending(&pendmask) == -1) { err_sys("sigpending() error"); exit(-1); } //判断SIGQUIT是否是阻塞的 if(sigismember(&pendmask,SIGQUIT)) printf("\nSIGQUIT is pending.\n"); //恢复原始的信号集 if(sigprocmask(SIG_SETMASK,&oldmask,NULL) == -1) { err_sys("SIG_SETMASK error"); exit(-1); } printf("SITQUIT unblocked\n"); sleep(5); exit(0); } static void sig_quit(int signo) { printf("caught SIGQUIT.\n"); if(signal(SIGQUIT,SIG_DFL) == SIG_ERR) { err_sys("can't reset SIGQUIT"); exit(-1); } }输出结果:
$ ./sigset ^\ SIGQUIT is pending. caught SIGQUIT. SITQUIT unblocked $ ./sigset ^\^\^\^\^\^\ SIGQUIT is pending. caught SIGQUIT. SITQUIT unblocked在程序第二次 sleep 时,产生了多个 SIGQUIT 信号,此时被 pending,解除了 mask 后,只产生了一次 action,也说明了在同一时刻产生多次同一种信号,不会对信号排队。
《UNIX高级环境编程》