前幾天回答一個(gè)問(wèn)題,是關(guān)于我們項(xiàng)目中使用的epoll模式的,因?yàn)橛洸淮笄辶耍杏X(jué)應(yīng)該使用的就是epoll的高速模式,也就是ET(edge-trigger)模式。這兩天閑暇的時(shí)候,打開(kāi)代碼又看了一下,在epoll事件注冊(cè)時(shí)并未標(biāo)記ET模式,看來(lái)實(shí)際使用的是epoll默認(rèn)的LT(level-trigger )模式,為什么呢?使用LT意味著 只要 fd 處于 readable/writable 狀態(tài),每次 epoll_wait 時(shí)都會(huì)返回該 fd,系統(tǒng)開(kāi)銷不說(shuō),自己處理時(shí)每次都要把這些fd輪詢一遍,如果fd很多的話,不管這些fd有沒(méi)有事件發(fā)生,epoll_wait 都會(huì)觸發(fā)這些fd的輪詢判斷。
查閱了一些資料,才知道常用的事件處理庫(kù)很多都選擇了 LT 模式,包括大家熟知的libevent和boost::asio等,為什么選擇LT呢?那就不得不從ET的弊端的弊端說(shuō)起。
ET模式下,當(dāng)有事件發(fā)生時(shí),系統(tǒng)只會(huì)通知你一次,也就是調(diào)用epoll_wait 返回fd后,不管事件你處理與否,或者處理完全與否,再調(diào)用epoll_wait 時(shí),都不會(huì)再返回該fd,這樣programmer要自己保證在事件發(fā)生時(shí)及時(shí)有效的處理完。比如此時(shí)fd發(fā)生了EPOLLIN事件,在調(diào)用epoll_wait 后發(fā)現(xiàn)此事件,programmer要保證在本次輪詢中對(duì)此fd進(jìn)行了讀操作,并且還要循環(huán)調(diào)用recv操作,一直讀到recv的返回值小于請(qǐng)求值,或者遇到EAGAIN錯(cuò)誤,不然下次輪詢時(shí),如果此fd沒(méi)有再次觸發(fā)事件,你就沒(méi)有機(jī)會(huì)知道這個(gè)fd需要你的處理。這樣無(wú)形中就增加了programmer的負(fù)擔(dān)和出錯(cuò)的機(jī)會(huì)。
ET模式的短處正是LT模式的長(zhǎng)處,無(wú)論此fd是否有事件發(fā)生,或者有事件未處理完,每次epoll_wait 時(shí)總會(huì)得到此fd供你處理。顯而易見(jiàn),OS在LT模式下維護(hù)的 ready list 的大小肯定比ET模式下長(zhǎng),而且你自己輪詢所有的fd時(shí)也要比ET下要多,這種消耗和ET模式下循環(huán)調(diào)用處理函數(shù)(如recv和send等),還要邏輯處理是否處理完畢,理論上應(yīng)該是LT更大一些,不過(guò)個(gè)人感覺(jué)應(yīng)該差別不會(huì)太大。但是LT模式下帶來(lái)的邏輯處理的方便性和不易出錯(cuò)性,讓我們有理由把它作為首選。我想這可能也是為什么epoll后來(lái)在ET的基礎(chǔ)上又增加了LT,并且將其作為默認(rèn)模式的原因吧。
peakflys 上述觀點(diǎn),歡迎 志同道合或志同道不合的朋友拍磚。
PS:文中一味寫(xiě)LT的好處,沒(méi)有說(shuō)LT 極易引起的寫(xiě)觸發(fā) 頻繁通知的問(wèn)題,具體大家可以參考評(píng)論部分,再次感謝大家的指教。