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)

 

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