Linux信号机制概述(3)

1)查找指定信号由do_signal()->get_signal_to_deliver()完成

int get_signal_to_deliver(siginfo_t *info,struct k_sigaction *return_ka,

                              struct pt_regs *regs, void *cookie)

{

         struct sighand_struct *sighand = current->sighand;

         struct signal_struct *signal = current->signal;

         int signr;

 

relock:

         ……

         for (;;) {

                   struct k_sigaction *ka;

                   ……

                   signr = tracehook_get_signal(current, regs, info, return_ka);

                   if (unlikely(signr < 0))

                            goto relock;

                   if (unlikely(signr != 0))

                            ka = return_ka;

                   else {

                            /*从挂起队列中获取信号*/

                            signr = dequeue_signal(current, &current->blocked,

                                                      info);

……

                            ka = &sighand->action[signr-1];/*找到对应的action*/

                   }

                   if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */

                            continue;

                   if (ka->sa.sa_handler != SIG_DFL) {

                            /*将找到的action设置为参数返回*/

                            *return_ka = *ka;

                            if (ka->sa.sa_flags & SA_ONESHOT)

                                     ka->sa.sa_handler = SIG_DFL;

                            break; /* will return non-zero "signr" value */

                   }

                   ……

         }//end for

         spin_unlock_irq(&sighand->siglock);

         return signr;

}

 

int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)

{

         int signr;

/*先从私有挂起队列中寻找*/

         signr = __dequeue_signal(&tsk->pending, mask, info);

         if (!signr) {

/*当私有挂起队列中找不到时,从共享队列中找*/

                   signr = __dequeue_signal(&tsk->signal->shared_pending,

                                                mask, info);

         ……

}

……

         return signr;

}

内核先从自己私有的挂起信号队列中查找,当找不到时,再从共享信号队列中查找,以参数的形式返回找到的action。

2)信号处理do_signal()->handle_signal()

staticint

handle_signal(unsignedlong sig, siginfo_t *info, struct k_sigaction *ka,

               sigset_t *oldset, struct pt_regs *regs)

{

         int ret;

 

         /* Are we from a system call? */

         if (syscall_get_nr(current, regs) >= 0) {

                   /* If so, check system call restarting.. */

                   switch (syscall_get_error(current, regs)) {

                   case -ERESTART_RESTARTBLOCK:

                  case -ERESTARTNOHAND:

                            regs->ax = -EINTR;

                            break;

 

                   case -ERESTARTSYS:

                            if (!(ka->sa.sa_flags & SA_RESTART)) {

                                     regs->ax = -EINTR;

                                    break;

                            }

                   /* fallthrough */

                   case -ERESTARTNOINTR:

                            regs->ax = regs->orig_ax;

                            regs->ip -= 2;

                            break;

                   }

         }

……

         /*实际处理*/

         ret = setup_rt_frame(sig, ka, info, oldset, regs);

 

         if (ret)

                   return ret;

……

         return 0;

}

上面的setup_rt_frame()在Intel32体系下会最终调用下面函数完成。

int ia32_setup_frame(int sig,struct k_sigaction *ka,

                        compat_sigset_t *set,struct pt_regs *regs)

{

         struct sigframe_ia32 __user *frame;

         void __user *restorer;

         int err = 0;

         void __user *fpstate = NULL;

 

         /* copy_to_user optimizes that into a single 8 byte store */

/*设置ia32_sigreturn系统调用,用于返回*/

         staticconst struct {

                   u16 poplmovl;

                   u32 val;

                   u16 int80;

         } __attribute__((packed)) code = {

                   0xb858,              /* popl %eax ; movl $...,%eax */

                   __NR_ia32_sigreturn,

                   0x80cd,              /* int $0x80 */

         };

         /*返回为栈中的一部分数据*/

         frame = get_sigframe(ka, regs,sizeof(*frame), &fpstate);

……

 

/*设置为用户空间的各种寄存器,完成设置后,由于ip设置为handler处,所以调到该处执行*/

         /* Set up registers for signal handler */

         regs->sp = (unsignedlong) frame;

         regs->ip = (unsignedlong) ka->sa.sa_handler;

 

         /* Make -mregparm=3 work */

         regs->ax = sig;

         regs->dx = 0;

         regs->cx = 0;

 

         loadsegment(ds, __USER32_DS);

         loadsegment(es, __USER32_DS);

 

         regs->cs = __USER32_CS;

         regs->ss = __USER32_DS;

         return 0;

}

        终于看到内核怎么处理了吧,由于信号的handler为用户空间程序,系统需要返回到用户空间执行,所以我们看到,内核直接设置ip为handler,代码段cs为用户空间代码段,数据段ds、es以及栈ss等都设置为用户空间的,该函数完成后,程序将冲用户空间的handler处开始执行。上面程序设置ia32_sigreturn系统调用作为程序返回的调用。整体的运行原理图如下(该图参考《深入理解Linux内核框架》  ):

Linux信号机制概述

Linux内核信号机制用到很多地方,最常见的是进程间通信,原理较简单,并且异步调用。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/83875b1af14085687891dbb22a9c3d57.html