我正在阅读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 */
}
但是现在我不用写这种代码了,因为有自动重启机制。
因此,如果我的理解都是正确的,那么我现在应该关心什么/为什么要关心中断的系统调用..?似乎系统/操作系统会自动处理它。
信号处理程序对系统调用的中断仅发生在各种阻塞系统调用的情况下,并且当系统调用被程序员明确建立的信号处理程序中断时发生。
此外,在阻塞系统调用被信号处理程序中断的情况下,自动系统调用重新启动是一个可选功能。
SA_RESTART
您可以通过在建立信号处理程序时指定标志来选择自动重新启动系统调用。如(例如)Linux signal(7)手册页中所述:正如上面引用的最后一句话所暗示的,即使您选择使用此功能,它也不适用于所有系统调用,并且它适用的系统调用集因 UNIX 实现而异。Linux
signal(7)
手册页记录了许多在使用该SA_RESTART
标志时会自动重新启动的系统调用,但还继续记录了从未重新启动的各种系统调用,即使您在建立处理程序时指定了该标志,包括:对于这些系统调用,使用 APUE 中描述的形式的循环手动重新启动是必不可少的,例如:
[我没有读过 APUE 的东西,但你引用的东西看起来不太好]
没有任何系统调用。只有一些系统调用是可中断的。
不。
您的 10GB read() 仅在无法读取单个字节之前被中断时才会返回 EINTR ;否则它将返回它已经读取的数据量(短读=成功,errno 不相关)。
[这在链接的欺骗中没有解释]
因为你可能想在收到信号后做一些事情,而你不能从信号处理程序中做很多事情;任何使用 malloc() 或 stdio(甚至 printf())的东西都是不可能的。因此,您必须在程序的主循环中处理中断,并且为了能够做到这一点,您应该以某种方式从阻塞的 read() 中中断(即使在 poll() 返回 fd 之后,read() 也可以阻塞准备阅读)。
[这在链接的欺骗中也有解释]