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