从信号处理程序返回后再次读取块

时间:2019-08-05 19:04:44

标签: c signals system-calls errno

我编写了一个非常小的测试程序,以在errno被处理的信号中断时检查read()的值。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

void handler(int sig){
    printf("signal: %d\n", sig);
}

int main(){
    signal(SIGINT, handler);
    char arr[10];
    read(0, arr, 10);
    perror("emsg");
    return 0;
}

根据我所知道的一切以及read(2)上的手册页,

EINTR  The call was interrupted by a signal before any data  was  read;
              see signal(7).

read应该返回-1并将errno设置为EINTR。但是,程序的输出表明从信号处理程序返回后,它在read上再次阻塞。

这对我来说绝对没有意义,我无法弄清楚出了什么问题。

这是我得到的输出:

$ ./a.out
^Csignal: 2
^Csignal: 2
^Csignal: 2
^Csignal: 2
hello
emsg: Success

我的问题与this one不同。后者在任何地方都没有提到系统调用中断时会发生什么。该讨论的关键是应使用的内容。

在同一线程上另外this answersignal()在下面调用sigaction(),那么为什么在系统调用的情况下两者的行为不同?

1 个答案:

答案 0 :(得分:2)

  

根据我所知道的一切和read(2)上的手册页,

EINTR  The call was interrupted by a signal before any data  was  read;
              see signal(7).
     

read应该返回-1并将errno设置为EINTR

您对此读得太多了。 read() 可以返回-1并将errno设置为EINTR,该组合应解释为表示已被信号中断(在读取任何数据之前) )。甚至可以肯定地说,如果read由于被信号中断而失败,那么这就是期望它出现的方式。但这并不意味着read可以保证在阻塞时收到信号也会失败。

  

但是,程序的输出表明它再次在   从信号处理程序返回后读取。

这确实是一种可能的行为。特别是,它是signal()和信号处理的BSD语义的一部分,并且是Glibc的默认设置(受_BSD_SOURCE功能测试宏的约束),因此,这就是您期望的默认设置在Mac(由于BSD)和大多数Linux(由于Glibc)上。 the manual page for Glibc's implementation of signal()的“可移植性”部分对此问题进行了详细介绍。

  

此外,在同一线程上的此答案还表明signal()调用   sigaction()在下面,为什么在系统情况下该行为   两者的呼叫方式有所不同?

sigaction()的关键在于,它提供了用于指定处理已设置其信号的信号的所有细节的机制,尤其是

  • (某些)系统调用是否在信号处理后恢复;
  • 是否在接收到信号后重置信号处理;和
  • 在其处理程序运行时是否阻止了信号。

因此,如果signal()的某些实现通过调用sigaction()进行操作,则在随后接收到信号时,这些或其他辅助行为没有固有的含义。这也不意味着直接通过sigaction()注册该信号的处理程序必须产生与通过signal()间接进行该操作相同的效果-这都取决于sigaction()的参数。呼叫者可以选择。

特别注意上面链接的联机帮助页中的建议:

  

signal()的唯一可移植用法是设置信号的处理方式   到SIG_DFLSIG_IGN。何时的语义          使用signal()来建立一个信号处理程序,该信号处理程序在整个系统中有所不同(POSIX.1明确允许这种变化); 请勿为此目的使用

(强调原文)

假设您希望系统调用 not 在被SIGINT中断并由您的处理程序处理后恢复,您可以通过避免指定{{1 }}。最有可能的是,您也不需要任何其他标志,所以这意味着(例如)这样的东西:

sigaction