Linux内核中的信号机制——信号处理

发表于:2013-2-25 10:02

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:ce123    来源:51Testing软件测试网采编

  如果user_mode(regs)返回1,接下来会执行(中间略去一下和本文关系不大的代码)get_signal_to_deliver(),这个函数从当前进程的信号队列(保存Private Signal Queue和Shared Signal Queue)取出等待处理的信号(调用dequeue_signal()函数),然后根据信号定位到对应的signal_struct结构,如果信号的处理函数sa_handler为SIG_IGN,就忽略该信号,继续取下一个信号;如果信号的处理函数sa_handler为SIG_DFL,意味着按照信号默认的处理方式对待就可以了(例如直接调用do_coredump()等)。

  如果get_signal_to_deliver()函数返回值大于0,说明这个信号的处理函数是在用户态空间(通过signal()和sigaction()等函数设置的自定义信号处理函数。),将调用handle_signal()函数进行处理。handle_signal()函数的定义如下:

/*
 * OK, we're invoking a handler
 */ 
static void
handle_signal(unsigned long sig, struct k_sigaction *ka,
       siginfo_t *info, sigset_t *oldset,
       struct pt_regs * regs, int syscall)
{
 struct thread_info *thread = current_thread_info();
 struct task_struct *tsk = current;
 int usig = sig;
 int ret;

 /*
  * If we were from a system call, check for system call restarting...
  */
 if (syscall) {
  switch (regs->ARM_r0) {
  case -ERESTART_RESTARTBLOCK:
  case -ERESTARTNOHAND:
   regs->ARM_r0 = -EINTR;
   break;
  case -ERESTARTSYS:
   if (!(ka->sa.sa_flags & SA_RESTART)) {
    regs->ARM_r0 = -EINTR;
    break;
   }
   /* fallthrough */
  case -ERESTARTNOINTR:
   restart_syscall(regs);
  }
 }

 /*
  * translate the signal
  */
 if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap)
  usig = thread->exec_domain->signal_invmap[usig];

 /*
  * Set up the stack frame//设置栈帧
  */
 if (ka->sa.sa_flags & SA_SIGINFO)
  ret = setup_rt_frame(usig, ka, info, oldset, regs);
 else
  ret = setup_frame(usig, ka, oldset, regs);

 /*
  * Check that the resulting registers are actually sane.
  */
 ret |= !valid_user_regs(regs);

 /*
  * Block the signal if we were unsuccessful.
  */
 if (ret != 0) {
  spin_lock_irq(&tsk->sighand->siglock);
  sigorsets(&tsk->blocked, &tsk->blocked,
     &ka->sa.sa_mask);
  if (!(ka->sa.sa_flags & SA_NODEFER))
   sigaddset(&tsk->blocked, sig);
  recalc_sigpending();
  spin_unlock_irq(&tsk->sighand->siglock);
 }

 if (ret == 0)
  return;

 force_sigsegv(sig, tsk);
}

  在这样情况下,进程当前处于内核态,而信号处理函数却处于用户态,为此必须在进程的用户态构造一个临时的堆栈环境(因为进程的信号处理函数在进行函数调用以及使用局部变量时需要使用堆栈。),然后进入用户态执行信号处理函数,最后再返回内核态继续执行。在这个过程中,有以下几个问题需要解决:

  1、临时的用户态堆栈在哪里呢?这个很好解决,因为可以直接使用进程现有的用户态堆栈,这里要保证的是:使用结束后这个堆栈必须和使用前是一模一样的。

  2、临时堆栈解决后,需要确定的是通过什么方法来保证返回到用户态后,进程执行的是信号的处理函数。我们知道在进入内核态时,内核态堆栈中保存了一个中断现场,也就是一个pt_regs结构,中断返回地址就保存在pt_regts中的pc中,因此我们这里只要把当前进程的pt_regs中pc设置为sa_handler,然后返回到用户态就开始从sa_handler处开始执行了。

unsigned long handler = (unsigned long)ka->sa.sa_handler;
regs->ARM_pc = handler;

52/5<12345>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号