Chinaunix首页 | 论坛 | 博客
  • 博客访问: 712474
  • 博文数量: 31
  • 博客积分: 330
  • 博客等级: 一等列兵
  • 技术积分: 3004
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-05 22:38
个人简介

java开发工程师,专注于内核源码,算法,数据结构。 qq:630501400

文章分类
文章存档

2014年(2)

2013年(22)

2012年(7)

分类: 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


  1. static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
  2.                           int group, int from_ancestor_ns)
  3.   {
  4.           struct sigpending *pending;
  5.           struct sigqueue *q;
  6.           int override_rlimit;
  7.  
  8.           trace_signal_generate(sig, info, t);

  9.           assert_spin_locked(&t->sighand->siglock);
  10.  
  11.           if (!prepare_signal(sig, t, from_ancestor_ns))
  12.                   return 0;
  13.   
  14.           pending = group ? &t->signal->shared_pending : &t->pending;
  15.           /*
  16.            * Short-circuit ignored signals and support queuing
  17.           * exactly one non-rt signal, so that we can get more
  18.            * detailed information about the cause of the signal.
  19.            */
  20.           if (legacy_queue(pending, sig))     //903行

903lagacy_queue


  1. static inline int legacy_queue(struct sigpending *signals, int sig)
  2.   {
  3.           return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
  4.   }

SIGRTMIN=32sigismember函数:


  1. #define sigismember(set,sig) \
  2.          (__builtin_constant_p(sig) ? \
  3.           __const_sigismember(set,sig) : \
  4.           __gen_sigismember(set,sig))

__builtin_constant_pgcc内建函数,如果在编译期sig为常数。返回true,否则false



  1.  static inline int __const_sigismember(sigset_t *set, int _sig)
  2.  {
  3.          unsigned long sig = _sig - 1;
  4.         return 1 & (set->sig[sig / _NSIG_BPW] >> (sig % _NSIG_BPW));
  5.  }

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数组的indexsig % _NSIG_BPW代表long的第几位减1位,所以上述表达式的循环右移后,最后以为正好是信号sig的标志位,然后这个标记位和1进行与操作,返回结果就是在数组sigset_t中是否标记有信号sig


  1. static inline int __gen_sigismember(sigset_t *set, int _sig)
  2. {
  3.         int ret;
  4.         asm ("bfextu %1{%2,#1},%0"
  5.                  : "=d" (ret)
  6.                  : "od" (*set), "id" ((_sig-1) ^ 31)
  7.                 : "cc");
  8.          return ret;
  9. }

gen_sigismember 函数作着和__const_sigismember函数一样的事情,这个是用内联汇编实现的,返回sigset_t在相应的位上是否为1,首先befxtu汇编函数的格式是:

bfextu {offset:width},Dn

作用是:基地址是ea地址,位移offsetwidth是取得的宽度位数

这样通过befxtu函数直接取出了sigset_t数组中sig位的值,如果1就是有相关的信号,0就是没有相关的信号。

通过上面阐述,可以看出lagecy_queue函数的作用就是,如果信号值小于32且,sigset_t结构判断有无相关的信号标志,有的话就返回,kill函数结束退出,这就是所谓的非实时信号不排队,对于大于32信号的,不管该实时信号是否已经在sigset_t中,都继续后面的流程(加入队列中,如果是内核发出的sigkill,sigstop,就不排队了,后面直接唤醒,终止进程)。

后面的函数会为sig信号分配一个sigqueue结构q,同时把这个q加入到pending的队列中

  1. q = __sigqueue_alloc(sig, t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE,override_rlimit);
  2. if (q) {
  3.             list_add_tail(&q->list, &pending->list);


  1. sigaddset(&pending->signal, sig);
  2. complete_signal(sig, t, group);

调用这两行,971行把pending队列当中的信号标志位设置为1,972行就是按照具体的情况将进程唤醒或者直接返回,这个情况比较复杂,过程后面在分析。


阅读(4657) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:二叉树的实现

给主人留下些什么吧!~~