linux - EPOLLET 的用例是什么?

标签 linux epoll epollet

<分区>

epoll 边缘触发模式是一个奇怪的野兽。它要求流程跟踪每个受监视 FD 的最后响应是什么。它要求流程必须处理所有报告的事件(否则我们可能会认为 FD 没有报告任何内容,而实际上它被边沿触发行为静音了)。

边缘触发 epoll 有意义的用例是什么?

最佳答案

据我所知,EPOLLET 的主要用例是微线程。

回顾一下 - 用户空间根据要处理的内容的可用性在微线程(我将其称为“纤程”,因为它更短)之间进行上下文切换。这也称为“协作式多任务处理”。

文件描述符的基本处理是通过包装相关的 IO 函数,如下所示:

ssize_t read(int fd, void *buffer, size_t length) {
  // fd should already be in O_NONBLOCK mode
  while(true) {
    ssize_t result = ::read(fd, buffer, length); // The real read
    if( result!=-1 || (errno!=EAGAIN && errno!=EWOULDBLOCK) )
      return result;

    start_monitoring(fd, READ);
    wait_event();
  }
}

start_monitoring 是一个函数,可确保监控 fd 的读取可用性。 wait_event 执行上下文切换,直到调度程序重新唤醒此纤程,因为 fd 现在已准备好读取数据。

epoll实现这个的通常方法是在start_monitoring中的fd上调用EPOLL_CTL_MOD来添加监听EPOLLIN,并在 epoll 报告事件后再次停止监听 EPOLLIN

这意味着具有可用数据的read 将在 1 个系统调用内完成,但返回 EAGAIN 的 read 将至少 4 个系统调用(最初的 read,两个 EPOLL_CTL_MOD,最后一个成功的 read)。

请注意,上面的代码不包括同样必须发生的 epoll_wait。我没有计算它,因为我慷慨地假设其他光纤也将被同一个系统调用唤醒,因此将其成本完全归因于我们的光纤是不公平的。总而言之,该机制需要 4+x 次系统调用,其中 x 介于 0 和 1 之间。

降低成本的一种方法是使用EPOLLONESHOT。这样做会自动将 fd 从监控中移除,从而将我们的成本降低到 3+x。更好,但我们还可以做得更好。

输入EPOLLET。先前的 fd 状态可以是 armed 或 unarmed(即 - 下一个事件是否会触发 epoll)。此外,fd 当前可能(在 read 的入口点)准备好数据,也可能没有准备好数据。四个州。让我们把它们分散开来。

就绪(无论是否武装):第一次调用 read 返回数据。 1个系统调用。这条路径不会改变武装状态,就绪状态取决于我们是否读取了所有内容。

未就绪(无论是否准备好):第一次调用 read 返回 EAGAIN,从而准备好 fd。我们在 wait_event 中进入休眠状态,而无需执行另一个系统调用。一旦我们醒来,我们就处于非武装模式(因为我们刚刚醒来)。因此,我们不需要调用 epoll_ctl 来禁用对 fd 的监听。我们调用 read 返回数据。我们让该功能准备就绪或未准备就绪,但未武装。

总成本:2+x。

我们将不得不面对每个 fd 一次虚假唤醒,因为 fd 开始武装。我们的代码必须处理 epoll 报告没有光纤正在监听的 fd 的情况。在这种情况下,处理只是意味着忽略并继续前进。 FD 不会再次被虚假报告。

关于linux - EPOLLET 的用例是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46633433/

相关文章:

linux - Linux 中追加与连接大文件

python - 从 rc.local 运行 python 脚本不写入文件

linux - epoll的实现有没有rfc?

c - 非阻塞套接字(tcp)缓冲区消息逐一处理

c - epoll(EPOLLET)取决于读取字节数?

c - 为什么 EPOLLOUT 会改变 EPOLLIN 的处理方式?

python - manjaro linux 上 opencv 中的 ImportError

c++ - c++资源文件管理

linux - epoll 并一次设置多个兴趣

c - epoll 事件不会引发 SIGIO