ET和LT的原理和区别

前言

学习自用,有错麻烦提出,感谢

基本知识

ET和LT是IO复用的两种模式,ET早于LT出现(依据TODO)

对于几种IO复用,select和poll只支持LT(?TODO)

level triggered 和edge trigger,起源于电频的激发,TODO

ET是一次事件只会触发一次,如一次客户吨发来消息,fd可读,epoll_wait返回.等下次再调用epoll_wait则不会返回了

LT是一次事件会触发多次,如一次客户端发消息,fd可读,epoll_wait返回,不处理这个fd,再次调用epoll_wait,立刻返回,

LT和ET的内部实现

对于epoll,每次返回会有个ready list,以下参考自[1],epoll对于事件到来,是采用回调的机制来把fd加进ready_list中的,(后面有提到)

对于ET模式,事件到来后,回调函数把触发的事件的fd加入ready list中,

对于LT模式,还有个额外的操作,即对所有监听的(即红黑树里的)socket再poll以判断是否事件未被处理(来自参考资料[9]),如果事件未被处理,则重新再把事件对应的fd加入ready list中

以上可看出,ET比LT的高效点在于这里,论单纯的ET和LT调用的开销很容易看出ET较为高效,而若要算上应用层开销复杂度,则另当别论,详情往下读

LT和ET的应用场景和使用方法

使用方法

ET要与非阻塞fd一起使用,因为ET一次事件只触发一次,所以epoll_wait返回后一定要处理完毕,对于可读事件,要一直read fd到此fd被read完为止,而如果设置成blocking以后,fd上的数据read完后会阻塞,即while{epoll_wait(); read(fd)}这段代码会一直阻塞而影响重新调用epoll_wait来监听其他事件,正确做法是设置fd成non_blocking,且epoll_wait返回后吧事件read到EAGAIN为止

LT可搭配非阻塞也可搭配阻塞使用(证据TODO)

应用场景

LT的编程会比ET的编程更简洁的场景

对于可读事件,ET模式下的编程需要read到EAGAIN位置,发来的数据量多且并发量大的时候,还可能造成其他事件的饥饿,需要在应用层再额外代码以保证及时响应,而LT可直接每个消息事件read固定大小以保证每个连接公平,数据量大的且没读完的下次还会继续触发,(ET用应用层维护的例子TODO)

对于写事件,ET会实现更简单高效,例子:

要write1M数据,而缓冲区只有2kb,则需要epoll_wait() 可写事件EPOLLOUT,对于ET模式,等写完后则直接就可以了

而如果是LT模式对付这样场景,在写完后,需要再调用一次epoll_ctl来删去EPOLLOUT事件,否则下次调用epoll_wait还是会继续触发返回可写事件,具体代码可看参考资料[4]中文末位置的链接

可以看出,对于EPOLLOUT可写事件,用ET更高效

上面讲的可读事件和可写事件分别用LT和ET的代码实验,可见参考资料[5]

服务器场景

对于服务器编程,要处理三个半事件,这里先讲可读和连接到来事件.并讲下缓冲区满的可写事件

muduo所使用的连接到来事件(acceptor)是LT,对于消息到来(poller)也是LT模式..先不讨论为什么这么设计,因为陈硕还有其他要考虑的方面TODO

而Nginx,listen fd用的是LT来监听,connection fd用的是ET来监听,做实验这样组合并发度最高

listen fd用LT的原因:来自[7],若使用ET(边缘触发)模式,则非常可能有两个连接请求因为太靠近,而只accept()了其中一个(why,这是用户bug还是系统API的bug??)

此博客原话:这种情况要不就修改为LT,要不就继续ET模式,但listen socket为NOBLOCK模式。 accept()不断接收,直到返回 EAGAIN or EWOULDBLOCK。

connection socket用LT并发量高的原因:

TODO

另外ET和LT哪个更高效的讨论见参考资料[8]中评论的连接,评论中都是讨论,以后有时间再总结吧..TODO

epoll的实现

主要来源于参考资料[6]和[1]

直接说两个重要结构,红黑树和双向链表readylist

红黑树

功能:维护监听的fd和事件,方便添加删除时间复杂度都是logn水平,用来检查的fd添加是否重复

结构:key是fd,value是epitem结构

双向链表readylist,用于记录已发生的事件,并传输给应用层,

readylist中每个节点也是epitem对象吧?TODO

epoll_ctl流程:设置某个事件如网卡有数据的事件处理回调函数为添加fd到readylist

return_type epoll_ctl(epoll_event,fd)  //省略类型
{
    res = rbtree.find(fd);
    if(res != rbtree.end() ) return;
    rbtree[fd]=对应结构;
    设置事件回调函数(如连接到来事件,消息到来事件)为添加事件fd到readylist
}

epitem的封装:TODO

select和epoll

这里和标题不符,但是还是放进来了,

为什么连接少又活跃的时候应该选择select而不是epoll,是什么实现原理导致的呢? TODO

参考资料

[1](https://www.cnblogs.com/charlesblc/p/6242479.html)

[2](https://www.zhihu.com/question/20502870)

[3](https://www.zhihu.com/question/47002053)

[4](https://www.zhihu.com/question/20502870/answer/89738959)

[5]https://www.zhihu.com/question/47002053/answer/794254562

[6](https://zhuanlan.zhihu.com/p/63179839)

[7](http://blog.sina.com.cn/s/blog_602f87700102y2ob.html)

[8](http://www.cppblog.com/Leaf/archive/2013/02/25/198061.html)

[9](https://www.cnblogs.com/bbqzsl/p/7060819.html)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章