首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux进程管理之信号处理(转)(5)

linux进程管理之信号处理(转)(5)

spin_unlock_irq(¤t->sighand->siglock);

         //除去内核忽略和引起进程停止的信号之处的所有信号都会让过程
         //终止

         /*
          * Anything else is fatal, maybe with a core dump.
          */

         //置进程标志位PF_SIGNALED.表示该信号终止是由信号引起的
         current->flags |= PF_SIGNALED;
         if ((signr != SIGKILL) && print_fatal_signals)
              print_fatal_signal(regs, signr);

         //如果是一些会引起核心转储的信号
         //建立核心转储文件后退出
         if (sig_kernel_coredump(signr)) {
              /*
               * If it was able to dump core, this kills all
               * other threads in the group and synchronizes with
               * their demise.  If we lost the race with another
               * thread getting here, it set group_exit_code
               * first and our do_group_exit call below will use
               * that value and ignore the one we pass it.
               */
              do_coredump((long)signr, signr, regs);
         }

          /*
          * Death signals, no core dump.
          */

         //进程组退出
         do_group_exit(signr);
         /* NOTREACHED */
     }
     spin_unlock_irq(¤t->sighand->siglock);
     return signr;
}
这个函数比较简单,基本上就是遍历信号等待队列.然后处理信号.一直遇到信号处理被重设或者没有等待信号之后才会返回.
信号出列函数为dequeue_signal():
int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
{
     int signr = 0;

     /* We only dequeue private signals from ourselves, we don't let
      * signalfd steal them
      */

     //从pending 队列中取出等待信号
     signr = __dequeue_signal(&tsk->pending, mask, info);

     //如果pending 队列中没有等待信号,则从shared_pending中取
     if (!signr) {
         signr = __dequeue_signal(&tsk->signal->shared_pending,
                        mask, info);
         //如果是SIGALRM 信号
         //重启计时器
         if (unlikely(signr == SIGALRM)) {
              struct hrtimer *tmr = &tsk->signal->real_timer;

              if (!hrtimer_is_queued(tmr) &&
                  tsk->signal->it_real_incr.tv64 != 0) {
                   hrtimer_forward(tmr, tmr->base->get_time(),
                            tsk->signal->it_real_incr);
                   hrtimer_restart(tmr);
              }
         }
     }

     //重新判断是位还有末处理的信号,更新TIF_SIGPENDING 标志
     recalc_sigpending();

     //会引起进程终止的信号,置SIGNAL_STOP_DEQUEUED 标志
     //禁止信号出列,即阻止后续的信号处理
     if (signr && unlikely(sig_kernel_stop(signr))) {
         if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT))
              tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
     }

     //__SI_TIMER : 定时器到期
     if (signr &&
          ((info->si_code & __SI_MASK) == __SI_TIMER) &&
          info->si_sys_private){
         spin_unlock(&tsk->sighand->siglock);
         do_schedule_next_timer(info);
         spin_lock(&tsk->sighand->siglock);
     }
     return signr;
}
__dequeue_signal()用于从等待队列中取出信号.代码如下:
static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
              siginfo_t *info)
{
     //取位图中为第一个为1的标志位
     int sig = next_signal(pending, mask);

     if (sig) {

         //如果定义了进程通告?

         //task->notifier:指向一个函数指针. 设备驱动程序用它来阻塞某些信号
         if (current->notifier) {
              if (sigismember(current->notifier_mask, sig)) {
                   if (!(current->notifier)(current->notifier_data)) {
                       clear_thread_flag(TIF_SIGPENDING);
                       return 0;
                   }
              }
         }

         //将信号从等待队列中移除,更新等待信号标志位
         if (!collect_signal(sig, pending, info))
              sig = 0;
     }

     return sig;
}

Cllect_signal()代码如下:
static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
{
     struct sigqueue *q, *first = NULL;
     int still_pending = 0;


     //要处理的信号没有包含在等待队列中,退出
     if (unlikely(!sigismember(&list->signal, sig)))
         return 0;

     /*
      * Collect the siginfo appropriate to this signal.  Check if
      * there is another siginfo for the same signal.
     */

     //遍历等待队列.如果不止有一个sig 信号在等待.still_pending为1
list_for_each_entry(q, &list->list, list) {
         if (q->info.si_signo == sig) {
              if (first) {
                   still_pending = 1;
                   break;
              }
              first = q;
         }
     }
     if (first) {

         //如果等待队列中有此信号

         //在等待队列中将它删除
         list_del_init(&first->list);
         //将信号信号copy 到info
         copy_siginfo(info, &first->info);

         //释放信号
         __sigqueue_free(first);
         if (!still_pending)
              //如果只有一个信号在等待,也就是说该类的等待信号已经处理完了
              //从等待位图中删除该位
              sigdelset(&list->signal, sig);
     } else {

         /* Ok, it wasn't in the queue.  This must be
            a fast-pathed signal or we must have been
            out of queue space.  So zero out the info.
          */
          //如果等待队列中没有此信号,将对应位图置0.
          //info信号置空
         sigdelset(&list->signal, sig);
         info->si_signo = sig;
         info->si_errno = 0;
         info->si_code = 0;
         info->si_pid = 0;
         info->si_uid = 0;
     }
     return 1;
}
返回do_signal()中看看如果信号处理函数被重设会怎么样处理.这也是信号处理中比较难理解的部份.转入具体的处理代码之前,先思考一下:
用户空间的函数地址传递给内核空间之后,可不可以在内核直接运行呢?(即设置好内核堆,再把eip设为fuction address)?
是有可能运行的.因为内核切占不会切换CR3.用户进程切换会切换CR3.因此可以保证进程陷入内核后可以正常的对用户空间的地址进行寻址.但是基于以下几点原因.不建议直接在内核空间运行
1:安全因素.陷入内核空间后,对内核地址空间具有全部访问权限,没有内存保护进制
2:内核堆栈过小,最大只有8KB.
3:用户空间的函数在运行的时候可能会发出系统调用.由于在最高特权级下,导致系统调用/异常处理失败.

既然这样,那怎么运行信号处理函数呢?
我们只需要让它在返回用户空间后马上运行信号处理函数,运行信号处理函数再系统调用返回内核就可以了.
先分析一下有关的数据结构:
struct sigframe
{
     //信号处理函数的返回地址,它指向同一个结构中的retcode字段
     char __user *pretcode;
     //信号数值
     int sig;
     //保存当前regs的一个结构
     struct sigcontext sc;
     //保存FPU,MMX,XMM等相关信息
     struct _fpstate fpstate;
     //被阻塞的实时信号的位数组
     unsigned long extramask[_NSIG_WORDS-1];
     //信号处理程序运行完后执行的执令
     char retcode[8];
}
继承事业,薪火相传
返回列表