Linux 进程信号(上):信号产生方式与闹钟机制
1. 理解信号是什么,为什么要有?
1.1 信号的本质与分类
信号(Signal)是操作系统向进程发送的通知消息。它本质上是一种异步的通信机制。所谓异步,意味着通知的到来与进程当前的执行状态不同步——就像你正在打游戏,外卖小哥敲门送饭,这个敲门声就是一个异步通知,你不需要停下来等外卖员准备好再开始游戏。
在 Linux 中,信号分为普通信号和实时信号。通过 kill -l 可以查看系统支持的信号列表。常用的主要是编号 1 到 31 的普通信号,以及 34 到 64 的实时信号(带 RT 后缀)。我们日常开发中主要关注前 31 个。
1.2 信号的处理机制
进程收到信号后,不会立即处理,而是根据预设的策略来决定行为。这就像红绿灯:红灯亮时,默认是停车;如果你选择忽略,可能继续行驶;或者自定义一个动作,比如跳舞。具体来说,信号处理有三种默认策略:
- 默认(Default):如终止进程、忽略或 Core Dump。
- 忽略(Ignore):明确丢弃该信号。
- 自定义(Custom):注册信号处理函数(Signal Handler)来接管逻辑。
由于信号可能随时到达,而进程可能正忙(比如在处理高优先级任务),所以内核必须为每个进程提供临时保存信号的能力。这个能力通常体现在进程的 PCB(进程控制块)中的位图里,只有操作系统有权修改这些内核数据结构。
1.3 信号的识别与存储
信号在代码中通常用宏定义表示,如 SIGINT、SIGKILL 等,对应特定的整数编号。头文件 <signal.h> 中定义了这些常量。为了高效管理多个信号是否发生,内核使用位图(Bitmap)记录,每一位代表一个信号编号,值为 1 表示已到达,0 表示未到达。
1.4 如何自定义捕捉信号?
我们可以通过 signal() 函数来设置特定信号的处理动作。例如,将 2 号信号 SIGINT(通常由 Ctrl+C 触发)的处理方式从默认的终止改为自定义函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void handler(int sig) {
printf("Caught signal %d\n", sig);
}
int main() {
// 注册信号处理函数,当 SIGINT 发生时调用 handler
signal(SIGINT, handler);
while(1) {
sleep(1);
}
;
}


