1. 程式人生 > >I/O複用——select()、poll()與epoll()的區別

I/O複用——select()、poll()與epoll()的區別

       select()、poll()、epoll()三組I/O複用系統呼叫都可以同時監聽多個檔案描述符。它們將等待由timeout引數指定的超時時間,直到一個或者多個檔案描述符上有事件發生時返回,返回值就是就緒檔案描述符的數量,返回0表示沒有事件發生。

1、select()系統呼叫

select的引數型別fd_set沒有將檔案描述符和事件繫結,它僅僅是一個檔案描述符集合,因此select需要提供3個這種型別的引數來分別傳入和輸出可讀、可寫及異常等事件。這一方面使得select需要提供3個這種型別的事件,另一方面由於核心對fd_set集合的線上修改,應用程式下次呼叫select前不得不重置這3個fd_set集合。

2、poll()系統呼叫

poll的引數型別pollfd多少要聰明一些。它把檔案描述符和事件都定義其中,任何事件都被統一處理,從而使得程式設計介面簡潔得多。並且核心每次修改的是pollfd結構體的revents成員,而events成員保持不變,因此下次呼叫poll時應用程式無須重置pollfd型別的事件集引數。由於每次select和poll呼叫都返回整個使用者註冊的事件集合(包括就就緒的和未就緒的),所以應用程式索引就緒檔案描述符的時間複雜度O(n)。

3、epoll()系統呼叫

epoll則採用與select和poll完全不同的方式來管理使用者註冊的事件。它在核心中維護一個事件表,並提供了一個獨立的系統呼叫epoll_ctl來控制往其中新增、刪除、修改事件。這樣,每次epoll_wait呼叫都直接從該核心事件表中取得使用者註冊的事件,而無須反覆從使用者空間讀入這些事件。epoll_wait系統呼叫的events引數僅用來返回就緒的事件,這使得應用程式索引就緒檔案描述符的時間複雜度達到O(1)。

三者的區別:

1、向核心傳遞描述符數  select / poll       每輪迴圈都要重新拷貝到核心空間

 epoll                 每個描述符只拷貝一次到核心空間             

2、 核心實現 select / poll       核心輪詢檢查描述符上是否有事件   O(n)

epoll                 在描述符上設定回撥函式,有資料就緒呼叫回撥函式新增到就緒佇列  O(1)

3、 I/O函式返回後檢索就緒描述符 select / poll     遍歷所有描述符找到就緒的描述符    O(n)

epoll               直接返回就緒的描述符,不需要遍歷所有描述符   O(1)

a.  poll和epoll_wait分別用nfds和maxevents引數指定最多監聽多少個檔案描述符和事件。這兩個數值都達到系統允許開啟的最大檔案描述符數目,即65535(cat/proc/sys/fs/file-max)。而select允許監聽的最大檔案描述符數量通常有限制。

b.  select和poll都只能工作在相對低效的LT模式,而epoll則可以工作在ET高效模式。

c.   實現原理:select和poll採用的都是輪詢的方式,即每次呼叫都要掃描整個註冊檔案描述符集合,並將其中就緒的檔案描述符返回給使用者程式,因此它們檢測到就緒的檔案描述符時,將觸發回撥函式,回撥函式就將該檔案描述符上對應的事件插入核心事件就緒佇列。核心最後在適當的時機將該就緒事件佇列中的內容拷貝到使用者空間。因此epoll_wait無須輪詢整個檔案描述符集合來檢測哪些事件已經就緒,其演算法時間複雜度是O(1)。

select()和epoll()的簡單比較

select系統呼叫

1.每次呼叫都需要從使用者空間拷貝資料 大小 fd_set1024

2.每次返回 都需要遍歷所有描述符找到就緒描述符

3.核心實現:輪詢的方式

4、能監聽的檔案描述符數目相比epoll少一些 事件型別少一些

epoll系統呼叫

1.每個檔案描述符只需要從使用者空間往核心空間拷貝一次 epoll_ctl直接新增到核心事件表

2.epoll_wait只返回就緒的檔案描述符 不需要遍歷所有檔案描述符查詢就緒的檔案描述符

3.核心實現:在每個檔案描述符上註冊了回撥函式 事件就緒後通過回撥函式新增到就緒佇列

4.檔案描述符數目比select多 並且將檔案描述符和事件表集放在一個結構體中