select、poll、epoll總結
一、select總結
select本質上是通過設定或者檢查存放fd標誌位的資料結構來進行下一步處理。它僅僅知道了,有I/O事件發生了,卻並不知道是哪那幾個流(可能有一個,多個,甚至全部),我們只能無差別輪詢所有流,找出能讀出資料,或者寫入資料的流,對他們進行操作。所以select具有O(n)的無差別輪詢複雜度,同時處理的流越多,無差別輪詢時間就越長。
【優點】:
- select()的可移植性更好,在某些Unix系統上不支援poll()和epoll();
- select() 對於超時值提供了更好的精度:微秒,而poll是毫秒。
【缺點】:
- 單個程序可監視的fd數量被限制(FD_SIZE為1024);
- 需要維護一個用來存放大量fd的資料結構,這樣會使得使用者空間和核心空間在傳遞該結構時複製開銷大;
- 對fd進行掃描時是線性掃描。fd劇增後,IO效率較低,因為每次呼叫都對fd進行線性掃描遍歷,所以隨著fd的增加會造成遍歷速度慢的效能問題;
- select() 函式的超時引數在返回時也是未定義的,考慮到可移植性,每次在超時之後在下一次進入到select之前都需要重新設定超時引數。
二、poll總結
poll與select不同,通過一個pollfd陣列向核心傳遞需要關注的事件,故沒有描述符個數的限制,pollfd中的events欄位和revents分別用於標示關注的事件和發生的事件,故pollfd陣列只需要被初始化一次
poll的實現機制與select類似,其對應核心中的sys_poll,只不過poll向核心傳遞pollfd陣列,然後對pollfd中的每個描述符進行poll,相比處理fdset來說,poll效率更高。poll返回後,需要對pollfd中的每個元素檢查其revents值,來得指事件是否發生。
【優點】:
- poll() 不要求開發者計算最大檔案描述符加一的大小;
- poll() 在應付大數目的檔案描述符的時候相比於select速度更快;
- 它沒有最大連線數的限制,原因是它是基於連結串列來儲存的。
【缺點】:
- 大量的fd的陣列被整體複製於使用者態和核心地址空間之間,而不管這樣的複製是不是有意義;
- 與select一樣,poll返回後,需要輪詢pollfd來獲取就緒的描述符。
三、epoll總結
執行epoll_ create時,建立了紅黑樹和就緒連結串列,執行epoll_ ctl時,如果增加socket控制代碼,則檢查在紅黑樹中是否存在,存在立即返回,不存在則新增到樹幹上,然後向核心註冊回撥函式,用於當中斷事件來臨時向準備就緒連結串列中插入資料。執行epoll_wait時立刻返回準備就緒連結串列裡的資料即可。
epoll可以理解為event poll,不同於忙輪詢和無差別輪詢,epoll會把哪個流發生了怎樣的I/O事件通知我們。所以我們說epoll實際上是事件驅動(每個事件關聯上fd)的,此時我們對這些流的操作都是有意義的。(複雜度降低到了O(1))。
【優點】:
- 沒有最大併發連線的限制,能開啟的FD的上限遠大於1024(1G的記憶體上能監聽約10萬個埠);
- 效率提升,不是輪詢的方式,不會隨著FD數目的增加效率下降。只有活躍可用的FD才會呼叫callback函式;即Epoll最大的優點就在於它只管你“活躍”的連線,而跟連線總數無關,因此在實際的網路環境中,Epoll的效率就會遠遠高於select和poll;
- 記憶體拷貝,利用mmap()檔案對映記憶體加速與核心空間的訊息傳遞;即epoll使用mmap減少複製開銷。
四、三種方式總結
-
select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而非同步I/O則無需自己負責進行讀寫,非同步I/O的實現會負責把資料從核心拷貝到使用者空間。
-
select,poll實現需要自己不斷輪詢所有fd集合,直到裝置就緒,期間可能要睡眠和喚醒多次交替。 而epoll其實也需要呼叫 epoll_ wait不斷輪詢就緒連結串列,期間也可能多次睡眠和喚醒交替,但是它是裝置就緒時,呼叫回撥函式,把就緒fd放入就緒連結串列中,並喚醒在 epoll_wait中進入睡眠的程序。雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的 時候只要判斷一下就緒連結串列是否為空就行了,這節省了大量的CPU時間,這就是回撥機制帶來的效能提升。
-
select,poll每次呼叫都要把fd集合從使用者態往核心態拷貝一次,並且要把current往裝置等待佇列中掛一次,而epoll只要一次拷貝,而且把current往等待佇列上掛也只掛一次(在epoll_wait的開始,注意這裡的等待佇列並不是裝置等待佇列,只是一個epoll內 部定義的等待佇列),這也能節省不少的開銷。
【適用情況】:
表面上看epoll的效能最好,但是在連線數少並且連線都十分活躍的情況下,select和poll的效能可能比epoll好,畢竟epoll的通知機制需要很多函式回撥。
select低效是因為每次它都需要輪詢。但低效也是相對的,視情況而定,也可通過良好的設計改善。
參考:https://www.cnblogs.com/aspirant/p/9166944.html
https://blog.csdn.net/lixungogogo/article/details/52226501