实测epoll的LT和ET模式

epoll有两种工作模式:LT(Level-Trigge)和ET(Edge-Trigger),LT为缺省模式。

  • LT:无限次地“驱动”事件处理逻辑去处理事件,直到事件被处理“干净”。未被处理“干净”的事件是指,事件未被处理,或事件被处理了,但尚未处理完全。例如,接收缓冲区有数据就绪,此时,事件处理逻辑不recv该数据,或只recv了一部分,都属于处理不干净。

  • ET:一个事件发生时,只”驱动“一次事件处理逻辑。处理逻辑若不把此事处理”干净“,以后将不再有机会。当然,这次事件所对应的就绪数据是不会被清除的,它还在那。如果又有新的数据就绪,那这块未被及时处理的数据,还是有机会被处理的。

这次基于《逐步实现TCP服务端Step04-8:epoll》的代码,测试LT和ET模式的执行效果。下面的几个case分别是对ET和LT模式的相关测试,与ET相关的case以ET开头,同理以LT为前缀的是关于LT的测试。代码中加入了一系列宏,以方便开关不同的case,例如,要测试ET_Case1,就在base.h文件中,将__TEST__和__ET_CASE1__打开,代码详见imp_tcp_server工程的test_epoll_lt_et分支。

先测试ET模式下的几个行为:

首先,为方便观察,在测试时,将epoll_wait的等待时间设置为5秒。
启用ET模式,对所有新产生的socket,都注册上EPOLLET事件。修改NControl::EPollInit中的代码:

ET_Case1:读事件(EPOLLIN)发生,做recv操作,且把就绪的数据都recv完。

这是使用ET模式时最好的情况。
图中”Call epoll_wait cnt”指累计调用epoll_wait的次数,或者说是第几次调用epoll_wait函数,”retval”是当次调用的返回值。可见,client发来数据后,epoll_wait监测到有就绪事件,待事件处理逻辑将数据全部recv出来之后,下次调用epoll_wait将不再有就绪事件。

ET_Case2:读事件(EPOLLIN)发生,但不做recv操作。

这次不立即处理到来的事件。
在第三次调用epoll_wait时,监测到了事件,但是事件处理逻辑不予理会,在下次调用时,epoll_wait将对其忽略。

ET_Case3:读事件(EPOLLIN)发生,做recv操作,但只recv部分数据。

为达到只recv部分数据的效果,需将程序中的接收缓冲区设置的小一点。小到不足以存放一个完整的code即可。假设client只向netio发送一个英文字符,那么这个字符在被包装成Echo消息之后,其总长度将变为17个字节。将接收缓冲区的值设置为10个字节,修改base.h文件中的宏。

这是测试结果:
由于程序中的接受缓冲区不足以放下完整的code,此次recv之后,就绪的数据还有残留。但是,在后续调用中,epoll_wait将忽略此事。

ET_Case4:第一个读事件(EPOLLIN)发生,不做recv,或做recv但不将数据recv完。待第二个读事件发生再做recv,且把数据全部recv出来。

对第一个事件不予理会,待第二个事件发生时,再做recv操作,且recv完所有数据。
第一个事件发生,处理逻辑不做recv操作,则后续的epoll_wait调用将忽略此事。而当第二个事件发生时,处理逻辑将就绪的数据全部recv出来,此时,处理逻辑解析出来两个消息,这说明ET模式下epoll_wait只是忽略已通知过但未处理完的事件,而不会将相应的就绪数据清除。

对于LT模式,只做一个测试:

LT_Case1:读事件(EPOLLIN)发生,但不做recv操作。

此时,epoll_wait将一直关注该事件,直到其被处理干净。
进行recv但未recv完全的情况,与该情况的行为一致,不再做测试。