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

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

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

CPU architecture:ARM920T

Author:ce123(http://blog.csdn.net/ce123)

当进程被调度时,会调用do_notify_resume()来处理信号队列中的信号。信号处理主要就是调用sighand_struct结构中对应的信号处理函数。do_notify_resume()(arch/arm/kernel/signal.c)函数的定义如下:

[plain]view plaincopyprint?

  1. asmlinkagevoid
  2. do_notify_resume(structpt_regs*regs,unsignedintthread_flags,intsyscall)
  3. {
  4. if(thread_flags&_TIF_SIGPENDING)
  5. do_signal(¤t->blocked,regs,syscall);
  6. }

_TIF_SIGPENDING标志是在signal_wake_up()函数中设置的,检查该标志后,接下来就调用do_signal()函数,我们来看看do_signal()(arch/arm/kernel/signal.c)的具体定义:

[plain]view plaincopyprint?

  1. /*
  2. *Notethatinitisaspecialprocess:itdoesntgetsignalsitdoesnt
  3. *wanttohandle.ThusyoucannotkillinitevenwithaSIGKILLevenby
  4. *mistake.
  5. *
  6. *Notethatwegothroughthesignalstwice:oncetocheckthesignalsthat
  7. *thekernelcanhandle,andthenwebuildalltheuser-levelsignalhandling
  8. *stack-framesinonegoafterthat.
  9. */
  10. staticintdo_signal(sigset_t*oldset,structpt_regs*regs,intsyscall)
  11. {
  12. structk_sigactionka;
  13. siginfo_tinfo;
  14. intsignr;
  15. /*
  16. *Wewantthecommoncasetogofast,which
  17. *iswhywemayincertaincasesgetherefrom
  18. *kernelmode.Justreturnwithoutdoinganything
  19. *ifso.
  20. */
  21. if(!user_mode(regs))//regs保存的是进入内核态之前的寄存器现场,必须为用户模式,否则直接返回
  22. return0;
  23. if(try_to_freeze())
  24. gotono_signal;
  25. if(current->ptrace&PT_SINGLESTEP)
  26. ptrace_cancel_bpt(current);//和调试相关,我们在后面的文章中会具体分析
  27. signr=get_signal_to_deliver(&info,&ka,regs,NULL);//取出等待处理的信号
  28. if(signr>0){
  29. handle_signal(signr,&ka,&info,oldset,regs,syscall);//处理信号
  30. if(current->ptrace&PT_SINGLESTEP)
  31. ptrace_set_bpt(current);
  32. return1;
  33. }
  34. no_signal:
  35. /*
  36. *Nosignaltodelivertotheprocess-restartthesyscall.
  37. */
  38. if(syscall){
  39. if(regs->ARM_r0==-ERESTART_RESTARTBLOCK){
  40. if(thumb_mode(regs)){
  41. regs->ARM_r7=__NR_restart_syscall;
  42. regs->ARM_pc-=2;
  43. }else{
  44. u32__user*usp;
  45. regs->ARM_sp-=12;
  46. usp=(u32__user*)regs->ARM_sp;
  47. put_user(regs->ARM_pc,&usp[0]);
  48. /*swi__NR_restart_syscall*/
  49. put_user(0xef000000|__NR_restart_syscall,&usp[1]);
  50. /*ldrpc,[sp],#12*/
  51. put_user(0xe49df00c,&usp[2]);
  52. flush_icache_range((unsignedlong)usp,
  53. (unsignedlong)(usp+3));
  54. regs->ARM_pc=regs->ARM_sp+4;
  55. }
  56. }
  57. if(regs->ARM_r0==-ERESTARTNOHAND||
  58. regs->ARM_r0==-ERESTARTSYS||
  59. regs->ARM_r0==-ERESTARTNOINTR){
  60. restart_syscall(regs);
  61. }
  62. }
  63. if(current->ptrace&PT_SINGLESTEP)
  64. ptrace_set_bpt(current);
  65. return0;
  66. }

执行do_signal()函数时,进程一定处于内核空间,通常进程只有通过中断或者系统调用才能进入内核空间,regs保存着系统调用或者中断时的现场。user_mode()根据regs中的cpsr寄存器来判断是中断现场环境还是用户态环境。如果不是用户态环境,就不对信号进行任何处理,直接从do_signal()函数返回。

如果user_mode()函数发现regs的现场是内核态,那就意味着这不是一次系统调用的返回,也不是一次普通的中断返回,而是一次嵌套中断返回(或者在系统调用过程中发生了中断)。此时大概的执行路径应该是这样的:假设进场现在运行在用户态,此时发生一次中断,进场进入内核态(此时user_mode(regs)返回1,意味着中断现场是用户态。),此后在中断返回前,发生了一个更高优先级的中断,于是CPU开始执行高优先级的处理函数(此时user_mode(regs)返回0,意味着中断现场是在内核态)。当高优先级中断处理结束后,在它返回时,是不应该处理信号的,因为信号的优先级比中断的优先级低。在这种情况下,对信号的处理将会延迟到低优先级中断处理结束之后。相对于Windows内核来说,尽管linux内核中没有一组显式的操作函数来实现这一系列的优先级管理方案,但是linux内核和Windows内核都使用了同样的机制,优先级关系为:高优先级中断->低优先级中断->软中断(类似Windows内中的DPC)->信号(类似Windows内核中的APC)->进程运行。

如果user_mode(regs)返回1,接下来会执行(中间略去一下和本文关系不大的代码)get_signal_to_deliver(),这个函数从当前进程的信号队列(保存Private Signal Queue和Shared Signal Queue)取出等待处理的信号(调用dequeue_s

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

网站地图

Top