Select和epoll的區別

當一個節點和多個節點建立連接時,如何高效的處理多個連接的數據,下面具體分析兩者的區別。

1. select函數

函數原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

參數介紹:(1)nfds    -- fdset集合中最大描述符值加1

                  (2)fdset   -- 一個位數組,其大小限制爲_FD_SETSIZE(1024)

                                位數組的每一位代表的是其對應的描述符是否需要被檢查。

                         (3)readfds -- 讀事件文件描述符數組

                         (4 )writefds -- 寫事件文件描述符數組

                   (5)exceptfds -- 錯誤事件文件描述符數組

                   (6)timeout -- 超時事件,該結構被內核修改,其值爲超時剩餘時間。

對應內核:select對應於內核中的sys_select調用,sys_select首先將第二三四個參數指向的fd_set拷貝到內核,然後對每個被SET的描述符調用進行poll,並記錄在臨時結果中(fdset),如果有事件發生,select會將臨時結果寫到用戶空間並返回;當輪詢一遍後沒有任何事件發生時,如果指定了超時時間,則select會睡眠到超時,睡眠結束後再進行一次輪詢,並將臨時結果寫到用戶空間,然後返

2. select/poll特點

傳統的select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。
poll的執行分三部分:
(1).將用戶傳入的pollfd數組拷貝到內核空間,因爲拷貝操作和數組長度相關,時間上這是一個O(n)操作
(2).查詢每個文件描述符對應設備的狀態,如果該設備尚未就緒,則在該設備的等待隊列中加入一項並繼續查詢下一設備的狀態。 查詢完所有設備後如果沒有一個設備就緒,這時則需要掛起當前進程等待,直到設備就緒或者超時。設備就緒後進程被通知繼續運行,這時再次遍歷所有設備,以查找就緒設備。這一步因爲兩次遍歷所有設備,時間複雜度也是O(n),這裏面不包括等待時間......
(3). 將獲得的數據傳送到用戶空間並執行釋放內存和剝離等待隊列等善後工作,向用戶空間拷貝數據與剝離等待隊列等操作的的時間複雜度同樣是O(n)。
3. epoll機制
Linux 2.6內核完全支持epollepoll的IO效率不隨FD數目增加而線性下降。
要使用epoll只需要這三個系統調用:epoll_create(2), epoll_ctl(2), epoll_wait(2)
epoll用到的所有函數都是在頭文件sys/epoll.h中聲明的,內核實現中epoll是根據每個fd上面的callback函數實現的。只有"活躍"的socket纔會主動的去調用 callback函數,其他idle狀態socket則不會。
如果所有的socket基本上都是活躍的---比如一個高速LAN環境,過多使用epoll,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

3.1 所用到的函數:

(1)、int epoll_create(int size)
該函數生成一個epoll專用的文件描述符,其中的參數是指定生成描述符的最大範圍
(2)、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
用於控制某個文件描述符上的事件,可以註冊事件,修改事件,刪除事件。
如果調用成功返回0,不成功返回-1
int epoll_ctl{
    int epfd,//由 epoll_create 生成的epoll專用的文件描述符
    int op,  //要進行的操作例如註冊事件,可能的取值EPOLL_CTL_ADD 註冊、
               //EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 刪除
    int fd,   //關聯的文件描述符
    struct epoll_event *event//指向epoll_event的指針
    }
(3)、int epoll_wait(intepfd, struct epoll_event * events,int maxevents, inttimeout)
用於輪詢I/O事件的發生,返回發生事件數
int epoll_wait{
    int epfd,//由epoll_create 生成的epoll專用的文件描述符
    struct epoll_event * events,//用於回傳代處理事件的數組
    int maxevents,//每次能處理的事件數
    int timeout//等待I/O事件發生的超時值
                   //爲0的時候表示馬上返回,爲-1的時候表示一直等下去,直到有事件
                   //爲任意正整數的時候表示等這麼長的時間,如果一直沒有事件
                   //一般如果網絡主循環是單獨的線程的話,可以用-1來等,這樣可以保證一些效率
                   //如果是和主邏輯在同一個線程的話,則可以用0來保證主循環的效率
    }
epoll是爲處理大批量句柄而作了改進的poll。
4. epoll的優點:
<1>支持一個進程打開大數目的socket描述符(FD)
select 最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設置,默認值是2048。對於那些需要支持的上萬連接數目的IM服務器來說顯然太少了。這時候可以:
     (1) 可以修改這個宏然後重新編譯內核,不過資料也同時指出,這樣也會帶來網絡效率的下降
     (2) 可以選擇多進程的解決方案,不過雖然linux上創建進程的代價比較下,但是仍舊是不可忽視的,所以也不是很完美的方案
epoll沒有這樣的限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大於2048,具體數組可以查看cat /proc/sys/fs/file-max查看,這個數目和系統內存關係很大。
<2>IO效率不隨FD數目增加而線性下降
傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網絡延時,任一時間只有部分的socket是"活躍"的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。
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之上了。
<3>使用mmap加速內核與用戶空間的消息傳遞這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就 很重要,在這點上,epoll是通過內核於用戶空間mmap同一塊內存實現的。而如果你想我一樣從2.5內核就關注epoll的話,一定不會忘記手工 mmap這一步的。
<4>內核微調
這一點其實不算epoll的優點了,而是整個linux平臺的優點。也許你可以懷 疑linux平臺,但是你無法迴避linux平臺賦予你微調內核的能力。比如,內核TCP/IP協議棧使用內存池管理sk_buff結構,那麼可以在運行 時期動態調整這個內存pool(skb_head_pool)的大小--- 通過echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數的第2個參數(TCP完成3次握手 的數據包隊列長度),也可以根據你平臺內存大小動態調整。更甚至在一個數據包面數目巨大但同時每個數據包本身大小卻很小的特殊系統上嘗試最新的NAPI網 卡驅動架構。
發佈了49 篇原創文章 · 獲贊 8 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章