C语言 信号处理程序中 printf 的使用问题

C语言中,在信号处理程序中使用 printf 或其他非重入函数是不推荐的,因为信号处理程序必须是异步信号安全的。避免使用可能与堆内存交互、使用锁或依赖内部缓冲区的函数。如果需要输出,优先使用低级函数(例如 write),或将工作转移到主程序中完成。

1、要避免在信号处理程序中使用 printf的原因

许多C标准库函数(例如 printf)不是重入的。这意味着它们可能依赖共享状态(例如全局或静态变量)。如果在信号处理程序中中断了这些函数的执行,可能会导致未定义行为。信号处理程序中调用的函数必须是异步信号安全的。像 printf 这样的函数可能会分配内存、加锁或使用其他资源,这些在信号处理上下文中可能不安全。

2、避免在信号处理程序中使用 printf 的常见方法

1) 使用 write 替代 printf

write 系统调用是异步信号安全的,可以直接输出到文件描述符(例如 STDOUT_FILENO)。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void signal_handler(int signum) {
    const char msg[] = "Signal received!\n";
    write(STDOUT_FILENO, msg, sizeof(msg) - 1); // 使用 write 替代 printf
}

int main() {
    signal(SIGINT, signal_handler);

    while (1) {
        // 主程序循环
    }

    return 0;
}

2)设置标志位,让主程序处理输出

与其直接在信号处理程序中打印信息,不如设置一个标志位,让主程序负责打印。

#include <stdio.h>
#include <signal.h>
#include <stdbool.h>

volatile sig_atomic_t signal_flag = 0;

void signal_handler(int signum) {
    signal_flag = 1; // 设置标志位
}

int main() {
    signal(SIGINT, signal_handler);

    while (1) {
        if (signal_flag) {
            printf("Signal received!\n");
            signal_flag = 0; // 重置标志位
        }
    }

    return 0;
}

3)将日志写入文件描述符

可以使用 write 函数将日志直接写入文件描述符,而不是使用 printf

#include <signal.h>
#include <unistd.h>
#include <fcntl.h>

void signal_handler(int signum) {
    const char msg[] = "Signal received!\n";
    int fd = open("signal.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd >= 0) {
        write(fd, msg, sizeof(msg) - 1);
        close(fd);
    }
}

int main() {
    signal(SIGINT, signal_handler);

    while (1) {
        // 主程序循环
    }

    return 0;
}

4)在信号处理程序中尽量减少工作

信号处理程序中应该执行最少的必要工作,例如设置标志或写入小的固定消息,然后迅速返回。任何复杂操作都应交给主程序处理。

推荐阅读
cjavapy编程之路首页