select、poll和epoll之間的區別
阿新 • • 發佈:2020-07-12
在深入理解select、poll和epoll之間的區別之前,首先要了解什麼是IO多路複用模型。
IO多路複用
簡單來說,IO多路複用是指核心一旦發現程序指定的一個或者多個IO條件準備就緒,它就通知該程序去進行IO操作。
詳細的描述可以參考IO模型。select、poll和epoll都是提供I/O多路複用的解決方案。
select
- 函式
int select(int maxfdp, fd_set *readset, fd_set *writeset,
fd_set *exceptset, struct timeval *timeout);
- 基本原理:select 函式監視的檔案描述符分3類,分別是
writefds
readfds
、和exceptfds
。呼叫select時會被阻塞,直到有fd就緒(讀、寫、或者異常),或者超時(timeout
指定等待時間,如果立即返回設為null
即可)函式返回一個大於0
的值,然後通過遍歷檔案描述符集合fd_set
,來找到就緒的描述符 - 說明:
maxfdp
是一個整數值,是指集合中所有檔案描述符的範圍,即所有檔案描述符的最大值加1。fd_set
是以點陣圖的形式來儲存這些檔案描述符。maxfdp
也就是定義了點陣圖中有效地位的個數 - 時間複雜度:
O(n)
,n
為檔案描述符集合fd_set
的大小 - 檔案描述符最大限制:
1024
poll
- 函式
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
- 基本原理:和select 函式很相似,定義了一個
struct pollfd
結構型別的陣列,用於存放需要檢測其狀態的所有檔案描述符。呼叫poll函式之後,系統不會清空這個陣列。特別是對於檔案描述符比較多的情況下,在一定程度上可以提高處理的效率。這一點與select()函式不同,呼叫select()函式之後,select()函式會清空它所檢測的檔案描述符集合,導致每次呼叫select()之前都必須把檔案描述符重新加入到待檢測的集合中。因此,select()函式適合於只檢測一個檔案描述符的情況,而poll()函式適合於大量檔案描述符的情況 - 時間複雜度:
O(n)
,n
為檔案描述符集合的大小 - 檔案描述符最大限制
fds
是一個連結串列
epoll
- 函式
// 建立一個epoll物件,引數size是核心保證能夠正確處理的最大控制代碼數
int epoll_create(int size);
// 操作上面建立的epoll
// 例如,將剛建立的socket加入到epoll中讓其監控,或者
// 把 epoll正在監控的某個socket句並移出epoll
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 在指定的timeout時間內,當所有控制代碼中有事件發生時,就返回給使用者態的程序
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
- 基本原理:將所有的檔案描述符
fd_set
儲存在共享記憶體(使用者空間和核心空間都可以直接訪問)。呼叫epoll_create
函式建立epoll物件,並以紅黑樹的結構儲存在核心空間,epoll_ctl
函式用來在紅黑樹中新增或者登出待監視檔案描述符,最後呼叫epoll_wait
函式直到有就緒的檔案描述符立即返回給使用者態程序
- 時間複雜度:
O(1)
- 檔案描述符最大限制:能開啟的fd的上限遠大於
1024
(1G
的記憶體上能監聽約10w+
)
epoll的兩種工作方式
- 水平觸發(LT):若就緒的事件一次沒有處理完所有要做的事件,就會一直去處理。即就會將沒有處理完的事件繼續放回到就緒佇列之中(即那個核心中的連結串列),一直進行處理
- 邊緣觸發(ET) :就緒的事件只能處理一次,若沒有處理完會在下次的其它事件就緒時再進行處理。而若以後再也沒有就緒的事件,那麼剩餘的那部分資料也會隨之而丟失。
ET模式的效率比LT模式的效率要高很多。只是如果使用ET模式,就要保證每次進行資料處理時,要將其處理完,不能造成資料丟失,這樣對編寫程式碼的人要求就比較高。 為了保證資料的完整性,ET模式只支援非阻塞的讀寫。
select、poll和epoll對比
實現機制 | 時間複雜度 | 連線數 | 傳遞方式 |
---|---|---|---|
select | O(n) |
1024 |
核心-->使用者 |
poll | O(n) |
無限制 | 核心-->使用者 |
epoll | O(1) |
很大 | 共享記憶體 |
在select和poll中,程序只有在呼叫方法後,核心才對所有監視的檔案描述符進行掃描,發現有任何一個檔案描述符就緒或者超時就立刻返回。epoll採用基於事件的就緒通知方式,事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()時便得到通知。