内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字mask可以影响未决信号集。而我们可以在应用程序中自定义set来改变mask。已达到屏蔽指定信号的目的。综上:自定义信号集set(也为一个字,64位)通过信号集操作函数来改变信号屏蔽字mask,然后mask进一步来影响未决信号集,从而控制信号是否应该被屏蔽。后面我们只是研究常规信号,即1~31号!
(1)信号集的设置
sigset_t set; // typedef unsigned long sigset_t; 声明一个信号集变量set,信号集类型为sigset_t,为unsigned long(无符号长整型),64位。
int sigemptyset(sigset_t *set); //将set所指信号集清0 成功:0;失败:-1
int sigfillset(sigset_t *set); //将set所指信号集置1 成功:0;失败:-1
int sigaddset(sigset_t *set, int signum); //将某个信号加入信号集(即将其置1) 成功:0;失败:-1
int sigdelset(sigset_t *set, int signum); //将某个信号清出信号集(即将其置0) 成功:0;失败:-1
int sigismember(const sigset_t *set, int signum); 判断某个信号是否在信号集中(是否为1)返回值:在集合:1;不在:0;出错:-1
sigset_t类型的本质是位图。但不应该直接使用位操作,而应该使用上述函数,保证跨系统操作有效。
对比认知select 函数。
(2)sigprocmask函数
用来屏蔽信号或解除屏蔽,即用于读取和修改进程的信号屏蔽字mask。在用户设置好自己的信号集set后,通过sigprocmask函数,可以利用set来对mask进行修改,以实现屏蔽和解除屏蔽的目的。
注意:屏蔽信号只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢弃处理。
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
返回值: 成功:0;失败:-1,设置errno
参数how:1.SIG_BLOCK: 当how设置为此值,set表示需要屏蔽的信号。相当于 mask = mask|set,即将set中为1的信号添加到mask中,mask中原有的信号不变;2.SIG_UNBLOCK: 当how设置为此,set表示需要解除屏蔽的信号。相当于 mask = mask & ~set(先对set位取反,再位与操作),即将set中为1的信号对应的mask中将其置为0(解除屏蔽),其余信号不变;3.SIG_SETMASK:当how设置为此,set表示用于替代mask的新屏蔽集。相当于 mask = set。调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。
参数set:传入参数,是一个位图,自己设置。
参数oldset:传出参数,保存旧的信号屏蔽集,即在该函数对mask处理前的mask的值。 如果不关心上一次的屏蔽字mask是什么,第三个参数可以设为NULL。
(3)sigpending函数
作用:读取当前进程的未决信号集
int sigpending(sigset_t *set); set传出参数。 返回值:成功:0;失败:-1,设置errno
//练习:编写程序。把所有常规信号的未决状态打印至屏幕。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>void printset(sigset_t *ped) //打印出ped所指信号集的前1~31位(常规信号)
{int i;for(i = 1; i < 32; i++){if( (sigismember(ped, i) == 1) ){putchar('1'); //标准输出} else if( (sigismember(ped, i) == 0) ) {putchar('0');} else {perror( “sigismember”);exit(1);}}printf("\n");
}int main(void)
{sigset_t set, ped;
#if 0 sigemptyset(&set); //清0sigaddset(&set, SIGINT); //加入2号信号sigaddset(&set, SIGQUIT); //加入3号信号sigaddset(&set, SIGKILL); //加入9号信号,记住其不可以被阻塞、忽略和捕捉sigaddset(&set, SIGSEGV); //加入11号信号
#endifsigfillset(&set); //全部置1,则所有信号全部设置为阻塞sigprocmask(SIG_BLOCK, &set, NULL); //不获取原屏蔽字while(1){sigpending(&ped); //获取未决信号集printset(&ped);sleep(1);}return 0;
}
[root@localhost 01_signal_test]# ./sigpending
0000000000000000000000000000000
0000000000000000000000000000000
^Z0000000000000000000100000000000 //Ctrl +Z 20号信号
^C0100000000000000000100000000000 //Ctrl +C 2号信号
^\0110000000000000000100000000000 //Ctrl +\ 3号信号
0110000000000000000100000000000
0110000000000000000100000000000
0110000000000000000100000000000
0110000000100000000100000000000 // [root@localhost ~]# kill -11 5710
通过以上可以看出:发送的信号全部被阻塞,在未决信号集中都置为1。最后发送9号信号,杀死该进程。