3、信号的保存
在聊这个之前首先要了解一些术语
实际执行信号的处理动作称为信号递达(Delivery)
信号从产生到递达之间的状态,称为信号未决(Pending)。
进程可以选择阻塞 (Block )某个信号。
被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
其中这三个对应pcb中的一个数据结构,那就位图结构,由block位图和pending位图和handler函数指针数组,发送信号过来首先要查看block位图,看看有没有阻塞掉当前的信号,如果阻塞了就不会访问pending位图,如果没有阻塞就会将pending位图由零置为一,在找他对应的函数指针去执行函数。
3.5 sigset_t
每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。
因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号
的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有
效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。
4、信号的递达
之前说过信号递达以后会在合适的时间处理,合适的时间是什么时候呢,是进程状态由内核态到用户态以后。那么如何陷入内核呢,我们需要当我们进行访问硬件资源时就会陷入内核,陷入以后在回到用户态,执行信号,在回到内核态中修改对应的位图结构,然后在出来,听起来很麻烦,其实只要将内容抽象一下,其实就是我们高中的无穷大,在这个里面关键结点我已经标出来了。记住这个图就可以很清晰的描述信号的递达过程。
在信号递达以后,有三种处理方式,默认、自定义和忽略。其中默认和自定义就比较简单了,只需要找到对应函数指针,调用函数即可,忽略就有很多讲究了。首秀按需要了解一些列的接口
Sigpending用于处理当前进程的pending位图结构,sigpromask用于处理bolck位图的
接下来就写一个好玩的东西
static void myhandler(int signo)
{
std::cout<<signo<<"信号已递达\n";
}
int main()
{
for(const auto& n : v)
{
signal(n,myhandler);
}
sigset_t block,oblock/*表示输出型参数*/,pending;
// 1. 先尝试屏蔽信号
// 1.1初始化
sigemptyset(&block);
sigemptyset(&oblock);
sigemptyset(&pending);
// 1.2添加要屏蔽的信号
// sigaddset(&block,BLOCK_SIGNAL);
for(const auto& n : v)
{
sigaddset(&block,n);
}
// 1.3开始屏蔽
sigprocmask(SIG_SETMASK, &block, &oblock);
// 2.遍历打印pending信号集
int cnt = 1;
while (1)
{
/* code */
// 2.1初始化
sigemptyset(&pending);
// 2.2获取他
sigpending(&pending);
// 2.3打印他
show_pending(pending);
sleep(1);
cnt++;
if(cnt == 10)
{
std::cout<<"恢复对信号的屏蔽,不屏蔽任何信号\n";
sigprocmask(SIG_SETMASK, &oblock, &block);
}
}
return 0;
}
我们屏蔽了2、3号信号,当第十秒的时候会取消屏蔽,当我一发送信号他就会立即调用对应的函数