c - 从信号处理程序返回后再次读取 block

标签 c signals system-calls errno

<分区>

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

#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 answer在同一线程上说 signal() 在下面调用 sigaction(),那么为什么两者在系统调用情况下的行为不同?

最佳答案

According to everything I know and the man page on read(2),

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

read should return -1 and set errno to EINTR.

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

However, the output of the program suggests that it blocks again on read after returning from the signal handler.

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

Additionally this answer on the same thread says that signal() calls sigaction() underneath, so why is the behaviour in case of system calls different for the two?

sigaction() 的关键是它提供了机制来指定处理已设置处置的信号的所有细节,特别是,

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

因此,如果 signal() 的某些实现通过调用 sigaction() 进行操作,那么在后续接收到信号时,对于这些或其他辅助行为没有内在的暗示.这也不意味着直接通过 sigaction() 为该信号注册处理程序必须产生与通过 signal() 间接注册该信号的处理程序相同的效果——这完全取决于sigaction() 的参数。来电者可以选择。

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

The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.

(重点在原文中。)

假设您希望系统调用在被SIGINT中断并由您的处理程序处理后恢复,您可以从sigaction获得通过避免在标志中指定 SA_RESTART。很可能,您也不想要任何其他标志,因此这意味着(例如)像这样:

// with this form, flags and signal mask are initialized by default to all-bits-zero
sigaction(SIGINT, & (struct sigaction) { .sa_handler = handler }, NULL);

关于c - 从信号处理程序返回后再次读取 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57364691/

相关文章:

c - 在从 C 调用的函数中迭代 Lua 中的表

c++ - 子级和父级 C 程序中的 SIGSEGV 处理

C - 创建和写入文件时出现奇怪的字符

C程序计算数组的长度

c - 如何从txt文件中加载随机数

c - 为什么 malloc 根本不分配内存?

c++ - 无法处理信号,导致系统调用中断

python - 为什么使用 threading.Event 导致 SIGTERM 未被捕获?

c - Linux 系统调用 time() 出错时返回 ((time_t) -14)

c - 如何解释 PTRACE_PEEKTEXT 返回值