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

本文详细解析了内核系统调用与中断处理程序之间的交互过程,特别是do_signal()函数如何通过修改中断返回环境,使得中断能够直接跳转到用户态的信号处理函数,并在完成信号处理后恢复到原始系统调用的执行路径。文章借助图表直观展示了这一复杂过程,揭示了操作系统底层技术的巧妙之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         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的地址去,也就是原先保存的下一条语句。感觉还是很巧妙的!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值