TCP 套接字:当 select() 指示有数据可用时,read() 是否仍会因 EINTR 而失败?

TCP socket: Can read() still fail with EINTR when select() indicates there are data available?

我正在使用 select() 来自 TCP 套接字的非阻塞 read()。当select()表示有数据可读时,我不确定在read()之后是否还需要处理EINTR

他们是我理解文档的方式(我没有通读内核代码,所以我可能是错的),答案是:是的。

如果在内核的read操作期间发生中断(当内核正在将数据从套接字的缓冲区复制到用户内存并更新套接字的状态时),操作被取消并且read 函数将 return -1errno 设置为 EINTR)。

套接字是 non-blocking 的事实只会最小化 "window" 但它不会阻止这种情况的发生。

此外,即使情况并非如此,我还是建议您假设 EINTR 是可能的。只要文档没有说明 EINTR 错误对 non-blocking 文件描述符无效,未来对内核实现的更新可能会在他们认为合适的时候调用此错误。

此外,请考虑即使 read 操作对应用程序来说感觉是原子的,但内核正在处理许多内部结构(仅套接字的读取缓冲区就有许多元素需要在之后更新内存被复制)。为此,内部结构具有锁定/同步机制。即使套接字 non-blocking,仍有 window 的时间可以中断呼叫。

是的,绝对是。 select 函数是一个状态报告函数,当您调用 select 并且您注意到它的 return 值时,它会在某个时间 in-between 报告某物的状态。它绝对没有未来的保证,期间。

这是一个非常普遍的误解。但是认为 select 确保未来的操作将提供某些特定结果的想法与认为检查磁盘上是否有空闲 space 意味着未来的写入不会失败一样被误导了。根据其判断,即使您认为有足够的空闲 space,该实现也允许在空闲 space 不足的情况下使写入失败。 space 可以在找到 space 和尝试使用它之间填充。

select也是如此。没有规定实现必须以某种方式记住它给了你一个命中 select 并影响后续读取的实现。人们已经多次做出这种假设,并被它可怕地咬伤了。不要仅仅因为你想不出它会失败的任何方式就认为它不会失败。

例如,没有规则禁止实施执行以下操作:

  1. 收到一个数据包。
  2. 这是有效的 TCP 数据,因此 select return 可以读取了。此时发出的 read 会 return 编辑数据。
  3. 在实现可以确认数据之前,网络接口的写入队列已满。
  4. 由于实施发现由于队列已满而无法立即发送 ACK,因此它只是选择丢弃数据包并强制另一端重新传输
  5. 后续 read 操作阻塞,因为没有数据可读。

如果您认为某些标准禁止它,请引用该标准。 select 命中后的 read 可能因任何原因而失败,select 命中前的 read 可能会失败。 select 函数 never 提供未来保证。