I/O多路複用之select,poll,epoll的區別

一、關於select,poll,epoll


種IO模型,都屬於多路IO就緒通知,提供了對大量文件描述符就緒檢查的高性能方案,只不過實現方式有所不同:


select原理概述

調用select時,會發生以下事情:

  1. (1)從用戶空間拷貝fd_set到內核空間;

  2. (2)註冊回調函數__pollwait;

  3. (3)遍歷所有fd,對全部指定設備做一次poll(這裏的poll是一個文件操作,它有兩個參數,一個是文件fd本身,一個是當設備尚未就緒時調用的回調函數__pollwait,這個函數把設備自己特有的等待隊列傳給內核,讓內核把當前的進程掛載到其中);

  4. (4)當設備就緒時,設備就會喚醒在自己特有等待隊列中的【所有】節點,於是當前進程就獲取到了完成的信號。poll文件操作返回的是一組標準的掩碼,其中的各個位指示當前的不同的就緒狀態(全0爲沒有任何事件觸發),根據mask可對fd_set賦值;

  5. (5)如果所有設備返回的掩碼都沒有顯示任何的事件觸發,就去掉回調函數的函數指針,進入有限時的睡眠狀態,再恢復和不斷做poll,再作有限時的睡眠,直到其中一個設備有事件觸發爲止。

  6. 只要有事件觸發,系統調用返回,將fd_set從內核空間拷貝到用戶空間,回到用戶態,用戶就可以對相關的fd作進一步的讀或者寫操作了。


  7. 一個select()系統調用來監視包含多個文件描述符的數組,當select返回,該數組中就緒的文件描述符便會被內核修改標誌位。

  8. select的 跨平臺 做的很好,幾乎每個平臺都支持。


  9. select缺點有以下四點:

  10. (1)單個進程能夠監視的文件描述符的數量存在最大限制

  11. (2)select()所維護的 存儲大量文件描述符的數據結構 ,隨着文件描述符數量的增長,其在用戶態和內核的地址空間的複製所引發的開銷也會線性增長

  12. (3)同時每次調用select都需要在內核遍歷傳遞進來所有的fd,這個開銷在fd很多時很大。

  13. (4)由於網絡響應時間的延遲使得大量TCP連接處於非活躍狀態,但調用select()還是會對 所有的socket進行一次線性掃描 ,會造成一定的開銷


  14. poll:

  15. poll跟select的實現很相似,唯一解決的問題是poll 採用pollfd的結構體指針實現,沒有最大文件描述符數量的限制


  16. epoll原理概述

  17. 調用epoll_create時,做了以下事情:

  18. 內核幫我們在epoll文件系統裏建了個file結點;

  19. 在內核cache裏建了個紅黑樹用於存儲以後epoll_ctl傳來的socket;

  20. 建立一個list鏈表,用於存儲準備就緒的事件。

  21. 調用epoll_ctl時,做了以下事情:

  22. 把socket放到epoll文件系統裏file對象對應的紅黑樹上;

  23. 給內核中斷處理程序註冊一個回調函數,告訴內核,如果這個句柄的中斷到了,就把它放到準備就緒list鏈表裏。

  24. 調用epoll_wait時,做了以下事情:

  25. 觀察list鏈表裏有沒有數據。有數據就返回,沒有數據就sleep,等到timeout時間到後即使鏈表沒數據也返回。而且,通常情況下即使我們要監控百萬計的句柄,大多一次也只返回很少量的準備就緒句柄而已,所以,epoll_wait僅需要從內核態copy少量的句柄到用戶態而已。

  26. epoll優點

  27. (1)支持一個進程打開大數目的socket描述符(FD)

  28. select 最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設置,默認值是2048。對於那些需要支持的上萬連接數目的IM服務器來說顯然太少了。這時候你

  29. 一是可以選擇修改這個宏然後重新編譯內核,不過資料也同時指出這樣會帶來網絡效率的下降,

  30. 二是可以選擇多進程的解決方案(傳統的Apache方案),不過雖然linux上面創建進程的代價比較小,但仍舊是不可忽視的,加上進程間數據同步遠比不上線程間同步的高效,所以也不是一種完美的方案。

  31. epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大於2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關係很大。

  32. (2)IO效率不隨FD數目增加而線性下降

  33. 傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網絡延時,任一時間只有部分的socket是"活躍"的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。

  34. epoll不存在這個問題,它只會對"活躍"的socket進行操作--- 這是因爲在內核實現中epoll是根據每個fd上面的callback函數實現的。那麼,只有"活躍"的socket纔會主動的去調用 callback函數,其他idle狀態socket則不會,在這點上,epoll實現了一個"僞"AIO,因爲這時候推動力在os內核。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll並不比select/poll有什麼效率,相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle
    connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

  35. (3)使用mmap加速內核與用戶空間的消息傳遞

  36. 這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核於用戶空間mmap同一塊內存實現的。而如果你想我一樣從2.5內核就關注epoll的話,一定不會忘記手工mmap這一步的。

  37. (4)內核微調

  38. 這一點其實不算epoll的優點了,而是整個linux平臺的優點。也許你可以懷疑linux平臺,但是你無法迴避linux平臺賦予你微調內核的能力。比如,內核TCP/IP協議棧使用內存池管理sk_buff結構,那麼可以在運行時期動態調整這個內存pool(skb_head_pool)的大小--- 通過echoXXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數的第2個參數(TCP完成3次握手的數據包隊列長度),也可以根據你平臺內存大小動態調整。更甚至在一個數據包面數目巨大但同時每個數據包本身大小卻很小的特殊系統上嘗試最新的NAPI網卡驅動架構。

  39. (5)epoll的除了提供select/poll 那種IO事件的電平觸發(LevelTriggered)外,還提供了邊沿觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。

  40. epoll的缺點:

  41. 缺點要相對來說  拿目前常見幾個網絡模型 select IOCP對比來說缺點有以下幾個:
    1. 相對select來說, epoll的跨平臺性不夠用 只能工作在linux下, 而select可以在windows linux  apple上使用, 還有手機端android iOS之類的都可以.android雖然是linux的內核 但早期版本同樣不支持epoll的.
    2. 相對select來說 還是用起來還是複雜了一些, 不過和IOCP比起來 增加了一點點的複雜度卻基本上達到了IOCP的併發量和性能, 而複雜度遠遠小於IOCP.

    3. 相對IOCP來說 對多核/多線程的支持不夠好,  性能也因此在性能要求比較苛刻的情況下不如IOCP.



  1. 如果在併發量低,socket都比較活躍的情況下,select就不見得比epoll慢了


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章