MST

星途 面试题库

面试题:C语言信号处理中的竞态条件及解决

在Linux C语言编写信号处理程序时,常常会遇到竞态条件问题。请举例说明一种可能出现竞态条件的场景,并阐述如何利用sigaction函数及相关机制来解决该竞态条件,给出相应的代码示例。
31.4万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

竞态条件场景举例

假设在程序中,有一个全局变量 flag,主程序在检查 flag 后根据其值执行一些操作,同时有一个信号处理函数会修改 flag。如果在主程序检查 flag 之后,但还没来得及执行相应操作时,信号处理函数修改了 flag,就会出现竞态条件。

利用 sigaction 函数及相关机制解决竞态条件

  1. 使用 sigaction 函数设置信号处理函数时,利用 sa_flags 字段设置 SA_RESTART 标志:这样可以保证被信号中断的系统调用(如 readwrite 等)能够自动重启,避免因信号中断导致系统调用提前返回错误。
  2. 使用信号掩码(Signal Mask):在主程序关键代码段中,阻塞信号,避免信号处理函数在不适当的时候被调用,从而防止竞态条件。处理完关键代码段后,再解除信号阻塞。

代码示例

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

volatile sig_atomic_t flag = 0;

void signal_handler(int signum) {
    flag = 1;
}

int main() {
    struct sigaction sa;

    // 初始化 sigaction 结构体
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;

    // 设置信号处理函数
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }

    sigset_t block_mask, old_mask;
    // 初始化信号集
    sigemptyset(&block_mask);
    sigaddset(&block_mask, SIGINT);

    // 阻塞 SIGINT 信号
    if (sigprocmask(SIG_BLOCK, &block_mask, &old_mask) == -1) {
        perror("sigprocmask");
        return 1;
    }

    // 模拟一些关键操作
    for (int i = 0; i < 5; i++) {
        printf("Working...\n");
        sleep(1);
    }

    // 解除 SIGINT 信号阻塞
    if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
        perror("sigprocmask");
        return 1;
    }

    if (flag) {
        printf("Flag was set by signal handler.\n");
    } else {
        printf("Flag was not set.\n");
    }

    return 0;
}

在上述代码中:

  1. 信号处理函数 signal_handler:负责设置 flag 变量。
  2. sigaction 设置:设置 SIGINT 信号的处理函数,并设置 SA_RESTART 标志。
  3. 信号掩码操作:在关键操作(模拟工作循环)期间阻塞 SIGINT 信号,完成后恢复信号掩码,避免竞态条件。