【问题标题】:Epoll TCP edge-triggered necessity of last read(2) call最后一次 read(2) 调用的 Epoll TCP 边缘触发必要性
【发布时间】:2014-02-02 08:42:03
【问题描述】:

给定一个非阻塞的 TCP 套接字,如果调用

read(sock, buf, bufLen)

返回值 bufLen,然后等待边缘触发的 EPOLLIN 事件是否安全?或者我必须再次拨打read 以确保它为零或EAGAIN?

在我的测试中,当我删除最后一个调用时,一切都会继续工作,我只想知道它是否在任何地方或 Linux 源代码中得到保证,以及我是否可以摆脱额外的调用。

【问题讨论】:

    标签: linux sockets tcp epoll epollet


    【解决方案1】:

    它是“安全的”只要它不会崩溃,但除非你继续调用read,直到你得到EAGAIN(或零,这意味着另一端已关闭连接),您有时会对数据的可用性做出错误的假设。最糟糕的是,它在大多数情况下看起来也可以正常工作。

    边缘触发与级别触发通知相比,仅保证在自您上次调用 epoll_wait 后就绪状态发生更改时,您会收到 一个通知,即使仍有数据可以阅读。
    边缘触发的事件通知确实有时在 Linux 下表现得有点奇怪或不直观,所以它可能做一些与你期望不同的事情,例如当更多数据到达时给您另一个通知(因此您的代码似乎“无论如何都可以工作”),但这不是保证的。
    在使用epolleventfd 时,我也有过类似的“惊喜”。您期望在边缘触发模式下发生的事情是所有已经被阻塞的线程都被唤醒(同时,并且恰好一次),并且每个人在事件发出阻塞信号后调用epoll_wait,直到事件发生再次消费并发出信号。它真正做的是唤醒调用epoll_waitfirst 线程。再次令人惊讶的是,关卡触发模式完全按照您的意愿工作,除了您必须消耗事件才能再次准备好它,对此没有适当的方法(因为您必须只做一次 否则你会屏蔽read)。

    因此,如果您不消耗所有数据并稍后等待再次收到通知,那么您可能很幸运并且它“无论如何都会工作”,或者您可能会等待很长时间,可能永远等待。因此,我的建议是绝对继续阅读,直到您收到 EAGAIN,这是避免意外的唯一真正可靠的方法。

    请注意,如果您继续天真地阅读,您可能会饿死慢速发件人。如果您有一个非常快的发件人并且您继续阅读快速发件人,那么您将永远不会看到EAGAIN(至少在另一端继续发送时不会看到!),您将完全饿死其他发件人。
    因此,将所有准备好的描述符放在一个列表中并循环读取它们是有意义的,当它们返回 EAGAIN 时将它们从列表中删除。

    【讨论】:

    • 这是一套很好的实用技巧,用于使用边缘触发的 epoll。程序总是需要读取,直到读取失败并显示 errno=EAGAIN(也称为 EWOULDBLOCK)。这样做,如果一个人正在写入一个大文件或大量数据流,它可以很容易地饿死其他远程连接。边缘触发 epoll 明显更快,但也有问题....
    • "...自上次 you 调用...以来的变化..." epoll 是否会跟踪它发送给某个地方的侦听器的事件?
    • @d9ngle:据我所知,我会严重怀疑。边缘触发的工作方式更像是一个脏标志。套接字准备就绪,标志已设置。你打电话给epoll_wait,标志被清除。其他的都是你的问题。在级别触发模式下,一旦描述符准备就绪,内核就会跟踪接收到的字节数(或保留在发送缓冲区中)以及已读取(或写入)的字节数,epoll_wait 阻塞或不阻塞t 阻止。这就是为什么边沿触发的开销也少一些。内核做的事情更少(......你做的)。
    【解决方案2】:

    man 7 epoll 已回答您的问题。如您所见,它取决于套接字类型(数据包/流):

    Q9在使用 EPOLLET 标志(边缘触发行为)时,我是否需要连续读取/写入文件描述符直到 EAGAIN?

    A9 从 epoll_wait(2) 接收事件应该会提示您该文件描述符已准备好进行请求的 I/O 操作。您必须认为它已准备就绪,直到下一次(非阻塞)读/写产生 EAGAIN。何时以及如何使用文件描述符完全取决于您。

    对于面向数据包/令牌的文件(例如,数据报套接字、规范模式下的终端),检测读/写 I/O 空间结束的唯一方法是继续读/写直到 EAGAIN。

    对于面向流的文件(如管道、FIFO、流套接字),也可以通过检查目标文件读/写的数据量来检测读/写I/O空间耗尽的情况描述符。 例如,如果您通过请求读取一定数量的数据来调用 read(2),而 read(2) 返回的字节数较少,则可以确定文件的读取 I/O 空间已用尽描述符。使用 write(2) 编写时也是如此。 (如果您不能保证被监控的文件描述符总是指向一个面向流的文件,请避免使用后一种技术。)

    【讨论】:

      猜你喜欢
      • 2016-07-08
      • 2014-10-14
      • 2013-01-25
      • 1970-01-01
      • 2012-02-28
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多