[轉]select/poll/epoll對比分析
目錄
select
/poll
/epoll
都是IO多路複用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程式進行讀或寫操作。本質上select
/poll
/epoll
都是同步I/O,即讀寫是阻塞的。
1.select
原型:
int select (int n, fd_set *readfds, fd_set * writefds, fd_set *exceptfds,
struct timeval *timeout);
從select
函式監控3類檔案描述符:writefds
、readfds
、exceptfds
。呼叫select
函式後會阻塞,直到描述符準備就緒(有資料可讀、可寫、或者出現異常)或者超時,函式便返回。當select
函式返回後,可以通過遍歷描述符集合,找到就緒的描述符。
select缺點
- 單程序能夠監控的檔案描述符的數量存在最大限制,在Linux上一般為1024,可以通過修改巨集定義增大上限,但同樣存在效率低的弱勢;
- IO效隨著監視的描述符數量的增長,其效率也會線性下降;
2.poll
原型:
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
其中pollfd表示監視的描述符集合,如下
struct pollfd {
int fd; //檔案描述符
short events; //監視的請求事件
short revents; //已發生的事件
};
pollfd
結構包含了要監視的event
和發生的event
,並且pollfd
並沒有最大數量限制(但數量過大同樣會導致性下降)。 和select
函式一樣,當poll
函式返回後,可以通過遍歷描述符集合,找到就緒的描述符。
從上面看,select
和poll
都需要在返回後,通過遍歷檔案描述符來獲取已經就緒的socket
。事實上,同時連線的大量客戶端在一時刻可能只有很少的處於就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降。
3.epoll
epoll
是在2.6核心中提出的,是select
和poll
的增強版。相對於select
和poll
來說,epoll
更加靈活,沒有描述符數量限制。epoll
使用一個檔案描述符管理多個描述符,將使用者空間的檔案描述符的事件存放到核心的一個事件表中,這樣在使用者空間和核心空間的copy只需一次。epoll
機制是Linux最高效的I/O複用機制,在一處等待多個檔案控制代碼的I/O事件。
select
/poll
都只有一個方法,而epoll
的操作過程有3個方法,分別是epoll_create()
, epoll_ctl()
,epoll_wait()
。
3.1 epoll_create()
int epoll_create(int size);
用於建立一個epoll
的控制代碼
,size
是指監聽的描述符個數, 現在核心支援動態擴充套件,該值的意義僅僅是初次分配的fd
個數,後面空間不夠時會動態擴容。 當建立完epoll
控制代碼後,佔用一個fd
值.
ls /proc/<pid>/fd/ #可通過終端執行,看到該fd
使用完epoll
後,必須呼叫close()
關閉,否則可能導致fd
被耗盡。
3.2 epoll_ctl()
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
用於對需要監聽的檔案描述符(fd
)執行op
操作,比如將fd
加入到epoll
控制代碼。
epfd
:是epoll_create()
的返回值;op
:表示op
操作,用三個巨集來表示,分別代表新增、刪除和修改對fd的監聽事件;EPOLL_CTL_ADD
(新增)EPOLL_CTL_DEL
(刪除)EPOLL_CTL_MOD
(修改)
fd
:需要監聽的檔案描述符;epoll_event
:需要監聽的事件,struct
epoll_event結構如下:
struct epoll_event {
__uint32_t events; /* Epoll事件 */
epoll_data_t data; /*使用者可用資料*/
};
events可取值:(表示對應的檔案描述符的操作)
EPOLLIN
:可讀(包括對端SOCKET正常關閉);EPOLLOUT
:可寫;EPOLLERR
:錯誤;EPOLLHUP
:中斷;EPOLLPRI
:高優先順序的可讀(這裡應該表示有帶外資料到來);EPOLLET
: 將EPOLL
設為邊緣觸發模式,這是相對於水平觸發來說的。EPOLLONESHOT
:只監聽一次事件,當監聽完這次事件之後就不再監聽該事件
3.3 epoll_wait()
int epoll_wait(int epfd, struct epoll_event * events, int maxevents,
int timeout);
epfd
:等待epfd
上的io
事件,最多返回maxevents
個事件;- events:用來從核心得到事件的集合;
maxevents
:events
數量,該maxevents
值不能大於建立epoll_create()
時的size;timeout
:超時時間(毫秒,0會立即返回)。
該函式返回需要處理的事件數目,如返回0表示已超時。
4.對比
在 select
/poll
中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll
事先通過epoll_ctl()
來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback
的回撥機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()
時便得到通知。(此處去掉了遍歷檔案描述符,而是通過監聽回撥的的機制。這正是epoll
的魅力所在。)
epoll優勢
- 監視的描述符數量不受限制,所支援的
fd
上限是最大可以開啟檔案的數目,具體數目可以cat /proc/sys/fs/file-max
檢視,一般來說這個數目和系統記憶體關係很大,以3G的手機來說這個值為20-30萬。 - IO效率不會隨著監視fd的數量增長而下降。
epoll
不同於select
和poll
輪詢的方式,而是通過每個fd
定義的回撥函式來實現的,只有就緒的fd
才會執行回撥函式。
如果沒有大量的idle-connection或者dead-connection,epoll
的效率並不會比select
/poll
高很多,但是當遇到大量的idle-connection,就會發現epoll
的效率大大高於select
/poll
。