當一個節點和多個節點建立連線時,如何高效的處理多個連線的資料,下面具體分析兩者的區別。
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核心完全支援epoll。epoll的IO效率不隨FD數目增加而線性下降。
要使用epoll只需要這三個:epoll_create(2), epoll_ctl(2), epoll_wait(2)
epoll用到的所有函式都是在標頭檔案sys/
epoll.h
如果所有的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 註冊、
int fd, //關聯的檔案描述符
struct epoll_event *event//指向epoll_event的指標
}
(3)、int epoll_wait(int
epfd, struct epoll_event *
events,int maxevents, int
timeout)
用於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網 卡驅動架構。
當一個節點和多個節點建立連線時,如何高效的處理多個連線的資料,下面具體分析兩者的區別。
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核心完全支援epoll。epoll的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 註冊、
int fd, //關聯的檔案描述符
struct epoll_event *event//指向epoll_event的指標
}
(3)、int epoll_wait(int
epfd, struct epoll_event *
events,int maxevents, int
timeout)
用於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網 卡驅動架構。