Linux 信号是什么,为什么需要它
普通信号与实时信号
通过 kill -l 可以查看系统支持的信号列表。常用的主要是 131 号普通信号,以及 3464 号的实时信号(带 RT 后缀)。0 号信号不存在,实际可用的是 62 个。在大多数应用场景中,我们主要关注 1~31 号普通信号。
信号的本质
信号是一种异步通知机制。所谓异步,意味着通知的到来与当前进程的执行流不同步。比如点外卖时,你继续打游戏,外卖员敲门是一个通知,你们之间是异步关系;而如果你必须等外卖员回来再继续讲课,那就是同步关系。
操作系统给目标进程发送信号,进程天然能够识别并处理。这是因为进程和信号都是程序员设计的代码,内核已经内置了这种特性。收到信号后,进程不会立即处理,而是根据当前状态选择合适时机。因此,进程必须具备临时保存信号的能力,通常使用位图来记录哪些信号已到达。
这个位图存储在进程的 PCB(进程控制块)中。只有操作系统有资格修改内核数据结构中的字段,用户通过系统调用请求操作系统向目标进程写入信号。
为什么要有信号?
Linux 引入信号是为了提供一种异步且轻量级的进程间通信与事件通知机制。它能够打断进程的正常执行流,通知其发生了用户中断(如 Ctrl+C)、硬件异常(段错误)、定时器到期或子进程退出等突发状况。这弥补了管道或共享内存等同步通信方式的不足,是实现进程控制和异常处理的核心基础。
信号的默认行为
信号的处理方式主要有三种:
- 默认:执行系统预设动作(如终止进程)。
- 忽略:直接丢弃信号。
- 自定义:注册回调函数处理特定逻辑。
以红绿灯为例,红灯亮时自动停下是默认行为;无视红灯继续走是自定义;完全忽略红灯则是忽略行为。
如何自定义捕捉信号
我们可以使用 signal 函数对指定信号进行自定义处理。例如,针对 2 号信号 SIGINT(通常由 Ctrl+C 触发),默认行为是终止进程。如果我们希望捕获它并执行其他操作,可以设置回调函数。
#include <stdio.h>
#include <signal.h>
void handler(int sig) {
printf("Received signal: %d\n", sig);
}
int main() {
signal(SIGINT, handler);
while(1) {
sleep(1);
}
return 0;
}
运行后,即使按下 Ctrl+C,进程也不会立即退出,而是进入我们的 handler 函数。注意,9 号信号 SIGKILL 和 6 号信号 SIGABRT 比较特殊,前者不能被捕捉或忽略,后者虽然可以设置捕捉但最终仍会终止进程。
信号的产生方式
操作系统负责将信号写入目标进程的 PCB。无论何种方式,最终都必须经过操作系统。信号的产生主要有以下几种途径:


