2

Given a nonblocking TCP socket, if the call

read(sock, buf, bufLen)

returns a value < bufLen, is it safe to then wait for an edge-triggered EPOLLIN event? Or must I call read again to ensure it's zero or EAGAIN?

In my testing, everything stays working when I remove the last call, I just want to know if it's guaranteed anywhere, or by the Linux source code, and if I can get rid of the extra call.

4

2 回答 2

4

您的问题已在 中得到解答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) 编写时也是如此。(如果你不能保证被监控的文件描述符总是指向一个面向流的文件,请避免使用后一种技术。)

于 2014-01-16T07:31:33.353 回答
3

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

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

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

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

于 2014-01-14T13:49:52.643 回答