Linux信号机制概述(2)

1,   设置新的action;

系统调用signal用于实现这个功能,当然也可以用sigaction系统调用,

SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler)

{

         struct k_sigaction new_sa, old_sa;

         int ret;

 

         new_sa.sa.sa_handler = handler;

         new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;

         sigemptyset(&new_sa.sa.sa_mask);

 

         ret = do_sigaction(sig, &new_sa, &old_sa);

 

         return ret ? ret : (unsignedlong)old_sa.sa.sa_handler;

}

该系统调用分配一个新的action后,调用do_sigaction完成实际工作,最终返回旧的action的handler。

int do_sigaction(int sig,struct k_sigaction *act, struct k_sigaction *oact)

{

         struct task_struct *t = current;

         struct k_sigaction *k;

         sigset_t mask;

……

         k = &t->sighand->action[sig-1];

         spin_lock_irq(&current->sighand->siglock);

         if (oact)

                   *oact = *k;/*保存旧的action*/

         if (act) {

                   sigdelsetmask(&act->sa.sa_mask,

                                  sigmask(SIGKILL) | sigmask(SIGSTOP));

                   *k = *act;/*设置新的action*/

 

                  /*对两种handler的特殊处理*/

                   if (sig_handler_ignored(sig_handler(t, sig), sig)) {

                            ……

                   }

         }

         spin_unlock_irq(&current->sighand->siglock);

         return 0;

}

实现很简单,先保存旧的action,用于系统调用返回,然后设置新的action。

2,   发送信号

发送信号的系统调用有很多,最终都会调用__send_signal()函数。

staticint __send_signal(int sig,struct siginfo *info, struct task_struct *t,

                            int group, int from_ancestor_ns)

{

         struct sigpending *pending;

         struct sigqueue *q;

         int override_rlimit;

         ……

         /*找到需要挂起的队列*/

         pending = group ? &t->signal->shared_pending : &t->pending;

         ……

         /*分配队列项结构*/

q = __sigqueue_alloc(t, GFP_ATOMIC | __GFP_NOTRACK_FALSE_POSITIVE,

                   override_rlimit);

         if (q) {/*如果分配成功,将该结构添加到挂起队列,并进行初始化*/

                   list_add_tail(&q->list, &pending->list);

                   switch ((unsigned long) info) {

                   case (unsigned long) SEND_SIG_NOINFO:

                            q->info.si_signo = sig;

                            q->info.si_errno = 0;

                            q->info.si_code = SI_USER;

                            q->info.si_pid = task_tgid_nr_ns(current,

                                                                 task_active_pid_ns(t));

                            q->info.si_uid = current_uid();

                            break;

                   case (unsigned long) SEND_SIG_PRIV:

                            q->info.si_signo = sig;

                            q->info.si_errno = 0;

                            q->info.si_code = SI_KERNEL;

                            q->info.si_pid = 0;

                            q->info.si_uid = 0;

                            break;

                   default:

                            copy_siginfo(&q->info, info);

                            if (from_ancestor_ns)

                                     q->info.si_pid = 0;

                            break;

                   }

         } else if (!is_si_special(info)) {

                   if (sig >= SIGRTMIN && info->si_code != SI_USER)

                            return -EAGAIN;

         }

 

out_set:

         signalfd_notify(t, sig);/*唤醒action中的等待队列*/

         sigaddset(&pending->signal, sig);/*设置信号ID位掩码,即上面所说的那个位图*/

         complete_signal(sig, t, group);/*试着唤醒执行该信号的进程*/

         return 0;

}

发送信号,即将该信号链接到制定进程的信号挂起队列上,最后试着唤醒执行该信号的进程。

 

3,   信号捕获与执行

说了这么一堆,但我们还不明白内核怎样确保进程的挂起信号得到处理呢?内核在允许进程恢复用户态下的执行之前,检查进程TIF_SIGPENDING标志的值。每当内核处理玩一个中断或异常时,就检查是否存在挂起信号,即我们可以这样理解,在每次由内核态切换到用户态前,内核都会发起信号队列的处理,具体的信号发起和体系结构有关,但最终为了处理阻塞的挂起信号,内核都会调用do_signal()函数,为说明程序执行框架,下面的代码省略了大部分无关的代码。

staticvoid do_signal(struct pt_regs *regs)

{

         struct k_sigaction ka;

         siginfo_t info;

         int signr;

         sigset_t *oldset;

 

……

/*获取挂起信号*/

         signr = get_signal_to_deliver(&info, &ka, regs, NULL);

         if (signr > 0) {

         ……

                   /*实际的信号处理*/

                   if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {

                            current_thread_info()->status &= ~TS_RESTORE_SIGMASK;

                   }

                   return;

         }

         /*如果从系统调用返回*/

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

                   /* Restart the system call - no handlers present */

                   switch (syscall_get_error(current, regs)) {

                   case -ERESTARTNOHAND:

                   case -ERESTARTSYS:

                   case -ERESTARTNOINTR:

                            regs->ax = regs->orig_ax;

                            regs->ip -= 2;

                            break;

 

                   case -ERESTART_RESTARTBLOCK:

                            regs->ax = NR_restart_syscall;

                            regs->ip -= 2;

                            break;

                   }

         }

……

}

该系统调用主要分为两部分执行,首先是冲挂起队列中查找信号,然后是执行信号处理函数。

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

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