MST
星途 面试题库

面试题:C语言信号捕获中的竞态条件处理

在C语言的信号处理程序中,存在竞态条件的风险。假设你正在编写一个程序,主线程进行文件读写操作,同时设置了SIGTERM信号的捕获处理函数,处理函数也会进行文件操作。请分析可能出现的竞态条件,并说明如何避免这种情况,用代码示例加以说明。
37.6万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

可能出现的竞态条件分析

  1. 资源竞争:主线程和信号处理函数都对文件进行操作,可能导致文件读写冲突。例如,主线程正在写入文件,此时SIGTERM信号到达,信号处理函数开始写入文件,可能会破坏主线程正在写入的数据结构或导致数据混乱。
  2. 重入问题:如果信号处理函数中调用了不可重入函数(如标准I/O函数,它们通常使用全局状态),而主线程也在调用这些函数,可能会导致数据不一致。

避免竞态条件的方法

  1. 使用信号掩码:在主线程执行关键文件操作时,屏蔽SIGTERM信号,操作完成后再恢复信号处理。
  2. 使用线程安全的函数:尽量使用可重入函数进行文件操作,如openreadwrite等系统调用函数,而避免使用标准I/O库中不可重入的函数如fprintf等。

代码示例

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

// 全局变量,记录信号是否被捕获
volatile sig_atomic_t sig_caught = 0;

// SIGTERM信号处理函数
void sigterm_handler(int signum) {
    sig_caught = 1;
}

int main() {
    // 设置SIGTERM信号的处理函数
    struct sigaction sa;
    sa.sa_handler = sigterm_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGTERM, &sa, NULL);

    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 主线程文件操作
    const char *message = "Hello, world!\n";
    while (1) {
        // 屏蔽SIGTERM信号
        sigset_t block_mask;
        sigemptyset(&block_mask);
        sigaddset(&block_mask, SIGTERM);
        sigprocmask(SIG_BLOCK, &block_mask, NULL);

        // 进行文件写入操作
        ssize_t bytes_written = write(fd, message, strlen(message));
        if (bytes_written == -1) {
            perror("write");
        }

        // 恢复SIGTERM信号处理
        sigprocmask(SIG_UNBLOCK, &block_mask, NULL);

        // 检查是否捕获到SIGTERM信号
        if (sig_caught) {
            break;
        }

        // 模拟其他工作
        sleep(1);
    }

    close(fd);
    return 0;
}

在上述代码中:

  1. 信号处理函数sigterm_handler只是简单设置一个标志sig_caught,不进行文件操作,避免了直接的资源竞争。
  2. 主线程:在进行文件写入操作前,屏蔽SIGTERM信号,操作完成后恢复信号处理,防止信号处理函数打断文件操作。同时,使用系统调用write而不是标准I/O函数,以保证可重入性。