【Linux】进程信号
在 Linux 编程世界中,信号(Signal)机制是一种极为关键的异步通信方式。它不仅是进程间通信的基础之一,也是操作系统与进程互动的重要通道。本文将从生活中的类比引入,再逐步深入技术细节,帮助读者系统性地掌握信号的概念、产生、处理、阻塞机制及相关编程实践。
一、信号是什么?
1.1 从生活场景谈起
你网购了几件商品,快递员在楼下时通知你取件,但你可能正忙着打游戏,五分钟后才下去。这段"知道快递来了但还没取"的时间,就像进程接收到信号但尚未处理的"未决状态"。
取快递的方式也多样:
- 默认处理:自己打开快递使用;
- 自定义处理:送人;
- 忽略处理:扔床头不理。
这种过程是异步的,快递(信号)何时到来无法预知,只能在收到通知后"在合适的时机"处理。
1.2 从技术场景看信号
举例说明,当我们在 Shell 中执行一个前台程序时按下 Ctrl+C
:
- 键盘产生一个中断;
- 操作系统捕捉中断并发送 SIGINT 信号;
- 前台进程收到信号并终止。
二、信号基础知识
2.1 信号的定义
信号是进程间用于事件通知的一种机制,本质上属于"软中断",用于通知目标进程发生了某个事件。
2.2 信号种类与查看方式
使用命令 kill -l
可以查看系统支持的所有信号。信号通常由宏定义标识(如 SIGINT
是信号编号 2),可在 signal.h
中查到。
三、信号的产生方式
3.1 终端按键产生
如:
Ctrl+C
→SIGINT
,默认终止进程;Ctrl+\
→SIGQUIT
,默认终止并生成 core dump。
3.2 调用系统函数发送
常用函数:
kill(pid, signo)
:向指定进程发送信号;raise(signo)
:向当前进程自身发送信号;abort()
:强制终止自身进程;alarm(seconds)
:定时发出SIGALRM
。
3.3 软件条件触发
如:
- 写入已关闭的管道 →
SIGPIPE
; - 调用
alarm()
→ 在指定秒数后触发SIGALRM
。
3.4 硬件异常触发
如:
- 除零 →
SIGFPE
; - 访问非法地址 →
SIGSEGV
。
四、信号的处理方式
信号的处理动作有三种:
- 忽略;
- 执行默认动作;
- 自定义处理函数(捕捉)。
示例代码:
c
signal(SIGINT, handler); // 捕捉SIGINT
4.1 Core Dump 简介
当进程异常终止时,将内存内容保存为文件 core
,可用于调试分析。需使用 ulimit -c
命令允许生成 core 文件。
五、信号的捕捉机制
5.1 内核中的捕捉流程
信号捕捉的本质是注册一个回调函数,在信号递达时中断当前流程并调用该函数。处理完信号后,再恢复原有上下文继续执行。
5.2 使用 sigaction
sigaction
提供更安全和灵活的信号处理方式,支持设置额外屏蔽信号、恢复上下文等操作。
示例用法:
c
struct sigaction act;
act.sa_handler = handler;
sigaction(SIGINT, &act, NULL);
六、信号的阻塞与未决
6.1 概念区分
- 阻塞(Block):信号被暂时挂起,不立即处理;
- 未决(Pending):信号已产生,但因被阻塞暂未递达;
- 递达(Delivery):信号处理动作被实际执行。
6.2 内核实现方式
信号在 PCB 中用两个标志位表示:
- 是否阻塞;
- 是否未决。
如果信号被阻塞,系统仅设置其未决标志;待解阻塞后再处理。
七、信号集与相关函数
信号集类型 sigset_t
用于描述多个信号状态。
常用函数包括:
c
sigemptyset(&set); // 初始化为空
sigfillset(&set); // 所有信号
sigaddset(&set, sig);
sigdelset(&set, sig);
sigismember(&set, sig); // 是否包含某信号
7.1 操作信号屏蔽字:sigprocmask
用于读取/更改当前进程的阻塞信号集。
c
sigprocmask(SIG_BLOCK, &set, NULL);
7.2 查询未决信号:sigpending
c
sigpending(&pending_set);
八、可重入函数与信号安全
在信号处理函数中使用的代码必须是可重入 的。不可重入函数(如 malloc
, printf
)在信号处理时会引发数据错乱。
九、关键字 volatile 的作用
当变量被 volatile
修饰时,编译器不会优化其访问操作,确保对该变量的访问都在真实内存中进行,防止因缓存导致的判断失效。
c
volatile int flag = 0;
避免如下问题:
c
while (!flag); // 如果没有volatile修饰,可能永远不退出循环
十、SIGCHLD 信号与子进程管理
SIGCHLD
信号在子进程终止时发送给父进程。
10.1 信号处理函数方式
使用 signal(SIGCHLD, handler)
捕捉信号,在处理函数中调用 waitpid
回收子进程资源,避免僵尸进程。
10.2 SIG_IGN 简化处理
将 SIGCHLD
的处理动作设为 SIG_IGN
可自动回收子进程(Linux 专属行为)。
结语:
信号机制的设计体现了操作系统对异步事件控制的精巧思路。通过信号,进程得以与系统高效交互,也为开发者提供了强大的编程工具。