信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件),是硬件中断的软件模拟(软中断),它是 UNIX 进程通信最古老的方法。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程利用它来通知用户空间进程发生了哪些系统事件。它可以在任何时候发给某一进程,而无需知道该进程的状态。
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它为止;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。一个完整的信号生命周期可以分为 3 个阶段,这 3 个阶段由 4 个事件来刻画:信号产生、信号在进程中注册、信号在进程中注销、执行信号处理函数。
信号的生命周期:
每个信号用一个整型常量宏表示,以 SIG 开头,如 SIGCHLD、SIGINT 等,它们在系统头文件 <signal.h> 中定义,也可以通过在 shell 下输入 kill -l 查看信号列表,或者输入 man 7 signal 查看更详细的说明。
信号的生成来自内核,让内核生成信号的请求来自 3 个地方。
- 用户:用户能够通过输入 Ctrl+C、Ctrl+\,或是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;
- 内核:当进程执行出错时,内核会给进程发送一个信号,例如,非法段存取(内存访问违规)、浮点数溢出等;
- 进程:一个进程可以通过系统调用 kill 给另一个进程发送信号,一个进程可以通过信号和另一个进程进行通信。
进程接收到信号以后,可以有如下 3 种选择进行处理:
- 接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如,连接到终端的进程,用户按下 Ctrl+C ,将导致内核向进程发送一个 SIGINT 信号,进程如果不对该信号做特殊处理,系统将采用默认方式处理该信号,即终止进程的执行;
- 忽略信号:进程可以通过代码,显式的忽略某个信号处理,但是某些信号是不能被忽略的;
- 捕捉信号并处理:进程可以事先注册信号处理函数,当接收到某个信号时,由信号处理函数自动捕捉并处理该信号。
有两个信号既不能被忽略也不能被捕捉,它们是 SIGKILL 和 SIGSTOP ,即进程接收到这两个信号后只能接受系统的默认处理,即终止进程。
常见信号:用 kill -l 产看信号。