信号是进程之间通信的一种方式。它包括3部分操作:
1.设置信号处理函数。系统调用signal。内核调用sys_signal(),设置当前进程对某信号的处理函数。
2.发送信号.系统调用kill。内核调用sys_kill()。向目标进程发送信号。3.接收并处理信号。目标进程调用do_signal()处理信号。
从用户态的角度看,目标进程在执行用户态的代码时突然“中断”,转而去执行对应的信号处理函数(同样在用户态)。等到信号处理函数执行完后,又从原来被中断的代码开始执行。
如何达到这样的效果呢?由前面的几种内核的伪装现场的手段,我们可以猜出它这次使用的手段。比如,要让目标进程执行信号处理函数,在内核态中当然不可能直接调用,但是可以通过设置pt_regs中的eip来达到这种效果。但是,要使目标进程在执行完信号处理函数后,又恢复到被中断的现场继续执行,那得花些技巧。不过,不外乎设置堆栈。这一次还包括了用户态堆栈。由于恢复的任务比较艰巨,系统干脆提供了一个系统调用sigreturn 。
既然内核希望用户在执行完信号处理函数后,调用sigreturn。接下去的思路就比较简单了。就是先把用户态的eip设置为signal_handler(通过修改pt_regs中的eip来实现),然后把堆栈中的返回地址改成调用sigreturn的一段代码的入口(当然原来的返回地址也还是要保存的)并且把相关参数“压入”用户态堆栈。
这样,在源进程发送信号后不久,目标进程被调度到,然后执行到do_signal。对信号一一作处理。调用顺序:
do_signal()->handle_signal()->setup_rt_frame()用来设置用户态堆栈。
我们看看这个函数做了些啥?
447 frame = get_sigframe(ka, regs, sizeof(*frame));
struct rt_sigframe __user *frame是内核在用户态的堆栈上新分配的一个数据结构。
1.设置信号处理函数。系统调用signal。内核调用sys_signal(),设置当前进程对某信号的处理函数。
2.发送信号.系统调用kill。内核调用sys_kill()。向目标进程发送信号。3.接收并处理信号。目标进程调用do_signal()处理信号。
从用户态的角度看,目标进程在执行用户态的代码时突然“中断”,转而去执行对应的信号处理函数(同样在用户态)。等到信号处理函数执行完后,又从原来被中断的代码开始执行。
如何达到这样的效果呢?由前面的几种内核的伪装现场的手段,我们可以猜出它这次使用的手段。比如,要让目标进程执行信号处理函数,在内核态中当然不可能直接调用,但是可以通过设置pt_regs中的eip来达到这种效果。但是,要使目标进程在执行完信号处理函数后,又恢复到被中断的现场继续执行,那得花些技巧。不过,不外乎设置堆栈。这一次还包括了用户态堆栈。由于恢复的任务比较艰巨,系统干脆提供了一个系统调用sigreturn 。
既然内核希望用户在执行完信号处理函数后,调用sigreturn。接下去的思路就比较简单了。就是先把用户态的eip设置为signal_handler(通过修改pt_regs中的eip来实现),然后把堆栈中的返回地址改成调用sigreturn的一段代码的入口(当然原来的返回地址也还是要保存的)并且把相关参数“压入”用户态堆栈。
这样,在源进程发送信号后不久,目标进程被调度到,然后执行到do_signal。对信号一一作处理。调用顺序:
do_signal()->handle_signal()->setup_rt_frame()用来设置用户态堆栈。
我们看看这个函数做了些啥?
447 frame = get_sigframe(ka, regs, sizeof(*frame));
struct rt_sigframe __user *frame是内核在用户态的堆栈上新分配的一个数据结构。
011 struct rt_sigframe
012 {
013 &

本文详细介绍了Linux内核中的信号通信,特别是struct rt_sigframe的结构及其在信号处理中的作用。通过设置用户进程堆栈,内核使得信号处理程序能够执行,并在完成后使用__kernel_rt_sigreturn进行“善后”。在信号处理结束后,进程状态得以恢复,继续执行之前的代码。
1884

被折叠的 条评论
为什么被折叠?



