在一個進程或者多個進程的須要多個I/O,不能阻塞在一個I/O上而中止不前,而是用到I/O複用。進程預先告知內核須要哪些I/O描述符,內核一旦發現指定的一個或多個I/O條件就緒,則通知進程進行相應操做,這就是I/O複用。html
使用場合:node
一、客戶處理多個描述符(交互式輸入和網絡套接字)數組
二、TCP服務器既處理監聽套接字,又處理鏈接套接字服務器
三、一服務器既處理TCP又處理UDP網絡
四、一服務器要處理多個服務或多個協議數據結構
容許進程指示內核等待多個事件中的任何一個發生,且只在有一個或多個事件發生或經歷一段指定的時間後才喚醒它。併發
其中:maxfdp1表示探測描述符中的最大值加一(由於描述符從0開始,其表示個數),後面三個參數依次表示讀、寫和異常描述符集,最後一個表示等待時間。timeout:NULL 永遠等待;正數,等待一段時間後返回;0,不等待,檢查描述符後當即返回。函數
#include <sys/select.h> #include <sys/time.h> int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout); 返回:如有就緒描述符則爲其數目,若超時爲0,出錯爲-1 struct timeval { long tv_sec; //seconds long tv_usec; //microseconds }; void FD_ZERO(fd_set *fdset); //clear all bits in fdset void FD_SET(int fd, fd_set *fdset); //trun on the bit in fdset void FD_CLR(int fd, fd_set *fdset); //turn off the bit for fd in fdset void FD_ISSET(int fd, fd_set *fdset); //is the bit for fd on in fdset
與select函數大體相同,不一樣在於select描述符最大個數FD_SETSIZE,poll可更大。且傳遞的結構不一樣,poll對每一個描述符管理起來,select分別用三個數組管理起來。timeout:INFTIM永遠等待,0當即返回,正數等待指定毫秒數返回。性能
#include <poll.h> int poll(struct pollfd *fdarray, unsigned long nfds, int timeout); //返回:如有就需描述符則爲其數目,超時爲0,出錯爲-1 struct pollfd { int fd; //descriptor to check short events; //events of interest on fd short revents; //events that pccurred on fd };
總結:參考http://www.open-open.com/lib/view/open1410403215664.html#articleHeader0spa
select缺點:一、單進程可監視文件描述符最大限制1024個,可更改。但select採用輪詢方式掃描文件描述符,文件描述符數量越多性能越差(Linux內核中:#define _FD_SETSIZE 1024)
二、內核、用戶空間內存拷貝,select須要賦值大量的句柄數據結構,產生巨大開銷;
三、select返回整個句柄數組,應用程序須要遍歷數組查找就緒文件描述符;
四、select水平觸發,應用程序若是沒有完成對一個已經就緒的文件描述符進行IO操做,那麼以後每次select調用仍是會將其桃枝進程
poll:相比select只是數據結構發生變化,用一個結構體數組來表示監視的文件描述符,每個結構存儲監視的文件描述符和其監視事件,並在其中返回監視結果。其監視文件數量沒有限制。可是其餘缺點和select同樣。
例如:服務器須要支持100萬併發鏈接,在_FD_SETSIZE爲1024的狀況下,咱們至少須要建立1K歌進程才能實現100萬的併發鏈接,除進程間上下文切換的時間開銷,從內核、用戶空間的內存拷貝,數組輪詢等都是系統難以承受和實現的。所以基於select模型的服務器,要達到10萬級別的併發訪問控制,是很難完成的。
就上面例子中,select/poll都是服務器進程每次都把這100萬個鏈接告訴操做系統(從用戶賦值句柄數據結構到內核),讓操做系統內核查詢這些套接字上是否有事件發生,該過程資源消耗較大,所以select/poll通常只能處理幾千的併發鏈接。
epoll的設計和實現與select徹底不一樣。epoll經過Linux內核中申請一個建議的文件系統(B+樹),吧原先的select/poll分爲:
一、epoll_creat()簡歷一個epoll對象(epoll文件系統中爲這個句柄對象分配資源)
二、epoll_ctl向epoll對象中添加監視的描述符;
三、epoll_wait收集發生的事件的鏈接;
epoll實現思路:
當某一進程調用epoll_creat方法,Linux內核會建立一個eventpoll結構體,這個結構體中有兩個成員與epoll的使用方式密切相關
struct eventpoll{ .... /*紅黑樹的根節點,這顆樹中存儲着全部添加到epoll中的須要監控的事件*/ struct rb_root rbr; /*雙鏈表中則存放着將要經過epoll_wait返回給用戶的知足條件的事件*/ struct list_head rdlist; .... };
每個epoll對象都有一個eventpoll結構體,用於存放經過epoll_ctl方法將epoll對象中添加進來的事件。這些事件都會掛載在紅黑樹中,如此重複添加的事件也能夠經過紅黑樹而高效的識別出來。
全部添加到epoll中的事件都會與設備(網卡)驅動程序簡歷回調關係,當相應的事件發生時會調用這個回調方法。該回調方法在內核中叫ep_poll_callback,它將發生的事件添加到rdlist雙鏈表中。
對於每個事件都會創建epitem結構體:
struct epitem{ struct rb_node rbn;//紅黑樹節點 struct list_head rdllink;//雙向鏈表節點 struct epoll_filefd ffd; //事件句柄信息 struct eventpoll *ep; //指向其所屬的eventpoll對象 struct epoll_event event; //期待發生的事件類型 }
當調用epoll_wait檢查是否有事件發生時,只須要檢查eventpoll對象中的rdlist雙鏈表中是否有epitem元素便可。若是rdlist不爲空,則把發生的事件賦值到用戶態,同時將時間數量返回。