1. 程式人生 > >網路通訊 :IO多路複用之select、poll、epoll詳解

網路通訊 :IO多路複用之select、poll、epoll詳解

 目前支援I/O多路複用的系統呼叫有 select,pselect,poll,epoll,I/O多路複用就是通過一種機制,一個程序可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作但select,pselect,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而非同步I/O則無需自己負責進行讀寫,非同步I/O的實現會負責把資料從核心拷貝到使用者空間。

     與多程序和多執行緒技術相比,I/O多路複用技術的最大優勢是系統開銷小,系統不必建立程序/執行緒

,也不必維護這些程序/執行緒,從而大大減小了系統的開銷。

 

一、使用場景

IO多路複用是指核心一旦發現程序指定的一個或者多個IO條件準備讀取,它就通知該程序。IO多路複用適用如下場合:

  1)當客戶處理多個描述符時(一般是互動式輸入和網路套介面),必須使用I/O複用。

  2)當一個客戶同時處理多個套介面時,這種情況是可能的,但很少出現。

  3)如果一個TCP伺服器既要處理監聽套介面,又要處理已連線套介面,一般也要用到I/O複用。

  4)如果一個伺服器即要處理TCP,又要處理UDP,一般要使用I/O複用。

  5)如果一個伺服器要處理多個服務或多個協議,一般要使用I/O複用。

 

二、select、poll、epoll簡介

  epoll跟select都能提供多路I/O複用的解決方案。在現在的Linux核心裡有都能夠支援,其中epoll是Linux所特有,而select則應該是POSIX所規定,一般作業系統均有實現。

1、select

基本原理:select 函式監視的檔案描述符分3類,分別是writefds、readfds、和exceptfds。呼叫後select函式會阻塞,直到有描述符就緒(有資料 可讀、可寫、或者有except),或者超時(timeout指定等待時間,如果立即返回設為null即可),函式返回。當select函式返回後,可以通過遍歷fdset,來找到就緒的描述符。

 

基本流程,如圖所示:

 

  select目前幾乎在所有的平臺上支援,其良好跨平臺支援也是它的一個優點select的一個缺點在於單個程序能夠監視的檔案描述符的數量存在最大限制,在Linux上一般為1024,可以通過修改巨集定義甚至重新編譯核心的方式提升這一限制,但是這樣也會造成效率的降低。

 

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

1、select最大的缺陷就是單個程序所開啟的FD是有一定限制的,它由FD_SETSIZE設定,預設值是1024

  一般來說這個數目和系統記憶體關係很大,具體數目可以cat /proc/sys/fs/file-max察看。32位機預設是1024個。64位機預設是2048.

2、對socket進行掃描時是線性掃描,即採用輪詢的方法,效率較低。

  當套接字比較多的時候,每次select()都要通過遍歷FD_SETSIZE個Socket來完成排程,不管哪個Socket是活躍的,都遍歷一遍。這會浪費很多CPU時間。如果能給套接字註冊某個回撥函式,當他們活躍時,自動完成相關操作,那就避免了輪詢,這正是epoll與kqueue做的。

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

 

2、poll

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

 

它沒有最大連線數的限制,原因是它是基於連結串列來儲存的,但是同樣有一個缺點:

1)大量的fd的陣列被整體複製於使用者態和核心地址空間之間,而不管這樣的複製是不是有意義。

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

 

注意:從上面看,select和poll都需要在返回後,通過遍歷檔案描述符來獲取已經就緒的socket。事實上,同時連線的大量客戶端在一時刻可能只有很少的處於就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降。

 

3、epoll

  epoll是在2.6核心中提出的,是之前的select和poll的增強版本。相對於select和poll來說,epoll更加靈活,沒有描述符限制。epoll使用一個檔案描述符管理多個描述符,將使用者關係的檔案描述符的事件存放到核心的一個事件表中,這樣在使用者空間和核心空間的copy只需一次

 

基本原理:epoll支援水平觸發和邊緣觸發,最大的特點在於邊緣觸發,它只告訴程序哪些fd剛剛變為就緒態,並且只會通知一次。還有一個特點是,epoll使用“事件”的就緒通知方式,通過epoll_ctl註冊fd,一旦該fd就緒,核心就會採用類似callback的回撥機制來啟用該fd,epoll_wait便可以收到通知。

 

epoll的優點:

1、沒有最大併發連線的限制,能開啟的FD的上限遠大於1024(1G的記憶體上能監聽約10萬個埠)。

2、效率提升,不是輪詢的方式,不會隨著FD數目的增加效率下降

  只有活躍可用的FD才會呼叫callback函式;即Epoll最大的優點就在於它只管你“活躍”的連線,而跟連線總數無關,因此在實際的網路環境中,Epoll的效率就會遠遠高於select和poll。

3、記憶體拷貝,利用mmap()檔案對映記憶體加速與核心空間的訊息傳遞;即epoll使用mmap減少複製開銷

 

epoll對檔案描述符的操作有兩種模式:LT(level trigger)和ET(edge trigger)。LT模式是預設模式,LT模式與ET模式的區別如下:

LT模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫epoll_wait時,會再次響應應用程式並通知此事件。

ET模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式必須立即處理該事件。如果不處理,下次呼叫epoll_wait時,不會再次響應應用程式並通知此事件。

1、LT模式

  LT(level triggered)是預設的工作方式,並且同時支援block和no-block socket。在這種做法中,核心告訴你一個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,核心還是會繼續通知你的

2、ET模式

  ET(edge-triggered)是高速工作方式,只支援no-block socket。在這種模式下,當描述符從未就緒變為就緒時,核心通過epoll告訴你。然後它會假設你知道檔案描述符已經就緒,並且不會再為那個檔案描述符傳送更多的就緒通知,直到你做了某些操作導致那個檔案描述符不再為就緒狀態了(比如,你在傳送,接收或者接收請求,或者傳送接收的資料少於一定量時導致了一個EWOULDBLOCK 錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),核心不會發送更多的通知(only once)

  ET模式在很大程度上減少了epoll事件被重複觸發的次數,因此效率要比LT模式高。epoll工作在ET模式的時候,必須使用非阻塞套介面,以避免由於一個檔案控制代碼的阻塞讀/阻塞寫操作把處理多個檔案描述符的任務餓死。

3、在select/poll中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()時便得到通知。(此處去掉了遍歷檔案描述符,而是通過監聽回撥的的機制。這正是epoll的魅力所在。)

注意:如果沒有大量的idle-connection或者dead-connection,epoll的效率並不會比select/poll高很多,但是當遇到大量的idle-connection,就會發現epoll的效率大大高於select/poll。

 

三、select、poll、epoll區別

1、支援一個程序所能開啟的最大連線數

 

2、FD劇增後帶來的IO效率問題

 

3、訊息傳遞方式

 

 

綜上,在選擇select,poll,epoll時要根據具體的使用場合以及這三種方式的自身特點:

1、表面上看epoll的效能最好,但是在連線數少並且連線都十分活躍的情況下,select和poll的效能可能比epoll好,畢竟epoll的通知機制需要很多函式回撥。

2、select低效是因為每次它都需要輪詢。但低效也是相對的,視情況而定,也可通過良好的設計改善。

原文:https://www.cnblogs.com/jeakeven/p/5435916.html