Linux信号处理机制(三)(信号捕捉)

2017-06-10 00:22 阅读 833 次 评论 0 条

本文主要针对信号捕捉展开话题,文中或多或少会出现前两篇文章中的知识点,对此迷惑的可以查看信号处理机制(一)信号处理机制(二)

内核是如何捕捉信号的?

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为信号捕捉。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,画此图说明:

上图很好的说明了信号捕捉时用户与内核态的切换(用户处理信号最好的时机是程序从内核态切换到用户态时),下面就上图的操作作以解释说明:


1)用户程序注册了SIGQUIT信号的处理函数sighandler(自定义信号处理函数)。

2)当前正在执行main函数,这是发生中断、异常或系统调用切换的内核态。

3)在中断处理完毕后要返回用户态的main函数之间,检查到有信号SIGQUIT递达。

4)内核决定返回用户态后不是恢复main函数的上下文继续向下执行,而是执行sighandle函数,sighandler和main函数使用不同的堆栈空间,两者之间不存在调用和被调用的关系,属于两个独立的控制流程。

5)sighandler函数返回后自动执行特殊的系统调用,调用sigreturn再次进入内核态。

6)如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续向下执行。


当然除过以上的解释之外,你是否注意了那几个特殊的ABCD坐标呢?没错,信号捕捉过程供发生了4次内核与用户态的切换,其中3与4是自定义捕捉函数引起的,可有可无

除此之外,用户处理信号的时机为上图红色箭头所示,即内核态切换到用户态之时,为什么要选此时?原因在于:

信号不一定会被立即处理,操作系统不会为了处理一个信号而挂起当前正在运行的进程,这样产生的消耗太大(紧急信号可能会被立即处理)。操作系统选择在内核态切换到用户态的时候去处理信号,不要单独进行进程切换而浪费时间。但是有时候一个正在睡眠的进程突然收到信号,操作系统肯定不愿意切换当前正在运行的进程,预示就将该信号存在此进程的PCB的信号字段中。

信号处理程序捕捉信号的基本思想

捕捉思想是对上述内核捕捉的一点补充,如果没有理解上图,请看下图信号处理程序捕获信号的基本过程:

一个进程可以有选择性地阻塞接受某种信号,当一种信号被阻塞时,它仍可以被发送,但是产生的待处理信号不会被接受,直到进程取消对这种信号的屏蔽。

一个待处理信号最多只能被接收因此。内核为每个进程在pending表中维护着待处理信号的集合,而在block表中维护着被阻塞的信号集合。只要传送一个类型为n的信号,内核就会设置pending表中的第n位只要接收了类型为n的信号,内核就会清除pending表的第n位

可移植的信号处理

为了处理信号处理语义的差异,Posix标准定义了sigaction函数,它允许像Linux和Solaris这样与Posix兼容的系统上的用户,明确地指定它们想要的信号处理语义。

sigaction函数可以读取或指定信号相关联的处理动作,signal与其功能类似,但signal()是标准C的信号接口。函数原型如下:

参数signum:指定信号的编号(利用kill -l可以查看)。

参数*act:若act指针非空,则根据act修改该信号的处理动作。

参数*oldact:若oldact非空,则通过oldact传出该信号原来的处理动作。

返回值:成功返回0,出错返回-1。

上述参数中,act与oldact都指向sigaction结构体,其定义如下:

对上述结构体的成员的说明如下:

sa_handler:将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行默认动作,赋值为函数指针表示用自定义函数捕捉信号。该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这是一个回调函数,不是main函数调用,而是被系统所调用。

sa_mask:是一个信号集,可以将信号加进进程的信号屏蔽字中。如果在调用信号处理函数时,除当前信号外,还希望屏蔽一些其他的信号,就可以使用sa_mask来屏蔽,仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。

sa_flags:包含多个选项,一般设置为0即可。

sa_restorer:实时处理函数(感兴趣可以自行了解一下)。

pause与alarm函数

函数说明:pause() 库函数使调用进程(或线程)睡眠状态,直到接收到信号,要么终止,要么导致它调用一个信号捕获函数。

返回值:只返回-1。

错误代码:EINTR 有信号到达中断了此函数。

pause函数使调用进程挂起直到有信号递达。可能出现的三种状态如下:

① 如果信号的处理动作是终止进程,则进程终 止,pause函数没有机会返回。

