Nonblocking EINTR和非阻塞呼叫

Nonblocking EINTR和非阻塞呼叫,nonblocking,eintr,Nonblocking,Eintr,众所周知,一些阻塞调用,如read和write将返回-1并将errno设置为EINTR,我们需要处理这个问题 我的问题是:这是否适用于非阻塞调用,例如,将套接字设置为O_NONBLOCK 因为我读过的一些文章和资料说,非阻塞调用不需要为此费心,但我没有找到关于它的权威参考。如果是这样,它是否适用于不同的实现?我无法给您这个问题的确切答案,答案可能会因系统而异,但我希望使用EINTR时,非阻塞套接字永远不会失败。如果您查看各种系统的手册页,了解以下套接字函数bind()、connect()、sen

众所周知,一些阻塞调用,如
read
write
将返回-1并将
errno
设置为
EINTR
,我们需要处理这个问题

我的问题是:这是否适用于非阻塞调用,例如,将套接字设置为
O_NONBLOCK


因为我读过的一些文章和资料说,非阻塞调用不需要为此费心,但我没有找到关于它的权威参考。如果是这样,它是否适用于不同的实现?

我无法给您这个问题的确切答案,答案可能会因系统而异,但我希望使用
EINTR
时,非阻塞套接字永远不会失败。如果您查看各种系统的手册页,了解以下套接字函数
bind()
connect()
send()
、和
receive()
,或在POSIX标准中查找这些函数,您会注意到一些有趣的事情:除了一个函数之外,所有这些函数都可以返回
-1
并将
errno
设置为
EINTR
。在
EINTR
中没有记录到会失败的函数是
bind()
。而
bind()
也是该列表中默认情况下永远不会阻塞的唯一函数。因此,似乎只有阻塞函数可能会因为
EINTR
而失败,包括
read()
write()
,但是如果这些函数从不阻塞,它们也永远不会因为
EINTR
而失败,如果使用
O\u NONBLOCK
,这些函数永远不会阻塞

从逻辑的角度来看,这也毫无意义。例如,您使用的是阻塞I/O,并且调用“代码> Read())/代码>,这个调用必须阻止,但是当它被阻塞时,一个信号被发送到您的进程,因此读请求被解除阻塞。系统应该如何处理这种情况?声称
read()
成功了?那将是一个谎言,它没有成功,因为没有读取数据。声称它成功了,但读取了零字节的数据?这也不正确,因为“零读取结果”用于指示流的结束(或文件的结束),所以您的流程将假定没有读取任何数据,因为已到达文件的结尾(或另一端的套接字/管道已关闭),而事实并非如此。尚未到达文件结尾(或流结尾),如果再次调用
read()
,它将能够返回更多数据。所以这也是一个谎言。您的期望是,此读取调用要么成功并读取数据,要么失败并出现错误。因此,在这种情况下,读取调用必须失败并返回
-1
,但系统应设置什么
errno
值?所有其他错误值都表明文件描述符存在严重错误,但是没有严重错误,并且指出这样的错误也是谎言。这就是为什么
errno
被设置为
EINTR
,这意味着:“流没有问题。您的读取调用刚刚失败,因为它被信号中断。如果没有中断,它可能仍然成功,因此如果您仍然关心数据,请重试。”

如果现在切换到非阻塞I/O,则不会出现上述情况。read调用永远不会阻塞,如果它不能立即读取数据,它将失败,并出现错误
EAGAIN
(POSIX)或
ewoodblock
(非官方,在Linux上,两者都是相同的错误,只是它的替代名称),这意味着:“目前没有可用的数据,因此您的读取调用必须阻塞并等待数据到达,但不允许阻塞,因此它失败。”因此,可能出现的每种情况都会出现错误

当然,即使使用非阻塞I/O,读取调用也可能会被信号暂时中断,但为什么系统必须指出这一点?每个函数调用,无论是系统函数还是用户编写的函数调用,都可能会被信号暂时中断,实际上是每个函数调用,没有例外。如果系统必须通知用户无论何时发生这种情况,所有系统功能都可能因为
EINTR
而失败。然而,即使有信号中断,这些功能通常会一直执行到最后,这就是为什么这种中断是不相关的。错误
EINTR
用于告诉调用者他需要的操作ted因信号中断而未执行,但在非阻塞I/O的情况下,没有理由不执行读取或写入请求,除非现在无法执行,但这可以通过适当的错误指示

为了证实我的理论,我看了一下MacOS(10.8)的内核,它仍然主要基于FreeBSD内核,似乎证实了这一怀疑。如果由于没有可用数据,当前无法进行读取调用,那么内核将检查文件描述符标志中的
O_NONBLOCK
标志。如果设置了此标志,它将立即与
EAGAIN
一起失败。如果未设置,则会将当前t通过调用名为
msleep()
的函数使线程休眠。该函数是(正如我所说,OS X在其内核中使用了大量FreeBSD代码)。该函数使当前线程休眠,直到它被显式唤醒(如果数据准备好读取)或超时(例如,您可以在套接字上设置接收超时)。但是,如果发送了信号,线程也会被唤醒,在这种情况下,
msleep()
本身返回
EINTR
,下一个更高的层只会传递此错误。因此它是
msleep())产生
EINTR
错误的
,但如果设置了
O\u NONBLOCK
标志,则不会首先调用
msleep()
,因此无法返回此错误

当然,这是MacOS/FreeBSD,其他系统可能会有所不同,但由于大多数系统都试图保持