You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在捕获信号时让阻塞的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 07:30:00