处于边缘触发模式的epoll是一种奇怪的野兽。它要求流程跟踪每个被监控FD的最后响应是什么。它授权流程处理每个报告的事件(否则我们可能会认为FD没有报告任何事情,而实际上,它被边缘触发行为抑制了)。
边缘触发epoll有什么意义?
发布于 2017-10-08 17:50:36
我所知道的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侦听以停止侦听EPOLLIN。
这意味着拥有可用数据的read将在1个系统调用内完成,但返回EAGAIN的读将至少接受4系统调用(原始read、两个EPOLL_CTL_MOD和成功的最终read )。
请注意,上面没有计算也必须发生的epoll_wait。我不算它,因为我有一个慷慨的假设,即其他光纤也会在同一个系统调用中被唤醒,所以把它的成本完全归因于我们的光纤是不公平的。总之,这种机制需要4+x系统调用,其中x介于0和1之间。
降低成本的一种方法是使用EPOLLONESHOT。这样做可以将fd从自动监控中删除,从而降低3+x的成本。更好,但我们还可以做得更好。
输入EPOLLET。以前的fd状态可以是武装的,也可以是非武装的(也就是说,下一个事件是否会触发epoll)。此外,fd目前(在read的入口点)可能或可能没有准备好数据。四个州。我们把它们摊开。
就绪(无论是否已武装):对read的第一个调用将返回数据。1系统呼叫。这条路不会改变武装状态,准备状态取决于我们是否阅读了所有内容。
还没有准备好(不管是否已准备好):对read的第一个调用返回EAGAIN,从而武装fd。我们在wait_event中睡觉,而不必执行另一个系统调用。一旦我们醒来,我们就处于非武装状态(就像我们刚刚醒来一样)。因此,我们不需要调用epoll_ctl来禁用fd的侦听功能。我们称之为read,它返回数据。我们要么准备好,要么不准备,但没有武器。
总成本: 2+x。
我们将不得不面对一个虚假的唤醒每fd,因为fd开始武装。我们的代码必须处理epoll报告没有光纤监听的fd的情况。在这种情况下,处理只是意味着忽略并继续前进。FD不会再被伪造的报道。
https://stackoverflow.com/questions/46633433
复制相似问题