select、poll和epoll

I/O複用:

  在一個進程或者多個進程的須要多個I/O,不能阻塞在一個I/O上而中止不前,而是用到I/O複用。進程預先告知內核須要哪些I/O描述符,內核一旦發現指定的一個或多個I/O條件就緒,則通知進程進行相應操做,這就是I/O複用。html

使用場合:node

一、客戶處理多個描述符(交互式輸入和網絡套接字)數組

二、TCP服務器既處理監聽套接字,又處理鏈接套接字服務器

三、一服務器既處理TCP又處理UDP網絡

四、一服務器要處理多個服務或多個協議數據結構

 

select函數:

容許進程指示內核等待多個事件中的任何一個發生,且只在有一個或多個事件發生或經歷一段指定的時間後才喚醒它。併發

其中: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 

 

 

poll函數

  與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萬級別的併發訪問控制,是很難完成的。

epoll

  就上面例子中,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不爲空,則把發生的事件賦值到用戶態,同時將時間數量返回。

相關文章
相關標籤/搜索