redis自学(19)epoll事件通知机制
epoll事件通知机制
当FD有数据可读时,我们调用epoll_wait就可以得到通知。但是事件通知的模式有两种:
- LevelTriggered:简称LT。当FD有数据可读时,会重复通知多次,直到数据处理完成。是epoll的默认模式。
- EdgeTriggered:简称ET。当FD有数据可读时,只会被通知一次,不管数据是否处理完成。
LT举个例子:
① 假设一个客户端socket对应的FD已经注册到了epoll实例中
② 客户端socket发送了2kb的数据
③ 服务端调用epoll_wait,得到通知说FD就绪
④ 服务端从FD读取了1kb数据
⑤ 回到步骤3(再次调用epoll_wait,形成循环)
而ET的话,再次步骤3的时候,就不会通知就绪了。
内部实现差异:
调用epoll_wait的时候,list_head会断开与链表的连接,在链表拷贝到过用户态后,会判断是ET还是LT,ET的话,就不恢复连接了,那么list_head就会是空;而LT的话,如果链表里面还有数据,就会恢复连接。
ET如何解决数据读取问题?
第一种方式:手动LT,也就是说,断开连接拷贝完数据后,如果还有数据,就会调用epoll_ctl,判断哪些数据就绪了,然后再添加到list_head
第二种方式:在步骤4循环读取,全都读完为止(不能用阻塞IO的模式去读取,阻塞IO读完了不是返回错误,而是会等待,导致进程被阻塞;而非阻塞IO,有数据返回,没数据返回无数据的标识)
LT的问题:
重复通知对于效率和性能会有影响
LT可能会出现惊群的现象(在多线程的情况下,都在调用epoll_wait获取就绪的FD,而因为任何一个进程它通知完了一个,因为FD还在list_head里面,所以其他监听FD的进程都会被通知到,其实有可能就绪FD就被第一个或者第二个进程处理完了, 后续的进程没有必要被唤醒,而ET就没有这个情况)
结论:
- ET模式避免了LT模式可能出现的惊群现象
- ET模式最好结合非阻塞IO读取FD数据,相比LT会复杂一些(性能会更好一些)