【问题标题】:Why having to use non-blocking fd in a edge triggered epoll function?为什么必须在边缘触发的 epoll 函数中使用非阻塞 fd?
【发布时间】:2013-01-16 14:02:58
【问题描述】:

我在 web 中阅读了文档关于边缘触发的 epoll 功能如下:

1. The file descriptor that represents the read side of a pipe (rfd) is registered on the epoll instance.
2. A pipe writer writes 2 kB of data on the write side of the pipe.
3. A call to epoll_wait(2) is done that will return rfd as a ready file descriptor.
4. The pipe reader reads 1 kB of data from rfd.
5. A call to epoll_wait(2) is done.
.......
.......

使用epoll作为边缘触发(EPOLLET)接口的建议方式如下: i) 使用非阻塞文件描述符 ii) 仅在 read(2) 或 write(2) 返回 EAGAIN 后调用 epoll_wait 事件。

我理解了2,但我不知道为什么要使用非阻塞文件描述符。

谁能解释为什么使用非阻塞文件描述符? 为什么在级别触发的 epoll 函数中可以使用阻塞文件描述符?

【问题讨论】:

    标签: linux socket.io nonblocking epoll


    【解决方案1】:

    这个想法是当你有一个边缘触发的通知表明有数据需要时,尝试完全耗尽文件描述符。因此,一旦epoll() 返回,您将循环遍历read()write(),直到它返回-EAGAIN,此时没有更多数据可用。

    如果 fd 被打开阻塞,那么最后一个 read()write() 也将阻塞,您将没有机会返回到 epoll() 调用来等待整个 fd 集。当以非阻塞方式打开时,最后一个 read()/write() 会返回,您确实有机会返回轮询。

    在以电平触发方式使用epoll() 时,这不是什么大问题,因为在这种情况下,如果有任何 数据,epoll() 将立即返回。所以一个(伪代码)循环,例如:

    while (1) {
      epoll();
      do_read_write();
    }
    

    会起作用,因为只要有数据,您就一定会致电do_read_write()。使用边缘触发的 epoll 时,如果在 do_read_write() 完成和下一次调用 epoll() 之间出现新数据可用的通知,则可能会丢失。

    【讨论】:

    • 如何处理“有可能遗漏新数据的通知”?
    • 这个答案的最后一部分似乎并不重要。在您没有等待时发生的事件也将被注册并在下次运行 epoll_wait 时返回。但真正的问题是,如果您没有通过 do_read_write 函数从套接字读取所有数据,并且您再次进入 epoll_wait。然后对于边缘触发的行为,您将无限期地卡在那里,因为只有在可读性(或可写性)发生变化时才会产生事件。问题不在于数据何时到来,而在于您没有全部阅读。
    【解决方案2】:

    我猜这是因为边缘触发的语义。根据语义,边缘触发器将仅在收到 EAGAIN 后引发另一个事件。在阻塞套接字的情况下,没有 EAGAIN。你可以用其他方式定义它,但是 Linux 是这样定义它的。换句话说,如果你使用阻塞套接字,你不知道什么时候可以安全地调用 epoll_wait。

    【讨论】:

      【解决方案3】:

      epoll 的 ET 模式必须读取所有数据或写入所有数据,因为在 flag 改变后,et 模式触发了一次。当您读取所有数据后,如果您使用块读取或写入,则线程必须挂起。所以必须使用非阻塞。

      【讨论】:

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