② 如果信号的处理动作是忽略,则进程继续处于挂起状态,pause 不返回。

③ 如果信号的处理动作是捕捉,则调⽤用了信号处理函数之后pause返回-1,errno设置为 EINTR,所以pause只有出错的返回值。

注意:alarm在此不再獒述,对此不解翻阅我的另外一篇博客信号处理机制(一)

普通版本的mysleep

现象是每2秒打印一次,模拟了sleep函数的概念。

对于上述代码的理解主要有以下几点:

主函数调用mysleep函数mysleep函数调用sigaction注册了SIGALRM信号的处理函数。

调用alarm(seconds)设置闹钟

调用pause函数挂起等待,内核切换到别的进程执行。

④ seconds秒之后,闹钟超时,内核发SIGALRM给这个进程

⑤ 从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,处理函数为handler。

⑥ 切换到用户态执行handler函数,发现SIGALRM信号被自动屏蔽,从handler函数返回时SIGALRM信号自动解除屏蔽,然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程。

pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。

关于上述问题的几点思考

问题1:信号处理函数handler什么都没做,为什么还有注册它为SIGALRM的处理函数?不注册可以吗?

答:必须注册。因为pause函数使调用进程挂起直到有信号递达,如果未注册,当有信号SIGALRM产生时会执行默认动作,即终止进程

问题2:为什么mysleep函数返回时要恢复SIGALRM信号原先的sigaction?

答:mysleep函数在mysleep(time)之后不会对SIGALRM信号进行修改,将SIGALRM不恢复会使alarm()失效

问题3:mysleep函数的返回值代表什么含义?什么情况下返回非0值?
答:表示信号传来时闹钟还剩余的秒数。当闹钟结束前有其他信号发送给该进程,并对该进程进行了相关处理时,alarm(0)表示取消闹钟,且返回值为非0。

遗留的问题

上述mysleep程序虽然跑完了,但是并没有立即结束。出现这个问题的根本原因是系统运行的时序并不像我们写程序时所设想的那样,虽然alarm(seconds)紧接着下一步就是pause(),但是无法保证pause()一定会在调用alarm(seconds)之后的seconds秒之内被调用。

由于异步事件在任何时候都可能会发送(异步事件在这里指出现更高优先级的进程),如果我们写程序时考虑不周密,就可能由于时序问题而导致错误,这就是竞态条件(Race Condition)

避免竞态条件

设想如何将解除信号屏蔽挂起等待信号合并成一个原子操作就可以避免时序问题,因此引入了sigsuspend函数。它不仅用于pause函数的挂起等待功能,而且也解决了竞态条件产生的时序问题。

sigsupend()函数的原型如下:

参数make:指定进程的信号屏蔽字,可以临时解除对某一信号的屏蔽,然后挂起等待。当sigsuspend返回时,进程的信号屏蔽字恢复原先的值,如果原先对该信号是屏蔽的,返回后仍然屏蔽。

返回值:返回值与pause一致。永远返回-1,errno设置为EINTR。

规避竞态条件的mysleep

网上对这里说的千篇一律,实在不该恭维。如果在调用mysleep函数时SIGALRM信号没有屏蔽,则有:

1)调用sigprocmask(SIG_BLOCK,&newmask, &oldmask)时,屏蔽SIGALRM。

2)调用sigsuspend(&suspmask)时,解除对SIGALRM的屏蔽,然后挂起等待。

3)SIGALRM递达后suspend返回,自动恢复原来的屏蔽字,也就是再次屏蔽SIGALRM。

4)调用sigprocmask(SIG_SETMASK, &oldmask, NULL)时,再次解除对SIGALRM的屏蔽。

pause与sigsupend的区别

① sigsupend是凌驾于pasue基础之上的函数,不仅实现了pause函数特有的挂起等待功能,也解决了pasue的不足带来的时序问题

② 当sigsupend函数的参数信号集为空信号时,sigsupend函数与pause函数功能一致,可以接受任何信号的中断。

③ sigsupend函数可以屏蔽信号,接受指定的信号中断,而pause不可以。

④ pause函数通过指定屏蔽信号可以达到sigsupend函数的功能。

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:Linux信号处理机制(三)(信号捕捉) | 术与道的分享
分类:操作系统 标签:, , ,
1024do.com导航_术与道导航平台

发表评论


表情