我对基于事件的编程很新。我正在试验epoll
的边缘模式,这显然只会发出已准备好进行读/写的文件信号(而不是水平模式,它会发出全部信号)准备好的文件,无论是否已经准备就绪,或者刚刚准备好了。
我不清楚的是:处于边缘模式,我是否知道在我epoll_wait
之前发生的准备事件?那些尚未被重新安装的一次性文件的事件呢?
为了说明我提出这个问题的原因,请考虑以下情况:
epoll_ctl
以便在套接字准备好阅读时做出反应,在边缘模式+ oneshot :EPOLLET | EPOLLONESHOT | EPOLLIN
epoll_wait
发生的事情(报告最多10个事件)read
并处理数据套接字#1(直到E_AGAIN
)read
和处理数据套接字#2(直到E_AGAIN
)epoll_ctl
模式下使用EPOLL_CTL_MOD
重新启动了触发的文件,因为oneshot epoll_wait
下一批事件好的,最后epoll_wait
总是会收到socket S 的准备情况吗?如果 S 为#1(即它没有重新安装),会发生什么事?
答案 0 :(得分:5)
我正在试验epoll的边缘模式,这显然只是信号 已准备好进行读/写的文件(与级别模式相反) 它会发出所有就绪文件的信号,无论是否存在 已准备好,或刚刚准备好了)
首先,让我们清楚地了解系统,您需要一个准确的心理模型来了解系统的工作原理。您对epoll(7)
的看法并不准确。
边缘触发和水平触发之间的区别在于对事件的确切定义。前者为已在文件描述符上订阅的每个操作生成一个事件;一旦你消耗了这个事件,它就会消失 - 即使你没有消耗产生这种事件的所有数据。 OTOH,后者一直反复生成相同的事件,直到你消耗了生成事件的所有数据。
这是一个将这些概念付诸实践的例子,从man 7 epoll
公然被盗:
表示管道读取端(rfd)的文件描述符已在epoll实例上注册。
管道写入器在管道的写入端写入2 kB数据。
完成对epoll_wait(2)的调用,将rfd作为就绪文件描述符返回。
管道读取器从rfd读取1 kB数据。
- 醇>
完成了对epoll_wait(2)的调用。
如果已将rfd文件描述符添加到epoll接口使用 EPOLLET(边缘触发)标志,对epoll_wait(2)的调用完成 尽管仍然存在可用数据,但步骤5可能会挂起 文件输入缓冲区;同时远程同伴可能会期待一个 根据已发送的数据做出响应。原因是 边缘触发模式仅在发生更改时才传递事件 受监视的文件描述符。因此,在步骤5中,呼叫者可能会结束 等待输入缓冲区中已存在的一些数据。 在上面的示例中,将生成rfd上的事件,因为 写入2完成并且事件在3中消耗。自读取 在4中完成的操作不消耗整个缓冲区数据,即调用 在步骤5中完成的epoll_wait(2)可能会无限期地阻止。
简而言之,根本区别在于"事件":边缘触发将事件视为您消耗一次的单个单位; level-triggered定义事件的消耗等同于消耗属于该事件的所有数据。
现在,尽管如此,让我们解决您的具体问题。
在边缘模式下,我被告知在我的时候发生的准备事件 不是epoll_waiting
是的,你是。在内部,内核将每个文件描述符上发生的有趣事件排队。他们会在下次致电epoll_wait(2)
时退回,因此您可以放心,您不会丢失任何活动。好吧,如果还有其他事件未决,并且传递给epoll_wait(2)
的事件缓冲区无法容纳所有事件,那么也许不会完全在下一次调用中,但关键是,最终将报告这些事件。
有关未经重新安装的一次性文件的事件呢?
同样,你永远不会失去事件。如果文件描述符还没有被重新安装,如果出现任何有趣的事件,它只是在内存中排队,直到文件描述符被重新引导。一旦 重新启动,任何待处理事件 - 包括在描述符被重新启动之前发生的事件 - 将在下一次调用epoll_wait(2)
时报告(再次,可能不完全是下一个,但是他们将被报道)。换句话说,EPOLLONESHOT
不会禁用事件监控,它只是暂时禁用事件通知。
好的,最后一次epoll_wait也会被告知准备好了 插座S?如果S是#1(即它没有重新安装),则会发生事件?
鉴于我上面所说的,现在应该很清楚:是的,它会。你不会输掉任何比赛。 epoll提供强有力的保证,非常棒。它也是线程安全的,您可以在不同的线程中等待相同的epoll fd并同时更新事件订阅。 epoll非常强大,非常值得花时间学习它!