面试题答案
一键面试可能出现的竞态条件分析
- 资源竞争:主线程和信号处理函数都对文件进行操作,可能导致文件读写冲突。例如,主线程正在写入文件,此时SIGTERM信号到达,信号处理函数开始写入文件,可能会破坏主线程正在写入的数据结构或导致数据混乱。
- 重入问题:如果信号处理函数中调用了不可重入函数(如标准I/O函数,它们通常使用全局状态),而主线程也在调用这些函数,可能会导致数据不一致。
避免竞态条件的方法
- 使用信号掩码:在主线程执行关键文件操作时,屏蔽SIGTERM信号,操作完成后再恢复信号处理。
- 使用线程安全的函数:尽量使用可重入函数进行文件操作,如
open
、read
、write
等系统调用函数,而避免使用标准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;
}
在上述代码中:
- 信号处理函数:
sigterm_handler
只是简单设置一个标志sig_caught
,不进行文件操作,避免了直接的资源竞争。 - 主线程:在进行文件写入操作前,屏蔽SIGTERM信号,操作完成后恢复信号处理,防止信号处理函数打断文件操作。同时,使用系统调用
write
而不是标准I/O函数,以保证可重入性。