我编写了一个非常小的测试程序来检查 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);