linux信号处理handler2,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上下文在中断

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信号处理Linux系统的一个重要概念,用于处理进程间通信和异常情况。信号是由操作系统或其他进程发送给进程的通知,用于通知进程发生了某个事件或异常情况。信号可以被进程捕获和处理,也可以被忽略或使用默认处理方式。 在Linux信号可以由多种情况触发,比如按下CTRL+C键产生的SIGINT信号,非法内存访问产生的信号,硬件故障产生的信号,以及环境切换等。进程可以通过调用signal函数来注册信号处理函数,以捕获和处理特定的信号signal函数的原型如下: ```c typedef void (*sighandler)(int); sighandler signal(int signum, sighandler handler); ``` 其,signum是需要处理的信号编号,handler信号的处理函数。处理函数可以是用户自定义的函数,也可以是预定义的常量SIG_IGN表示忽略该信号,或者SIG_DFL表示使用默认的信号处理方式。 在信号处理函数,可以执行一些特定的操作来处理信号,比如打印日志、保存数据、发送信号给其他进程等。处理函数可以是空函数,表示仅仅捕获信号但不做任何处理。 需要注意的是,一个进程可以屏蔽掉大多数的信号,除了SIGSTOP和SIGKILL这两个信号是无法被屏蔽的。信号有优先级,当一个进程有多个未决信号时,内核将按照发送的顺序来递送信号。值越小的信号越先被递送。 在Linux,可以通过编写信号处理程序来处理不同的信号,并根据需要执行特定的操作。通过信号处理,可以实现进程间通信、优雅地关闭进程或处理异常情况等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值