微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > linux内核中的信号机制--信号处理

linux内核中的信号机制--信号处理

时间:11-22 来源:互联网 点击:

ignal()函数),然后根据信号定位到对应的signal_struct结构,如果信号的处理函数sa_handler为SIG_IGN,就忽略该信号,继续取下一个信号;如果信号的处理函数sa_handler为SIG_DFL,意味着按照信号默认的处理方式对待就可以了(例如直接调用do_coredump()等)。

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

[plain]view plaincopyprint?

  1. /*
  2. *OK,wereinvokingahandler
  3. */
  4. staticvoid
  5. handle_signal(unsignedlongsig,structk_sigaction*ka,
  6. siginfo_t*info,sigset_t*oldset,
  7. structpt_regs*regs,intsyscall)
  8. {
  9. structthread_info*thread=current_thread_info();
  10. structtask_struct*tsk=current;
  11. intusig=sig;
  12. intret;
  13. /*
  14. *Ifwewerefromasystemcall,checkforsystemcallrestarting...
  15. */
  16. if(syscall){
  17. switch(regs->ARM_r0){
  18. case-ERESTART_RESTARTBLOCK:
  19. case-ERESTARTNOHAND:
  20. regs->ARM_r0=-EINTR;
  21. break;
  22. case-ERESTARTSYS:
  23. if(!(ka->sa.sa_flags&SA_RESTART)){
  24. regs->ARM_r0=-EINTR;
  25. break;
  26. }
  27. /*fallthrough*/
  28. case-ERESTARTNOINTR:
  29. restart_syscall(regs);
  30. }
  31. }
  32. /*
  33. *translatethesignal
  34. */
  35. if(usig<32&&thread->exec_domain&&thread->exec_domain->signal_invmap)
  36. usig=thread->exec_domain->signal_invmap[usig];
  37. /*
  38. *Setupthestackframe//设置栈帧
  39. */
  40. if(ka->sa.sa_flags&SA_SIGINFO)
  41. ret=setup_rt_frame(usig,ka,info,oldset,regs);
  42. else
  43. ret=setup_frame(usig,ka,oldset,regs);
  44. /*
  45. *Checkthattheresultingregistersareactuallysane.
  46. */
  47. ret|=!valid_user_regs(regs);
  48. /*
  49. *Blockthesignalifwewereunsuccessful.
  50. */
  51. if(ret!=0){
  52. spin_lock_irq(&tsk->sighand->siglock);
  53. sigorsets(&tsk->blocked,&tsk->blocked,
  54. &ka->sa.sa_mask);
  55. if(!(ka->sa.sa_flags&SA_NODEFER))
  56. sigaddset(&tsk->blocked,sig);
  57. recalc_sigpending();
  58. spin_unlock_irq(&tsk->sighand->siglock);
  59. }
  60. if(ret==0)
  61. return;
  62. force_sigsegv(sig,tsk);
  63. }

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

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

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

[plain]view plaincopyprint?

  1. unsignedlonghandler=(unsignedlong)ka->sa.sa_handler;
  2. regs->ARM_pc=handler;

3.当信号的用户态处理函数执行结束时,需要再次进入内核态,还原用户态堆栈,并且修改pt_regs中的pc,保证将来能够按照正常的方式返回用户态。我们知道进程要主动进入内核态只有通过系统调用,出发异常等方法,为此内核专门提供了一个系统调用sys_sigreturn()(还有一个sys_rt_sigreturn()),但是如何调用sys_sigreturn()呢?强制安装信号处理函数最后必须调用一个sigreturn()不是一个好办法,因为不了解内核的程序员会对这个限制感到不解,为此程序员常常忘记在它们的信号处理函数的末尾调用sigreturn(),如果真是这样,arm-linux-gcc也检测不出这个错误。为此,内核修改regs的ARM_lr值,:

[plain]view plaincopyprint?

  1. regs->ARM_lr=retcode;

当用户态信号处理函数运行结束时,会从lr取出返回地址,因此内核在构建临时regs时,会把上面这段代码的入口地址保存在lr,这样当信号处理完成后,就会顺利的通过系统调用sys_sigreturn()进入内核。

4.当通过构造的sys_sigreturn()返回到内核态之后,内核需要顺利的返回到用户态执行原来的代码(信号处理前应该返回的用户空间状态),但是此时进入内核空间的pt_regs上下文是通过sys_sigreturn()构造的,而最初的内核堆栈中的pt_regs上下文在第一次返回用户空间执行信号处理函数时,就已经被“销毁”了(内核态堆栈的pt_regs上下文在中断

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top