epoll LT/ET 深度剖析

EPOLL事件的兩種模型:

Level Triggered (LT) 水平觸發

.socket接收緩衝區不為空 有數據可讀 讀事件一直觸發

.socket發送緩衝區不滿 可以繼續寫入數據 寫事件一直觸發

符合思維習慣,epoll_wait返回的事件就是socket的狀態

Edge Triggered (ET) 邊沿觸發

.socket的接收緩衝區狀態變化時觸發讀事件,即空的接收緩衝區剛接收到數據時觸發讀事件

.socket的發送緩衝區狀態變化時觸發寫事件,即滿的緩衝區剛空出空間時觸發讀事件

僅在狀態變化時觸發事件

ET還是LT?

LT的處理過程:

. accept一個連接,添加到epoll中監聽EPOLLIN事件

. 當EPOLLIN事件到達時,read fd中的數據並處理

. 當需要寫出數據時,把數據write到fd中;如果數據較大,無法一次性寫出,那麼在epoll中監聽EPOLLOUT事件

. 當EPOLLOUT事件到達時,繼續把數據write到fd中;如果數據寫出完畢,那麼在epoll中關閉EPOLLOUT事件

ET的處理過程:

. accept一個一個連接,添加到epoll中監聽EPOLLIN|EPOLLOUT事件

. 當EPOLLIN事件到達時,read fd中的數據並處理,read需要一直讀,直到返回EAGAIN為止

. 當需要寫出數據時,把數據write到fd中,直到數據全部寫完,或者write返回EAGAIN

. 當EPOLLOUT事件到達時,繼續把數據write到fd中,直到數據全部寫完,或者write返回EAGAIN

從ET的處理過程中可以看到,ET的要求是需要一直讀寫,直到返回EAGAIN,否則就會遺漏事件。而LT的處理過程中,直到返回EAGAIN不是硬性要求,但通常的處理過程都會讀寫直到返回EAGAIN,但LT比ET多了一個開關EPOLLOUT事件的步驟

LT的編程與poll/select接近,符合一直以來的習慣,不易出錯

ET的編程可以做到更加簡潔,某些場景下更加高效,但另一方面容易遺漏事件,容易產生bug

例子與測試

這裡有兩個簡單的例子演示了LT與ET的用法(其中epoll-et的代碼比epoll要少10行):

github.com/yedf/handy/b

github.com/yedf/handy/b

針對容易觸發LT開關EPOLLOUT事件的情景(讓伺服器返回1M大小的數據),我用ab做了性能測試

測試的結果顯示ET的性能稍好,詳情如下:

LT 啟動命令 ./epoll a

ET 啟動命令 ./epoll-et a

ab 命令:ab -n 1000 -k 127.0.0.1/

LT 結果:Requests per second: 42.56 [#/sec] (mean)

ET 結果:Requests per second: 48.55 [#/sec] (mean)

當我把伺服器返回的數據大小改為48576時,開關EPOLLOUT更加頻繁,性能的差異更大

ab 命令:ab -n 5000 -k 127.0.0.1/

LT 結果:Requests per second: 745.30 [#/sec] (mean)

ET 結果:Requests per second: 927.56 [#/sec] (mean)

對於nginx這種高性能伺服器,ET模式是很好的,而其他的通用網路庫,很多是使用LT,避免使用的過程中出現bug


推薦閱讀:

在有epoll 後,哪些應用場景下還需要select?
大神們來看看這樣架構 使用epoll有沒有問題?
現在 epoll 單機(4G內存)並發量最大能達到多少?

TAG:epoll | CC | 异步IO |