Linux 信号处理:可重入函数与安全实践
一、什么是可重入函数?核心定义与形象类比
1. 官方定义
可重入函数(Reentrant Function)指:函数在执行过程中被异步打断(如信号、中断、多线程调度),再次调用(重入)后,原执行流程和新执行流程都能正确完成,结果不受打断影响。
2. 形象类比
- 可重入函数:像自动售货机 —— 你投币买水到一半被人打断去买零食,回来继续投币,售货机仍能正确给你水(逻辑独立,不依赖'半完成'状态)。
- 不可重入函数:像手工记账本 —— 你记到一半(写了'收入 100'但没写'元')被打断去记另一笔账,回来忘了之前的进度,账本直接混乱(依赖全局状态,操作非原子)。
3. 与线程安全的区别
很多人会混淆'可重入'与'线程安全',但二者是完全不同的概念:
| 对比维度 | 可重入函数 | 线程安全函数 |
|---|---|---|
| 核心场景 | 单线程中被异步打断后重入 | 多线程并发调用 |
| 依赖资源 | 仅使用局部变量 / 函数参数(私有) | 可能使用共享资源(需同步机制) |
| 实现方式 | 无状态设计,不依赖全局资源 | 可能通过锁、原子操作保护共享资源 |
| 关系逻辑 | 可重入未必线程安全,线程安全未必可重入 | —— |
示例:fprintf() 是线程安全的(内部有锁保护),但不可重入 —— 若信号处理函数打断主程序的 fprintf(),再调用 fprintf() 会导致全局输出缓冲区错乱。
二、不可重入函数的'致命陷阱':为什么不能在信号处理中使用?
不可重入函数的核心问题是'依赖非私有资源'(全局变量、静态变量、共享内存等)或'执行非原子操作'。在信号处理场景中,这种依赖会被无限放大,因为信号的触发时机完全不可预测。
典型案例:strtok() 函数的重入灾难
strtok() 是 C 标准库中的字符串分割函数,其不可重入的根源是使用静态变量保存'上次分割的位置':
#include <stdio.h>
#include <string.h>
#include <signal.h>
void handle_sigint(int signum) {
char str[] = "x,y,z";
// 信号处理函数中调用 strtok(),覆盖静态变量
char *p = strtok(str, ",");
printf(, p);
}
{
signal(SIGINT, handle_sigint);
str[] = ;
*p = strtok(str, );
(, p);
();
pause();
p = strtok(, );
(, p);
;
}

