知方号

知方号

信号捕捉与处理:信号捕捉的过程 + 信号处理函数(signal、sigaction)

一般情况下,我们进行系统调用以后,会有一个从用户态 向内核态的转换,等系统调用执行完以后,我们再从内核态返回到用户态,继续执行下一条语句。

现在我们要在这种情况下加入对信号的捕捉和处理过程,最关键的问题就是,什么时候处理最合适??答案是准备从内核态返回到用户态的时候!!(也就是执行完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号信号,结果进程没有任何反应,这正是我们预期的结果

 三、总结:信号从发送到被处理所经历的过程

下面我们只使用一张图来表示我们学到的所有内容

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。