从信号处理程序返回后再次读取块
Read blocks again after returning from signal handler
我编写了一个非常小的测试程序来检查 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
应 return -1
并将 errno
设置为 EINTR
。但是,程序的输出表明它在从信号处理程序 returning 之后再次阻塞在 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()
可以 return -1 并设置 errno
为 EINTR
,这种组合应该被解释为是被a打断了信号(在读取任何数据之前)。甚至可以肯定地说,如果 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()
的 "Portability" 部分详细介绍了这个问题。
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.
(强调在原文中。)
假设您希望系统调用 not 在被 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);
我编写了一个非常小的测试程序来检查 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
应 return -1
并将 errno
设置为 EINTR
。但是,程序的输出表明它在从信号处理程序 returning 之后再次阻塞在 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 seterrno
toEINTR
.
你读得太多了。 read()
可以 return -1 并设置 errno
为 EINTR
,这种组合应该被解释为是被a打断了信号(在读取任何数据之前)。甚至可以肯定地说,如果 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()
的 "Portability" 部分详细介绍了这个问题。
Additionally this answer on the same thread says that
signal()
callssigaction()
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 toSIG_DFL
orSIG_IGN
. The semantics when usingsignal()
to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.
(强调在原文中。)
假设您希望系统调用 not 在被 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);