Linux中的信号来自Unix,在发展了30多年之后,许多方面都没有发生太大的变化。信号可以由内核产生,也可以由用户进程产生,并由内核传送给特定的进程或线程(组),若这个进程定义了自己的信号处理程序,则调用这个程序去处理信号,否则则执行默认的程序或者忽略。
信号处理程序是用户态进程所定义的函数,并包含在用户态的代码段中。handle_signal()函数运行在内核态,而信号处理程序运行在用户态,这就意味着在当前进程恢复“正常”执行之前,它必须首先执行用户态的信号处理程序。此外,当内核打算恢复进程的正常执行时,内核态堆栈不再包含被中断程序的硬件上下文,因为每当从内核态向用户态转变时,内核态堆栈都被清空。
Linux所采用的解决方法是把保存在内核态堆栈中的硬件上下文拷贝到当前进程的用户态堆栈中。用户态堆栈也以这样的方式被修改,即当信号处理程序终止时,自动调用 sigreturn() 系统调用把这个硬件上下文拷贝回到内核态堆栈中,并恢复用户态堆栈中原来的内容,它看起来就好像下面这样。
信号处理程序首先做的工作是把一个称为“帧”的东西放在用户态堆栈中,帧中包含的一些列内容已经在图中说明,这里不再做过多的说明。
下面,让我们来看下另一张图,这张图说明了信号处理的执行过程。
首先,信号处理肯定是在内核态执行的,但是用户的信号处理程序是在用户进程的代码段中,所以,它的执行又是在用户态,所以,首先,需要从内核态进入用户态
之后执行用户自定义的信号处理程序,执行完之后,我们从第一张图可以看到,用户态堆栈的最低位置存放的是返回程序,所以,接下来会回到内核态,内核会恢复进程的硬件上下文,重新回到用户态执行。