基本概念
信号递达(Delivery):实际执行信号的处理动作
信号的处理动作为:默认处理动作,忽略,用户自定义函数
信号未决(Pending):信号从产生到递达之间的状态
信号未决表示当前进程已经接受到了该信号,位图中该信号对应的bit位已经被置成了1。
在Linux下:普通信号在递达之前如果产生多次,只被记录一次,而实时信号在递达之前产生多次可以依次放在一个队列里
阻塞信号(Block):信号一但产生,就会被阻塞在未决状态,只有在接触阻塞后,该信号才会被递达
注意:阻塞和忽略是不一样的,只要信号被阻塞,它就不会被递达,而忽略是信号递达的一种方式
在内核中的表示
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。
信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。
上图中:
1.SIGHUP信号未产生过,也没有被阻塞,所以block表和pending表中都未对该信号标志
2.SIGINT信号产生了,进程已经接受了该信号,但是该信号被阻塞了,block表和pending表都对其进行了标志,只有在阻塞解除后,操作系统才会在合适的时机对其进行处理。
3.SIGQUIT信号没有产生,但已经被阻塞,所以我们可以得出,并不是在信号产生后才能对其进行阻塞,而是在信号未产生时就会阻塞它,也就是一但SIGQUIT信号产生,它就会被阻塞在pending状态
signet_t 信号集类型
从上图我们可以看出,每个信号只有一个bit的未决标志和阻塞标志,非0即1,所以并不会记录其产生和阻塞的次数。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储。sigset_t称为信号集,这个类型可以表示每个信号的“有效”和“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。
阻塞信号集也叫作当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略
信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);//初始化set指向的信号集,使其中所有信号对应的的bit位清零,表示该信号集不含任何有效信号
int sigfillset(sigset_t *set);//初始化set指向的信号集,使其中所有信号对应的bit位置位,表示该信号集的有效函数包括系统支持的所有信号
int sigaddset(sigset_t *set, int signo);//向信号集中添加某种有效信号
int sigdelset(sigset_t *set, int signo);//删除信号集中对应的有效信号
int sigismember(const sigset_t *set, int signo);//布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含返回0,出错返回-1
返回值:前四个函数都是成功返回0,出错返回-1
sigprocmask
- 调用该函数可以读取或更改当前进程的信号屏蔽字(阻塞信号集)
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:成功返回0,出错返回-1
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。
如果set是非空指针,则更改当前进程的信号屏蔽字,参数how指示如何更改
如果oset和set都是非空指针,则现将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改当前信号屏蔽字。
假设当前信号屏蔽字为mask,下表说明了how参数如何指示修改
SIG_BLOCK | set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set |
---|---|
SIG_UNBLOCK | set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set |
SIG_SETMASK | 设置当前信号屏蔽字为set所指向的值,相当于mask=set |
sigpending
#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。
下面写一个小程序,显示当前进程的所有普通信号的未决状态
#include <signal.h>
#include <stdio.h>
void showpending(sigset_t *set)
{
int i = 0;
for(i=1;i<32;i++)//从1号信号开始
{
if(sigismember(set,i))//判断指定信号是否在目标信号集中
putchar('1');
else
putchar('0');
}
puts("");
}
int main()
{
sigset_t sig,pend;
sigemptyset(&sig);
sigfillset(&sig);
sigaddset(&sig,2);//将2号信号作为有效信号添加进去
sigprocmask(SIG_BLOCK,&sig,NULL);//添加2号信号到信号屏蔽字中,相当于阻塞2号信号
while(1)
{
sigpending(&pend);//获取当前进程的未决信号集,用新定义的信号集类型pend接收
showpending(&pend);//打印信号未决集
sleep(1);//防止打印过快,结果不明显
}
return 0;
}
运行结果:
可以看到当我们在键盘按下Ctrl-C时,2号信号被标志为了1,说明此时2号信号被阻塞,一直处在未决状态
大家测试的时候不要弄死循环了,还得再开一个终端用kill命令杀死进程才行