Linux 进程信号(上):信号产生方式和闹钟
1. 理解信号是什么,为什么要有?
1.1 信号的本质
信号是一种异步通知机制。简单来说,就是事件通知,但通知的到来与当前执行流不同步。
举个例子,你点了外卖然后打游戏。外卖小哥送货上门敲门是一个通知,说明外卖到了。你和外卖小哥是不同步的——你做你的游戏,他做他的配送。这就是异步关系。如果是同步,你得等他送回来再继续打游戏。
操作系统给目标进程发送信号,进程天然能够识别。因为进程和信号都是程序员写的代码,进程相当于被'教育'过的人,已经知道怎么处理信号了。
普通信号和实时信号
用 kill -l 可以查看系统中的信号列表。常用的只有 131 号,3464 号是实时信号(带 RT)。前 31 个是普通信号,我们主要关注这些。
1.2 生活中的信号与结论
生活中有很多信号,比如红绿灯、电话铃声等。这些信号产生的几乎都是异步的。
人之所以能识别信号,是因为经过教育,知道了信号对应的处理动作。操作系统同理,进程内核程序员设计好了信号的处理逻辑。
收到信号会立即处理吗?
不会立即处理。有时候中断不了,或者正在做优先级更高的事情。既然不一定立即处理,进程就必须有临时保存信号的能力。
这就好比外卖员打电话叫拿外卖,如果你答应去取却没保存这个信息,打着打着就忘了。所以,信号不会被立即处理,进程必须有临时保存信号的能力!
信号的处理方式
- 默认:按系统预设行为处理(如红灯停)。
- 忽略:直接无视该信号(如不想拿外卖)。
- 自定义:编写自己的处理函数(如拿完外卖直接丢了)。
后续我们将这三种统称为信号捕捉。
1.3 信号具体化
信号编号与宏
信号是有编号的,没有 0 号信号。大写的名字(如 SIGINT)是宏定义。
头文件通常包含 <signal.h>。内核中也可以查看相关定义。131 是普通信号,3464 是实时信号。
信号的保存
进程需要特定的数据类型来保存信号。通常使用位图(bitmap),比特位的位置表示信号编号,内容表示是否收到对应信号。这个位图位于进程的 PCB(进程控制块)中。
谁有资格修改内核数据结构中的字段?只有操作系统。用户通过系统调用让操作系统向目标进程发送信号。
1.4 为什么要有信号?
Linux 需要信号,是为了提供一种异步且轻量级的进程间通信与事件通知机制。它能让操作系统立即打断进程的正常执行流,通知其发生了突发状况,如用户中断(Ctrl+C)、硬件异常、定时器到期或子进程退出。信号弥补了管道或共享内存等同步通信方式的不足,是实现进程控制、异常处理和系统紧急交互的核心基础。
1.5 如何自定义捕捉信号?
我们可以使用 signal 函数对指定信号进行自定义处理。这是一个回调函数的设定。
挑一个信号,比如 2 号信号 SIGINT(对应 Ctrl+C)。默认情况下,收到 SIGINT 会终止进程。如果我们想让它不终止,就需要自定义处理。
#include <stdio.h>
#include <signal.h>
void handler(int sig) {
(, sig);
}
{
signal(SIGINT, handler);
() {
sleep();
}
;
}


