当不在epoll_waiting时,是否仍然监视epoll事件?

9

我对基于事件的编程还不是很熟悉。我正在尝试使用epoll边缘触发模式,它似乎只会通知已准备好进行读/写操作的文件(与水平触发模式相反,后者会通知所有已准备好的文件,无论它们是否已经准备好或者刚刚准备好)。

对我来说不清楚的是:在边缘触发模式下,当我没有执行epoll_wait时,是否会通知我就绪事件?未重新启用的一次性文件的事件呢?

为了说明我提出这个问题,考虑以下情况:

  • 有10个非阻塞套接字连接
  • epoll_ctl配置为在准备读取时响应套接字,在边缘触发模式+一次性触发模式下:EPOLLET | EPOLLONESHOT | EPOLLIN
  • epoll_wait等待发生事件(最多报告10个事件)
  • linux唤醒我的进程并报告套接字#1和#2已准备好
  • 我读取并处理数据套接字#1(直到E_AGAIN
  • 我读取并处理数据套接字#2(直到E_AGAIN
  • 当我这样做时,一个套接字S收到了数据
  • 我处理完所有事件,因此使用EPOLL_CTL_MOD模式重新启用触发文件,因为它是一次性的
  • 我的循环返回到等待下一批事件的epoll_wait

好的,那么最后一个epoll_wait会始终通知我套接字S已准备就绪吗?即使S是#1(即它没有被重新启用)?

1个回答

11
我正在尝试使用epoll的边缘触发模式,这种模式仅会通知已准备好进行读/写操作的文件(相对于水平触发模式,无论文件是否已经准备就绪或刚刚准备好,都会通知所有已准备好的文件)。
首先需要清楚地了解系统,你需要准确的心理模型来了解系统的工作原理。你对epoll(7)的理解并不准确。
边缘触发和水平触发的区别在于对事件定义的精确定义。前者为每个在文件描述符上订阅的动作生成一个事件;一旦消费了该事件,它就会消失 - 即使您没有消费掉生成此类事件的所有数据。而后者会不断生成相同的事件,直到您消费了生成事件的所有数据。
以下是一个将这些概念应用到实践中的示例,直接从man 7 epoll中抄袭:
1.将表示管道读端的文件描述符(rfd)注册到epoll实例中。 2.管道写入者在管道写端上写入2 kB的数据。 3.调用epoll_wait(2),返回rfd作为一个准备好的文件描述符。 4.管道读取器从rfd中读取1 kB的数据。 5.调用epoll_wait(2)。
如果使用EPOLLET(边缘触发)标志将rfd文件描述符添加到epoll接口中,则步骤5中对epoll_wait(2)的调用可能会挂起,尽管文件输入缓冲区中仍然有可用数据;同时,远程对等方可能会根据其已发送的数据期望获得响应。原因是边缘触发模式仅在监视文件描述符上发生更改时才提供事件。因此,在步骤5中,调用者可能会被阻塞,而不会收到新的事件通知,直到下一次发生文件描述符的更改。已经存在于输入缓冲区中的某些数据吗?
在上面的例子中,由于步骤2中的写操作和步骤3中的事件而生成了rfd上的事件。由于步骤4中的读操作没有消耗整个缓冲区数据,因此在步骤5中调用的epoll_wait(2)可能会无限期地阻塞。

简而言之,基本区别在于“事件”的定义:边沿触发将事件视为一次性消耗的单个单位;水平触发定义事件的消耗等同于消耗属于该事件的所有数据。
现在,既然已经讲清楚了这一点,让我们来回答您具体的问题。
在边沿模式下,我是否会收到在我不处于epoll_waiting状态时发生的准备就绪事件通知?
是的,你会。在内部,内核会将每个文件描述符上发生的有趣事件排队。它们会在下一次调用epoll_wait(2)时返回,因此您可以放心,不会丢失事件。如果有其他待处理事件并且传递给epoll_wait(2)的事件缓冲区无法容纳它们所有,则可能不会恰好在下一次调用上返回,但重点是,这些事件最终将报告。
对于尚未被重新装载的一次性文件的事件呢?
同样地,你永远不会丢失事件。如果文件描述符尚未被重新装载,则任何有趣的事件都会简单地排队在内存中,直到文件描述符被重新加载。一旦它被重新装备,任何挂起的事件(包括发生在描述符重新装备之前的事件)将在下次调用epoll_wait(2)时报告(同样地,可能不是恰好下一个,但它们将被报告)。换句话说,EPOLLONESHOT并不会禁用事件监视,它只是暂时禁用事件通知。
好的,那么最后一个epoll_wait是否总是会通知准备就绪呢?

根据我上面说的,现在应该很清楚了:是的,它会。你不会丢失任何事件。 epoll提供了强大的保证,非常棒。它也是线程安全的,你可以在不同的线程上等待同一个epoll fd,并且可以同时更新事件订阅。 epoll非常强大,值得花时间学习!


很棒的回答,谢谢。我会像你说的那样花时间学习它的 ;) - Antoine
1
@Antoine 当然,请继续!这非常有趣,你会喜欢的。我很高兴能帮忙 :) - Filipe Gonçalves
@FilipeGonçalves:Filipe,这是一个很棒的答案。不过有一个重要的问题:在一次性边沿触发模式下,一旦描述符被重新装载,如果自上次解除武装以来发生了多个事件,线程会收到每个事件的通知,还是只会收到整个事件集合的一次通知? - user541686

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接