select/poll/epoll都是IO多路復用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作。本質上select/poll/epoll都是同步I/O,即讀寫是阻塞的。
原型:
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
從select函數監控3類文件描述符:writefds、readfds、exceptfds。調用select函數後會阻塞,直到描述符准備就緒(有數據可讀、可寫、或者出現異常)或者超時,函數便返回。當select函數返回後,可以通過遍歷描述符集合,找到就緒的描述符。
select缺點
原型:
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。事實上,同時連接的大量客戶端在一時刻可能只有很少的處於就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降。
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()
。
int epoll_create(int size);
用於創建一個epoll的句柄,size是指監聽的描述符個數, 現在內核支持動態擴展,該值的意義僅僅是初次分配的fd個數,後面空間不夠時會動態擴容。 當創建完epoll句柄後,占用一個fd值.
ls /proc/<pid>/fd/ //可通過終端執行,看到該fd
使用完epoll後,必須調用close()關閉,否則可能導致fd被耗盡。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
用於對需要監聽的文件描述符(fd)執行op操作,比如將fd加入到epoll句柄。
epoll_event:需要監聽的事件,struct epoll_event結構如下:
struct epoll_event {
__uint32_t events; /* Epoll事件 */
epoll_data_t data; /*用戶可用數據*/
};
events可取值:(表示對應的文件描述符的操作)
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
該函數返回需要處理的事件數目,如返回0表示已超時。
在 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。