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在大量fd情況下優勢的直觀展示。
表格左側是描述符集合的大小,右側分別表示1s對poll和epoll的調用次數,也就是性能瓶頸。
從上表可以看出當fd數量較少的時候poll略優于epoll,但是當fd增大到某個閾值時,poll性能急劇下降。而epoll始終保持的穩定的性能。
4 使用
當同事需要保持很多的長連接,而且連接的開關很頻繁時,就能夠發揮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攻擊者的成本大大增加,這種模型攻擊者只能將服務器的帶寬全部占用,才能達到目的,而兩方的投入是不成比例的。
注:長連接——連接后始終不斷開,然后進行報文發送和接受;短鏈接——每一次通訊都建立連接,通訊完成即斷開連接,下次通訊再建立連接。
總結
以上是生活随笔為你收集整理的select,poll,epoll的归纳总结区分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SSH-CLIENT : gSTM
- 下一篇: 第十一章 图形视图、动画、状态机框架