可重入函数的特点
- 不依赖静态或全局数据:可重入函数在执行过程中不依赖于静态变量或全局变量的状态。如果函数依赖于静态或全局数据,当多个线程或信号处理程序同时调用该函数时,可能会导致数据竞争和未定义行为。
- 不调用不可重入函数:可重入函数不能调用不可重入的函数。例如,标准I/O库中的一些函数(如
printf
)通常不是可重入的,因为它们依赖于内部的静态数据结构。
- 具有局部状态:可重入函数应尽量使用局部变量来保存其执行过程中的状态。这些局部变量在函数的每次调用中都是独立的,不会相互干扰。
- 可中断执行:可重入函数能够在执行过程中被中断,然后在恢复执行时不会出现问题。这意味着函数的执行不应依赖于特定的执行顺序或不可中断的操作。
安全的信号处理函数示例
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
static volatile sig_atomic_t sigint_count = 0;
void sigint_handler(int signum) {
// 增加信号计数
sigint_count++;
// 打开文件用于追加写入
int fd = open("sigint_count.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);
if (fd == -1) {
// 错误处理
return;
}
char buffer[32];
// 将计数转换为字符串
snprintf(buffer, sizeof(buffer), "%d\n", sigint_count);
// 写入文件
ssize_t write_result = write(fd, buffer, strlen(buffer));
if (write_result == -1) {
// 错误处理
}
// 关闭文件
close(fd);
}
int main() {
// 注册信号处理函数
if (signal(SIGINT, sigint_handler) == SIG_ERR) {
perror("signal");
return 1;
}
while (1) {
// 主程序执行其他任务
sleep(1);
}
return 0;
}
示例中保证可重入性的方式
- 使用
sig_atomic_t
类型:sigint_count
变量被声明为volatile sig_atomic_t
类型。volatile
关键字告诉编译器该变量可能会在程序控制之外被修改(例如在信号处理函数中),sig_atomic_t
类型保证了对该变量的访问是原子操作,避免了数据竞争。
- 不调用不可重入函数:信号处理函数
sigint_handler
中没有调用不可重入的函数,如printf
。而是使用了可重入的open
、write
、close
等系统调用函数。
- 局部变量使用:函数中使用局部变量
buffer
来存储格式化后的计数数据,避免了使用静态或全局缓冲区,确保每次调用函数时状态独立。