1. 程式人生 > >select,poll,epoll的歸納總結區分

select,poll,epoll的歸納總結區分

1 Select、Poll與Epoll簡介

Select

select本質上是通過設定或者檢查存放fd標誌位的資料結構來進行下一步處理。這樣所帶來的缺點是:

1 單個程序可監視的fd數量被限制

2 需要維護一個用來存放大量fd的資料結構,這樣會使得使用者空間和核心空間在傳遞該結構時複製開銷大

3 對socket進行掃描時是線性掃描

Poll

poll本質上和select沒有區別,它將使用者傳入的陣列拷貝到核心空間,然後查詢每個fd對應的裝置狀態,如果裝置就緒則在裝置等待佇列中加入一項並繼續遍歷,如果遍歷完所有fd後沒有發現就緒裝置,則掛起當前程序,直到裝置就緒或者主動超時,被喚醒後它又要再次遍歷fd。這個過程經歷了多次無謂的遍歷。

它沒有最大連線數的限制,原因是它是基於連結串列來儲存的,但是同樣有一個缺點:大量的fd的陣列被整體複製於使用者態和核心地址空間之間,而不管這樣的複製是不是有意義。

poll還有一個特點是“水平觸發”,如果報告了fd後,沒有被處理,那麼下次poll時會再次報告該fd。

Epoll

epoll支援水平觸發和邊緣觸發,最大的特點在於邊緣觸發,它只告訴程序哪些fd剛剛變為就需態,並且只會通知一次。

在前面說到的複製問題上,epoll使用mmap減少複製開銷。

還有一個特點是,epoll使用“事件”的就緒通知方式,通過epoll_ctl註冊fd,一旦該fd就緒,核心就會採用類似callback的回撥機制來啟用該fd,epoll_wait便可以收到通知

注:水平觸發(level-triggered)——只要滿足條件,就觸發一個事件(只要有資料沒有被獲取,核心就不斷通知你);邊緣觸發(edge-triggered)——每當狀態變化時,觸發一個事件。

2 Select、Poll與Epoll區別

Select

Poll

Epoll

支援最大連線數

1024(x86) or 2048(x64)

無上限

無上限

IO效率

每次呼叫進行線性遍歷,時間複雜度為O(N)

每次呼叫進行線性遍歷,時間複雜度為O(N)

使用“事件”通知方式,每當fd就緒,系統註冊的回撥函式就會被呼叫,將就緒fd放到rdllist裡面,這樣epoll_wait返回的時候我們就拿到了就緒的fd。時間發覆雜度O(1)

fd拷貝

每次select都拷貝

每次poll都拷貝

呼叫epoll_ctl時拷貝進核心並由核心儲存,之後每次epoll_wait不拷貝

3 使用

當同事需要保持很多的長連線,而且連線的開關很頻繁時,就能夠發揮epoll最大的優勢了。這裡與伺服器模型其實已經有些交集了。

同時需要保持很多的長連線,而且連線的開關很頻繁,最高效的模型是非阻塞、非同步IO模型。而且不要用select/poll,這兩個API的有著O(N)的時間複雜度。在Linux用epoll,BSD用kqueue,Windows用IOCP,或者用libevent封裝的統一介面(對於不同平臺libevent實現時採用各個平臺特有的API),這些平臺特有的API時間複雜度為O(1)。 然而在非阻塞,非同步I/O模型下的程式設計是非常痛苦的。由於I/O操作不再阻塞,報文的解析需要小心翼翼,並且需要親自管理維護每個連結的狀態。並且為了充分利用CPU,還應結合線程池,避免在輪詢執行緒中處理業務邏輯。

但這種模型的效率是極高的。以知名的http伺服器nginx為例,可以輕鬆應付上千萬的空連線+少量活動連結,每個連線連線僅需要幾K的核心緩衝區,想要應付更多的空連線,只需簡單的增加記憶體(資料來源為淘寶一位工程師的一次技術講座,並未實測)。這使得DDoS攻擊者的成本大大增加,這種模型攻擊者只能將伺服器的頻寬全部佔用,才能達到目的,而兩方的投入是不成比例的。

注:長連線——連線後始終不斷開,然後進行報文傳送和接受;短連結——每一次通訊都建立連線,通訊完成即斷開連線,下次通訊再建立連線。

4 引用源