您的位置:首页 > 运维架构 > Linux

对linux 0.11版本中do_signal()的理解

2014-05-11 21:55 288 查看
         do_signal()的做法蛮让人印象深刻的,在此记录下来。

do_signal()函数式内核系统调用(int 0x80)中断处理程序中对信号预处理程序。假如看过系统调用或者时钟中断的源码,会发现他们在退出的时候总会检查下信号位图,假如有的会就会调用do_signal,它会把信号的处理函数插入到用户程序堆栈中,然后修改中断返回的环境,直接返回到用户态的信号处理函数中先,再从用户态中跳转到原先执行系统调用的后一条语句中。如下图所示:

       


           再深入的去想想具体是如何实现的。我觉得下图很能帮助理解这个过程。

           


         在执行系统调用进入内核态的时候,CPU会在该进程的内核态堆栈上面压入用户程序的SS和ESP,EFLAGS,还有下一条指令所在的CS和EIP。要想让这个中断返回到信号处理函数去执行,就可以修改这个返回的EIP,但这个旧的EIP一定要保存下来,这样才能返回到原程序中,这里的做法是将用户堆栈向下延生,来保存old_eip,再保存些信号处理函数可能改变的寄存器的值和上一篇博文中提到的恢复函数sa_restoreer。我觉得这里一定要清楚是在保存在用户态堆栈上面,由于EIP和ESP都变了,所以要在内核态堆栈里面修改这些值。这样在系统调用要返回的时候执行一条iret就会把内核堆栈中的内容弹出,跳转到信号处理程序里面,因为eip已经被设置为指向信号处理程序了。在来看用户态堆栈的栈顶为sa_restorer的地址,所以在信号处理程序执行完,执行ret指令的时候就会返回到栈顶的地址中也就是sa_restorer的地址,在这个程序里面就需要把信号处理程序中改变的寄存器的值恢复成执行系统调用后一条语句的环境,其实就是一直调用pop,之后再执行ret,跳转到old_eip的地址去,也就是原先保存的下一条语句。感觉还是很巧妙的!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux内核 do_signal