java开发工程师,专注于内核源码,算法,数据结构。 qq:630501400
分类: LINUX
2012-09-06 10:08:36
看到一些文章中看到说,linux信号有一部分是排队,另一部分是不排队的,在“独辟蹊径品内核”中对于信号排队是这样说的“由于linux早期只有31个信号,内核中仅仅用一个32位的变量signal来表示当前进程接受到的信号,如果要向一个进程发送一个信号,就把signal的第n位设置成为1,这样会很快的判断出进程接收到了哪些信号,这样是处理不了一个信号的多次发送,此后linux为了兼容的目的,1-32号信号继续采用原来的处理方式,33-64使用新的机制,32-64成为实时信号”。以上的引用的意思时,现在的linux内核实现中,1-32号采用原来的处理方式,32-64位采用队列方式处理信号。来看下内核中如何实现这些:
以下代码在2.6.35.13 : kernel/signal.c :894
在903中lagacy_queue中
宏 SIGRTMIN=32,sigismember函数:
__builtin_constant_p是gcc内建函数,如果在编译期sig为常数。返回true,否则false
const_sigismember函数返回进程中存储信号的标志位是否为1,为1代表有相关的信号,首先,sigset_t是够long的数组,长度为2,所以sigset_t能表示的信号总数有64个(2×4×8),如果进程中有信号15,那么数组中第一个元素(32位)的第15位为1,从上面的函数中看到,宏_NSIG_BPW=32代表一个long能代表的信号数量,所以 sig / _NSIG_BPW代表sig所在的long数组的index, sig % _NSIG_BPW代表long的第几位减1位,所以上述表达式的循环右移后,最后以为正好是信号sig的标志位,然后这个标记位和1进行与操作,返回结果就是在数组sigset_t中是否标记有信号sig。
gen_sigismember 函数作着和__const_sigismember函数一样的事情,这个是用内联汇编实现的,返回sigset_t在相应的位上是否为1,首先befxtu汇编函数的格式是:
bfextu
作用是:基地址是ea地址,位移offset,width是取得的宽度位数
这样通过befxtu函数直接取出了sigset_t数组中sig位的值,如果1就是有相关的信号,0就是没有相关的信号。
通过上面阐述,可以看出lagecy_queue函数的作用就是,如果信号值小于32且,sigset_t结构判断有无相关的信号标志,有的话就返回,kill函数结束退出,这就是所谓的非实时信号不排队,对于大于32信号的,不管该实时信号是否已经在sigset_t中,都继续后面的流程(加入队列中,如果是内核发出的sigkill,sigstop,就不排队了,后面直接唤醒,终止进程)。
后面的函数会为sig信号分配一个sigqueue结构q,同时把这个q加入到pending的队列中
调用这两行,971行把pending队列当中的信号标志位设置为1,972行就是按照具体的情况将进程唤醒或者直接返回,这个情况比较复杂,过程后面在分析。