【问题标题】:make open() return when signal is caught当信号被捕获时使 open() 返回
【发布时间】:2021-10-21 17:57:13
【问题描述】:

当我调用open("./fifo",O_RDONLY) 时,系统调用将阻塞,因为没有人在写入先进先出器./fifo。如果在此期间接收到没有信号处理程序的信号,则该过程立即结束。到现在为止还挺好。 但是当接收到具有信号处理程序的信号时,会执行信号处理程序并且open() 系统调用仍处于阻塞状态。

如何让open() 收到信号后返回?

我试图阻止信号,但它不起作用,因为 open() 没有 sigmask 参数,就像 pselect() 一样。使用O_NONBLOCK 也不起作用,因为无论是否有信号,open() 都会返回错误。删除信号处理程序也不好,因为我希望能够对信号做出反应。

我的测试代码:

#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>

static volatile bool 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);
  if(fd<0)
  {
    perror("can't open file");
    exit(1);
  }
  return fd;
}


int main()
{
  if(SIG_ERR==signal(SIGTERM,catchSignal))
  {
    perror("cant set up signal handler");
    return -1;
  }
  int fd = openFile();
  while(end==0)
  {
    puts("Still running");
    usleep(300UL*1000);
  }
  puts("End now");
  if(fd>0)
  {
    close(fd);
  }
  return 0;
}

【问题讨论】:

    标签: c linux signals


    【解决方案1】:

    signal() 函数存在问题,因为其实现历史具有不同的细节。根据its Linux manual page

    signal() 的唯一可移植用途是将信号的处置设置为SIG_DFLSIG_IGN。使用signal() 建立信号处理程序时的语义因系统而异(POSIX.1 明确允许这种变化); 请勿将其用于此目的

    (强调原文)

    您应该使用sigaction(),而不是signal()

      struct sigaction sa = { .sa_handler = catchSignal };
    
      if (SIG_ERR == sigaction(SIGTERM, &sa, NULL))
    

    请注意,struct sigaction 的字段中有sa_flags,这是一个位掩码,您可以使用它在历史上由不同版本的signal() 实现的各种行为中进行选择。特别是,如果你不包括SA_RESTART 标志,正如上面确实没有,那么你不应该看到系统调用在被信号中断时自动恢复(除了那些明确指定这样做的少数)。

    【讨论】:

      【解决方案2】:

      当您strace 程序时,您会看到 signal() 函数为信号设置 SA_RESTART 标志:

      rt_sigaction(SIGTERM, {sa_handler=0x562f2a8c3249, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fb504d2d210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
      

      意思是,open() 系统调用在处理完信号后会自动重启。

      您可以使用sigaction() 对信号处理进行更细粒度的控制,而无需设置 SA_RESTART:

      struct sigaction sa;
      memset (&sa, 0, sizeof (sa));
      sa.sa_handler = catchSignal;
      sa.sa_flags = 0;
      sigemptyset (&sa.sa_mask);
      if (sigaction (SIGTERM, &sa, NULL) == -1) {
        perror("sigaction");
        return -1;
      }
      

      【讨论】:

        【解决方案3】:

        您可以使用O_NONBLOCK,在这种情况下open() 将立即返回,并且您将在fcntl(2) 取消O_NONBLOCK 时立即阻止。

        阅读手册页,你可能有办法让open(2) 返回-1errno 等于EINTR。但是正常用法是已经描述的,重新发出调用,因此信号处理程序不会使调用被中断(很多代码取决于这种行为)。我不确定这一点,但我认为只有pause(2)select(2) 和朋友在收到非忽略信号时会被打断(并返回)。

        值得注意的是,只有在可中断调用中被阻塞并接收到信号的线程才会被唤醒并中断调用,而接收中断的线程可以是你进程中已经启动的任何线程。

        【讨论】:

          猜你喜欢
          • 2023-01-29
          • 1970-01-01
          • 2019-08-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多