Ant*_*ine 5 linux epoll event-based-programming epollet
我对基于事件的编程很陌生。我正在尝试使用epoll的边缘模式,该模式显然仅信号通知已准备好进行读/写的文件(而不是级别模式,该信号通知所有就绪文件,无论是否已经就绪或刚刚就绪)。
对我来说还不清楚的是:在边缘模式下,我是否得知我不epoll_wait读书时发生的准备事件?尚未恢复的单发文件事件如何处理?
为了说明我为什么要问这个问题,请考虑以下情形:
epoll_ctl为在边缘模式+ oneshot中准备好读取套接字时做出反应:EPOLLET | EPOLLONESHOT | EPOLLINepoll_wait 要发生的事情(最多报告10个事件)read并处理数据套接字#1(直到E_AGAIN)read并处理数据套接字2(直到E_AGAIN)epoll_ctlin EPOLL_CTL_MOD模式重新触发了文件epoll_wait下一批事件好吧,那么是否epoll_wait 总是会通知套接字S已准备就绪?如果S为#1(即不重新设置),是否会发生事件?
我正在尝试使用epoll的边缘模式,该模式显然仅发出已准备好进行读/写操作的信号(与级别模式相反,发出信号通知所有就绪文件的级别模式,无论是否已经就绪或刚刚就绪)
首先,让我们对系统有一个清晰的了解,您需要一个关于系统如何工作的准确的思维模型。您的观点epoll(7)并不十分准确。
边沿触发和电平触发之间的区别在于对事件产生原因的定义。前者为已在文件描述符上预订的每个动作生成一个事件。一旦您使用了该事件,它就消失了-即使您没有使用所有生成此类事件的数据。OTOH,后者会不断重复生成同一事件,直到您消耗掉生成该事件的所有数据为止。
这是一个将这些概念付诸实践的示例,该示例被公然从man 7 epoll以下位置窃取:
代表管道(rfd)读取端的文件描述符在epoll实例上注册。
管道写入器在管道的写入侧写入2 kB的数据。
完成对epoll_wait(2)的调用,该调用将返回rfd作为就绪文件描述符。
管道读取器从rfd读取1 kB数据。
对epoll_wait(2)的调用已完成。
如果已使用EPOLLET(边缘触发)标志将rfd文件描述符添加到epoll接口,则尽管文件输入缓冲区中仍然存在可用数据,但在步骤5中对epoll_wait(2)的调用可能会挂起;同时,远程对等端可能希望根据已发送的数据做出响应。原因是边缘触发模式仅在受监视的文件描述符发生更改时才传送事件。因此,在步骤5中,调用方可能最终等待输入缓冲区中已经存在的某些数据。在上面的示例中,由于在2中完成写操作,并且将在3中使用该事件,因此将在rfd上生成一个事件。由于在4中完成的读取操作不会消耗整个缓冲区数据,因此对epoll_wait(2)的调用已完成在第5步中可能会无限期屏蔽。
简而言之,根本的区别在于“事件”的定义:边缘触发将事件视为您消耗一次的单个单位;级别触发将事件的消耗定义为等同于消耗属于该事件的所有数据。
现在,让我们解决您的特定问题。
在边缘模式下,我是否得知在不epoll_waiting时发生的准备事件
是的,你是。在内部,内核将每个文件描述符上发生的有趣事件排队。它们会在下次致电时返回epoll_wait(2),因此您可以放心,您不会丢失任何事件。好吧,如果还有其他待处理的事件并且传递给事件缓冲区的事件epoll_wait(2)不能容纳所有事件,则可能不完全在下一次调用时发生,但是关键是最终将报告这些事件。
尚未恢复的单发文件事件如何处理?
同样,您永远不会输掉任何事件。如果尚未恢复文件描述符,则在发生任何有趣的事件时,它将仅在内存中排队,直到恢复文件描述符为止。一旦它被重新进入工作状态,任何未决的事件-包括那些描述符被重新进入工作状态之前发生了-将被报告到下一次调用epoll_wait(2)(再次,也许不完全是下一个,但他们会报)。换句话说,EPOLLONESHOT不禁用事件监视,它只是暂时禁用事件通知。
好的,那么是否总是将套接字S的就绪状态通知给最后一个epoll_wait?如果S为#1(即不重新设置),会发生事件吗?
鉴于我上面所说的,现在应该很清楚:是的,它将。您不会丢失任何事件。epoll提供了有力的保证,这很棒。它也是线程安全的,您可以在不同线程中等待同一epoll fd并同时更新事件订阅。epoll非常强大,值得花时间学习它!