如何在捕获信号时让阻塞的open()系统调用返回
问题根源
你遇到的现象是系统调用自动重启导致的。Linux下默认使用signal()函数注册信号处理函数时,会自动启用SA_RESTART标志,被信号中断的慢系统调用(比如阻塞的FIFO open、read、write等)会在信号处理函数执行完成后自动重启,所以你会看到open()继续阻塞。
解决方案
使用sigaction()函数注册信号处理,显式关闭SA_RESTART标志,这样被信号中断的系统调用会直接返回-1,同时errno被设置为EINTR,你就可以捕获这个状态做退出处理。
修改后的可运行代码如下:
#include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdbool.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // 用sig_atomic_t保证信号操作的原子性 static volatile sig_atomic_t end = 0; static void catchSignal(int signal) { (void)signal; const char *s = "Catched Signal\n"; write(STDERR_FILENO, s, strlen(s)); end = 1; } static int openFile(void) { int fd = open("./in", O_RDONLY); // 出错不直接退出,把状态返回上层处理 return fd; } int main() { struct sigaction sa = {0}; sa.sa_handler = catchSignal; sigemptyset(&sa.sa_mask); // 不设置SA_RESTART标志,禁止系统调用自动重启 sa.sa_flags = 0; if (sigaction(SIGTERM, &sa, NULL) < 0) { perror("can't set up signal handler"); return -1; } int fd = openFile(); if (fd < 0) { // 判断是不是被信号中断 if (errno == EINTR && end == 1) { puts("Open interrupted by signal, exiting"); return 0; } perror("can't open file"); exit(1); } while(end == 0) { puts("Still running"); usleep(300UL*1000); } puts("End now"); if(fd > 0) { close(fd); } return 0; }
额外说明
- 如果
open被信号中断后你还想重试,可以在判断errno == EINTR且end未被设置时,循环重试open操作即可 - 如果你需要兼容其他类Unix系统,
sigaction的行为是标准统一的,比signal函数更可靠,不会出现不同平台默认行为不一致的问题
内容的提问来源于stack exchange,提问作者12431234123412341234123
相关产品推荐
相关产品推荐

