【问题标题】:TCP socket: Can read() still fail with EINTR when select() indicates there are data available?TCP 套接字:当 select() 指示有可用数据时,read() 是否仍会因 EINTR 而失败?
【发布时间】:2018-08-04 04:06:59
【问题描述】:

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

【问题讨论】:

    标签: c sockets tcp network-programming kernel


    【解决方案1】:

    是的,当然。 select 函数是一个状态报告函数,它在您调用 select 和您注意到它的返回值之间的某个时间报告某物的状态。它带有绝对没有任何未来保证,期间。

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

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      套接字是非阻塞的这一事实只会最小化“窗口”,但并不能阻止这种情况的发生。

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

      另外,考虑到尽管read 操作对应用程序来说是原子操作,但内核正在处理许多内部结构(仅套接字的读取缓冲区就有许多元素需要在复制内存后更新)。为此,内部结构具有锁定/同步机制。即使套接字是非阻塞的,仍然有一个时间窗口可以中断调用。

      【讨论】:

      • 这个推理对我来说不太令人信服。想想像malloc/free 这样的API,我相信它们也有内部结构并涉及锁定机制,但它们永远不会因EINTR 而失败。但我同意现在总是检查 EINTR 是个好主意。
      • @pynexj - 我了解您的预订 think about APIs like malloc/free which i believe also has internal structures and involves locking mechanisms - 是的,但是......这些 API 调用不允许以 EINTR 失败(它们在中断后在内部恢复)。另一方面,如果 read 函数在等待锁定时被中断,则允许(并且可能会)失败并返回 EINTR,而不是继续等待文件锁定。
      猜你喜欢
      • 2016-05-15
      • 1970-01-01
      • 1970-01-01
      • 2015-09-21
      • 1970-01-01
      • 1970-01-01
      • 2020-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多