一般情况下,我们进行系统调用以后,会有一个从用户态 向内核态的转换,等系统调用执行完以后,我们再从内核态返回到用户态,继续执行下一条语句。
现在我们要在这种情况下加入对信号的捕捉和处理过程,最关键的问题就是,什么时候处理最合适??答案是准备从内核态返回到用户态的时候!!(也就是执行完OS代码的时候)
目录
一、信号捕捉的过程
1、没有收到信号或者信号被阻塞
2、收到了信号而且信号没有被阻塞
3、总结:进程处理信号时,用户态和内核态的转换过程
二、信号处理函数
1、signal函数
2、sigaction函数
(1) 结构体 struct sigaction
(2) sigaction函数参数解析
(2) 小测试:使用sigaction函数,附加处理信号时,屏蔽2号信号
一、信号捕捉的过程我们依然以我们熟悉的printf函数为例,打印内容时,实际上会执行系统调用,转换成内核态向stdout文件写入内容,写入完毕以后,我们需要先进行信号检测!!
1、没有收到信号或者信号被阻塞执行完OS代码,我们会先进行信号检测,怎么检测呢?检查pending表,这个我们在信号保存部分已经介绍了。通过pending表我们可以知道,是否收到了信号,如果没有,那就直接返回用户态,执行下一句代码
2、收到了信号而且信号没有被阻塞现在信号收到了,假设收到的是2号信号,pending表里的第二个位置就会变成1
执行完OS代码以后,检查pending表,发现收到了信号,然后检查block表,如果2号信号被阻塞了,就会像上面直接返回到用户态;如果没有被阻塞,那就会去执行对应的处理函数
这个时候要注意,执行完处理函数不是直接就去执行用户代码的下一句了!!我们必须要保证,最后返回到用户态的时候,是从内核态返回到用户态的!!执行完处理函数,我们要先返回到内核态,然后执行sys_sigreturn()函数来返回到用户态
3、总结:进程处理信号时,用户态和内核态的转换过程下面这个图是上面简化的结果,每个交点代表状态的改变
(1) 从左边开始,先执行用户层代码,然后进入内核区执行OS代码(用户态—》内核态)
(2) 执行完以后,检测是否收到信号,这里是一个分歧点,如果没有收到信号,则直接返回用户层执行下一句,否则检测是否被阻塞,如果被阻塞了,也是直接返回用户层执行下一句(内核态——》用户态)
(3) 如果接收到信号而且没有被阻塞,那么就依据handler表执行对应的处理函数,然后返回到内核态执行sys_sigreturn()(用户态—》内核态)
(4) 最后继续执行下一句代码(内核态——》用户态)
二、信号处理函数当某号信号的信号处理函数被执行的时候,该信号的block表对应位置会被设置成1,这样就保证了如果这种信号再次产生,那么它会被阻塞到当前处理结束为止,等到处理结束,会恢复原来的信号屏蔽字
1、signal函数signal函数的作用是,搭建起某个信号和对应信号之间的联系,本质上是更改handler表中对应信号的函数指针。简单介绍一下参数和返回值
第一个参数是信号的序号,既可以写信号序号,也可以写信号名
第二个参数是函数指针,即信号处理函数的地址,这里一般是写函数名,因为函数名可以当作函数地址来用(自定义的处理函数,形参必须是int类型)
返回值:执行成功则返回修改之前函数指针,即之前的信号处理函数的地址;执行失败返回SIG_ERR
函数的调用也比较简单
void signalhandler(int signo) //signal函数会传递信号序号给这个函数{ //信号处理函数}int main(){ ... signal(2,signalhandler); //收到2号信号时,就会执行对应的信号处理函数 ... return 0;} 2、sigaction函数主要作用和上面的signal函数是一样的,但是存在一个特殊的功能,我们可以设置sigaction的某个参数,来暂时性地阻塞某个信号,等到信号处理结束以后,又恢复原本的情况。不过在那之前,我们需要先了解一下参数中出现的sigaction结构体
(1) 结构体 struct sigaction这里的结构体名字只是恰好和函数名一样,两者没有任何联系,既要能设置handler表的函数指针,又要暂时性地屏蔽其他信号,所以Linux把需要的参数打包成了一个结构体sigaction
红色线划掉的我们暂时不关心,第一个成员,毫无疑问,就是信号处理函数的函数指针
第二个成员,就是在执行信号处理函数期间,希望暂时性屏蔽的信号集
(2) sigaction函数参数解析第一个参数是信号序号
第二个参数是输入型参数,上面也说了,我们希望在设置函数指针的同时,还希望能暂时屏蔽其他信号,将这些参数打包成一个结构体
第三个是输出型参数,也是一个结构体,输出我们上一次是怎么设置的,如果不关心可设为NULL
返回值:成功返回0,失败返回-1
(2) 小测试:使用sigaction函数,附加处理信号时,屏蔽2号信号测试代码如下,黄色框的内容都只是在为act.mask这一句做准备
一开始不发送任何信号,进程只是在等待,然后按下Ctrl + 发送3号信号
此时开始执行对应的信号处理函数
我们按下Ctrl + C 发送2号信号,结果进程没有任何反应,这正是我们预期的结果
三、总结:信号从发送到被处理所经历的过程下面我们只使用一张图来表示我们学到的所有内容