1. 程式人生 > >unix網絡編程——I/O多路復用之epoll

unix網絡編程——I/O多路復用之epoll

得到 是否 再次 專用 空間 line 正常 時間服務 struct

1. 基本概念

  當程序進行IO時,如果數據尚未準備好,那麽IO將處於阻塞狀態。當某個進程有多個打開的文件,比如socket,那麽其後的所有準備好讀寫的文件將受到阻塞的影響而不能操作。不借助線程,單一進程無法在同一時間服務多個文件描述符。非阻擋式IO可以作為一個解決方案,但是效率並不高。首先進程需要不斷發IO請求,其次,如果程序可以休眠,讓出CPU將提高效率。多任務式IO是在其中任何一個文件描述符就緒時收到通知,此時IO將不會受到阻擋,其余時間處於休眠狀態,將CPU資源讓給別的進程。

  為了實現I/O多路復用,epoll是在2.6內核中提出的,是之前的select和poll的增強版本。相對於select和poll來說,epoll更加靈活,沒有描述符限制。epoll使用一個文件描述符管理多個描述符,將用戶關系的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的copy只需一次。相比於select,epoll最大的好處在於它不會隨著監聽fd數目的增長而降低效率。

2. api

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  • int epoll_create(int size) [創建句柄]

  創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大。這個參數不同於select()中的第一個參數,給出最大監聽的fd+1的值。需要註意的是,當創建好epoll句柄後,它就是會占用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須調用close()關閉,否則可能導致fd被耗盡。

  函數生成一個epoll專用的文件描述符。它其實是在內核申請一空間,用來存放你想關註的socket fd上是否發生以及發生了什麽事件。size就是你在這個epoll fd上能關註的最大socket fd數。隨你定好了。只要你有空間。可參見上面與select之不同

  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)[事件註冊函數]

  epoll的事件註冊函數,對比select()函數,select()是在監聽事件時告訴內核要監聽什麽類型的事件,而epoll是在調用epoll_ctl函數的時候,先註冊要監聽的事件類型。

  epfd:epoll_create()的返回值;
  op:表示要進行的操作,操作的動作使用了宏定義:

    1. EPOLL_CTL_ADD: 註冊新的fd到epfd中;
    2. EPOLL_CTL_MOD: 修改已經註冊的fd的監聽事件;
    3. EPOLL_CTL_DEL: 刪除epfd中的一個fd;

  fd:關聯的文件描述符,表示需要監聽的fd;
  event:指向epoll_event的指針,告訴內核需要監聽什麽事件,epoll_event的結構如下:

struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

events描述事件類型,使用了宏定義:

  1. EPOLLIN:表示對應的文件描述符可以讀(包括對端socket正常關閉);

  2. EPOLLOUT: 表示對應的文件描述符可以寫;

  3. EPOLLPRI: 表示對應的文件描述符有緊急的數據可讀(表示有外數據到來);

  4. EPOLLHUP: 表示對應的文件描述符被掛斷;

  5. EPOLLET: 將EPOLL設置為邊緣觸發(Edge Triggered)模式;

  6. EPOLLONESHOT: 只監聽一次事件,當監聽玩這次事件之後,如果還需要繼續監聽,需要再次將該socket加入EPOLL隊列中。

  • int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) [等待事件觸發]

  等待事件的產生,類似於select()調用。參數events用來從內核得到事件的集合,maxevents告之內核這個events有多大,這個maxevents的值不能大於創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1表示不確定)。該函數返回需要處理的事件數目,如返回0表示已超時。

3.epoll工作模式

  epoll對文件描述符的操作有兩種模式:LT(level trigger,水平觸發)和ET(edge trigger,邊緣觸發)。

  LT模式:水平觸發是缺省的工作方式。當epoll_wait檢測到描述符事件發生並將此事件通知應用程序,應用程序可以不立即處理該事件(進行IO操作)。下次調用epoll_wait時,會再次響應應用程序並通知此事件。由於對於描述事件符在處理前會進行多次通知,因此出錯的概率小;

  ET模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程序,此時它會假設你知道文件描述符已就緒,應用程序必須立即處理該事件。如果不處理,下次調用epoll_wait時,不會再次響應應用程序並通知此事件,直到做了一定操作導致該文件描述符再次變為未就緒狀態。但是如果一直對該fd進行IO操作()

  ET模式在很大程度上減少了epoll事件被重復觸發的次數,因此效率要比LT模式高。epoll工作在ET模式的時候,必須使用非阻塞套接口,以避免由於一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。

unix網絡編程——I/O多路復用之epoll