面试题答案
一键面试竞态条件场景举例
假设在程序中,有一个全局变量 flag
,主程序在检查 flag
后根据其值执行一些操作,同时有一个信号处理函数会修改 flag
。如果在主程序检查 flag
之后,但还没来得及执行相应操作时,信号处理函数修改了 flag
,就会出现竞态条件。
利用 sigaction
函数及相关机制解决竞态条件
- 使用
sigaction
函数设置信号处理函数时,利用sa_flags
字段设置SA_RESTART
标志:这样可以保证被信号中断的系统调用(如read
、write
等)能够自动重启,避免因信号中断导致系统调用提前返回错误。 - 使用信号掩码(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;
}
在上述代码中:
- 信号处理函数
signal_handler
:负责设置flag
变量。 sigaction
设置:设置SIGINT
信号的处理函数,并设置SA_RESTART
标志。- 信号掩码操作:在关键操作(模拟工作循环)期间阻塞
SIGINT
信号,完成后恢复信号掩码,避免竞态条件。