我在读APUE和系统调用中断章节让我困惑。
我想根据书本写下我的理解,请指正。
早期 UNIX 系统的一个特征是,如果进程在“慢速”系统调用中被阻塞时捕获到信号,则系统调用将被中断。系统调用返回错误并被
errno
设置为EINTR
。这样做的假设是,由于发生了信号并且进程捕获了该信号,因此很有可能发生了一些事情,应该唤醒被阻止的系统调用。所以它说的是早些时候UNIX系统有一个特性:如果我的程序使用系统调用,如果程序在任何时候捕获到信号,它就会被中断/停止。 (默认处理程序也算作捕获吗?)
例如,如果我有一个
read
系统调用,它读取10GB数据,当它读取时,我发送任何一个信号(例如kill -SIGUSR1 pid
),然后read
会失败并返回。
为了防止应用程序必须处理中断的系统调用,4.2BSD 引入了自动重新启动某些中断的系统调用。自动重新启动的系统调用是
ioctl
、read
、readv
、write
、writev
、wait
和waitpid
。正如我们所提到的,这些函数中的前五个只有在运行速度较慢的设备上时才会被信号中断。 当捕获到信号时总是被中断wait
。waitpid
由于这会给某些应用程序带来问题,这些应用程序不希望操作在中断时重新启动,因此 4.3BSD 允许进程在每个信号的基础上禁用此功能。
所以之前自动重启被引入后,我必须自己处理中断的系统调用。我需要编写如下代码:
中断系统调用的问题是我们现在必须显式处理错误返回。典型的代码序列(假设读取操作并假设我们想要重新启动读取,即使它被中断)是:
again:
if ((n = read(fd, buf, BUFFSIZE)) < 0) {
if (errno == EINTR)
goto again; /* just an interrupted system call */
/* handle other errors */
}
但现在我不必编写这种代码,因为自动重启机制。
因此,如果我的理解都是正确的,那么我现在应该关心什么/为什么要关心中断的系统调用..?看来系统/操作系统会自动处理它。
答案1
信号处理程序对系统调用的中断仅发生在各种阻塞系统调用的情况下,并且当系统调用被程序员显式建立的信号处理程序中断时发生。
此外,在阻塞系统调用被信号处理程序中断的情况下,自动系统调用重新启动是一个选修的特征。您可以SA_RESTART
在建立信号处理程序时通过指定标志来选择自动重新启动系统调用。正如(例如)Linux 中所述信号(7)手册页:
If a signal handler is invoked while a system call or library
function call is blocked, then either:
* the call is automatically restarted after the signal handler
returns; or
* the call fails with the error EINTR.
Which of these two behaviors occurs depends on the interface and
whether or not the signal handler was established using the
SA_RESTART flag (see sigaction(2)).
正如上面引用的最后一句话所暗示的,即使您选择使用此功能,它也不适用于所有系统调用,并且它所适用的系统调用集因 UNIX 实现而异。 Linuxsignal(7)
手册页指出了使用该标志时会自动重新启动的许多系统调用SA_RESTART
,而且还指出了即使您在建立处理程序时指定该标志也永远不会重新启动的各种系统调用,包括:
* "Input" socket interfaces, when a timeout (SO_RCVTIMEO) has been
set on the socket using setsockopt(2): accept(2), recv(2),
recvfrom(2), recvmmsg(2) (also with a non-NULL timeout argu‐
ment), and recvmsg(2).
* "Output" socket interfaces, when a timeout (SO_RCVTIMEO) has
been set on the socket using setsockopt(2): connect(2), send(2),
sendto(2), and sendmsg(2).
* File descriptor multiplexing interfaces: epoll_wait(2),
epoll_pwait(2), poll(2), ppoll(2), select(2), and pselect(2).
* System V IPC interfaces: msgrcv(2), msgsnd(2), semop(2), and
semtimedop(2).
对于这些系统调用,使用 APUE 中描述的形式的循环手动重新启动至关重要,例如:
while ((ret = some_syscall(...)) == -1 && errno == EINTR)
continue;
if (ret == -1)
/* Handle error */ ;
答案2
[我还没读过 APUE 的东西,但你引用的东西看起来不太好]
如果我的程序使用系统调用,如果程序在任何时候捕获信号,它就会被中断/停止。
不是任何系统调用。仅有的一些系统调用是可中断的。
(默认处理程序也算作捕获吗?)
不。
例如,如果我有一个读取系统调用,它读取10GB数据,当它读取时,我发送任何一个信号(例如kill -SIGUSR1 pid),然后读取将失败并返回。
如果在读取之前被中断,您的 10GB read() 只会返回 EINTR即使是一个字节;否则它将返回已读取的数据量(短读取 = 成功,errno 不相关)。
[这在链接的欺骗中没有解释]
因此,如果我的理解都是正确的,那么我现在应该关心什么/为什么要关心中断的系统调用..?看来系统/操作系统会自动处理它。
因为你可能想做某物收到信号后,您无法通过信号处理程序执行太多操作;任何使用 malloc() 或 stdio(甚至 printf())的东西都是不可能的。因此,您必须处理程序主循环中的中断,并且为了能够做到这一点,您应该以某种方式从阻塞的 read() 中中断(即使在 poll() 返回 fd 之后,read() 也可以阻塞准备阅读)。
[这曾是也在链接的欺骗中进行了解释]