简短的版本是信号中断当前的系统调用。你正在做fgets()
的,现在可能会阻塞read()
系统调用。read()
调用被中断,它返回 -1 并设置errno
为EINTR
。
这会导致fgets
返回 NULL,循环结束,程序结束。
一些背景
linux 上的 glibc 为signal()
. 一种是跨信号自动重新启动系统调用,另一种不是。
当一个信号出现并且进程在系统调用中被阻塞时,系统调用被中断(“取消”)。执行在用户空间应用程序中恢复,并出现信号处理程序。中断的系统调用将返回错误,并将 errno 设置为EINTR
.
接下来会发生什么取决于系统调用是否跨信号重新启动。
如果系统调用可重新启动,则运行时 (glibc) 只需重试系统调用。对于read()
系统调用,这类似于read()
实现为:
ssize_t read(int fd, void *buf, size_t len)
{
ssize_t sz;
while ((sz = syscall_read(fd, buf, len)) == -1
&& errno == EINTR);
return sz;
}
如果系统调用没有自动重新启动,read()
其行为类似于:
ssize_t read(int fd, void *buf, size_t len)
{
ssize_t sz;
sz = syscall_read(fd, buf, len));
return sz;
}
在后一种情况下,由您的应用程序检查 read() 是否因为被信号中断而失败。由您决定是否read()
由于信号被处理而暂时失败,您可以重试read()
呼叫
信号与信号
通过使用sigaction()
instead of signal()
,您可以控制是否重新启动系统调用。您指定的相关标志sigaction()
是
SA_RESTART 通过使某些系统调用可跨信号重新启动,提供与 BSD 信号语义兼容的行为。该标志仅在建立信号处理程序时才有意义。有关系统调用重新启动的讨论,请参见 signal(7)。
BSD 与 SVR4 语义
如果你使用signal()
,这取决于你想要什么语义。从 的描述中可以看出SA_RESTART
,如果是 BSD 信号语义,系统调用会被重启。这是 glibc 中的默认行为。
另一个区别是 BSD 语义在处理信号signal()
后留下由安装的信号处理程序。SVR4 语义卸载信号处理程序,如果您想捕获更多信号,您的信号处理程序将不得不重新安装处理程序。
apue.h
然而,“apue.h”_XOPEN_SOURCE 600
在包含<signal.h>
. 这将导致 signal() 具有 SVR4 语义,系统调用不会重新启动。这将导致您的 fgets() 调用“失败”。
不要使用signal(),使用sigaction()
由于所有这些行为差异,请使用 sigaction() 代替信号。sigaction() 让您可以控制发生的事情,而不是根据(可能)隐藏来更改语义#define
,就像使用signal()