linux内核中的信号机制--信号处理
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?
- /*
- *OK,wereinvokingahandler
- */
- staticvoid
- handle_signal(unsignedlongsig,structk_sigaction*ka,
- siginfo_t*info,sigset_t*oldset,
- structpt_regs*regs,intsyscall)
- {
- structthread_info*thread=current_thread_info();
- structtask_struct*tsk=current;
- intusig=sig;
- intret;
- /*
- *Ifwewerefromasystemcall,checkforsystemcallrestarting...
- */
- 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);
- }
- }
- /*
- *translatethesignal
- */
- if(usig<32&&thread->exec_domain&&thread->exec_domain->signal_invmap)
- usig=thread->exec_domain->signal_invmap[usig];
- /*
- *Setupthestackframe//设置栈帧
- */
- if(ka->sa.sa_flags&SA_SIGINFO)
- ret=setup_rt_frame(usig,ka,info,oldset,regs);
- else
- ret=setup_frame(usig,ka,oldset,regs);
- /*
- *Checkthattheresultingregistersareactuallysane.
- */
- ret|=!valid_user_regs(regs);
- /*
- *Blockthesignalifwewereunsuccessful.
- */
- 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处开始执行了。
[plain]view plaincopyprint?
- unsignedlonghandler=(unsignedlong)ka->sa.sa_handler;
- 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?
- regs->ARM_lr=retcode;
当用户态信号处理函数运行结束时,会从lr取出返回地址,因此内核在构建临时regs时,会把上面这段代码的入口地址保存在lr,这样当信号处理完成后,就会顺利的通过系统调用sys_sigreturn()进入内核。
4.当通过构造的sys_sigreturn()返回到内核态之后,内核需要顺利的返回到用户态执行原来的代码(信号处理前应该返回的用户空间状态),但是此时进入内核空间的pt_regs上下文是通过sys_sigreturn()构造的,而最初的内核堆栈中的pt_regs上下文在第一次返回用户空间执行信号处理函数时,就已经被“销毁”了(内核态堆栈的pt_regs上下文在中断
linux内核信号机制信号处 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)