epoll是在2.6核心中提出的,是之前的select和poll的增強版本。相對於select和poll來說,epoll更加靈活,沒有描述符限制。epoll使用乙個檔案描述符管理多個描述符,將使用者關係的檔案描述符的事件存放到核心的乙個事件表中,這樣在使用者空間和核心空間的copy只需一次。
一 epoll操作過程
epoll操作過程需要三個介面,分別如下:
int epoll_create(int size);//建立乙個epoll的控制代碼,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);
1.int epoll_create(int size);
建立乙個epoll的控制代碼,size用來告訴核心這個監聽的數目一共有多大,這個引數不同於select()中的第乙個引數,給出最大監聽的fd+1的值,引數size並不是限制了epoll所能監聽的描述符最大個數,只是對核心初始分配內部資料結構的乙個建議。
當建立好epoll控制代碼後,它就會占用乙個fd值,在linux下如果檢視/proc/程序id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須呼叫close()關閉,否則可能導致fd被耗盡。
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
函式是對指定描述符fd執行op操作。
- epfd:是epoll_create()的返回值。
- op:表示op操作,用三個巨集來表示:新增epoll_ctl_add,刪除epoll_ctl_del,修改epoll_ctl_mod。分別新增、刪除和修改對fd的監聽事件。
- fd:是需要監聽的fd(檔案描述符)
- epoll_event:是告訴核心需要監聽什麼事,struct epoll_event結構如下:
struct epoll_event ;
//events可以是以下幾個巨集的集合:
epollin :表示對應的檔案描述符可以讀(包括對端socket正常關閉);
epollout:表示對應的檔案描述符可以寫;
epollpri:表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);
epollerr:表示對應的檔案描述符發生錯誤;
epollhup:表示對應的檔案描述符被結束通話;
epollet: 將epoll設為邊緣觸發(edge triggered)模式,這是相對於水平觸發(level triggered)來說的。
epolloneshot:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到epoll佇列裡
3int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待epfd上的io事件,最多返回maxevents個事件。
引數events用來從核心得到事件的集合,maxevents告之核心這個events有多大,這個maxevents的值不能大於建立epoll_create()時的size,引數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函式返回需要處理的事件數目,如返回0表示已超時。
二 工作模式
epoll對檔案描述符的操作有兩種模式:lt(level trigger)
和et(edge trigger)
。lt模式是預設模式,lt模式與et模式的區別如下:
lt模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫epoll_wait時,會再次響應應用程式並通知此事件。
et模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式必須立即處理該事件。如果不處理,下次呼叫epoll_wait時,不會再次響應應用程式並通知此事件。
1.lt模式
lt(level triggered)是預設的工作方式,並且同時支援block和no-block socket.在這種做法中,核心告訴你乙個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行io操作。如果你不作任何操作,核心還是會繼續通知你的。
2.et模式
et(edge-triggered)是高速工作方式,只支援no-block socket。在這種模式下,當描述符從未就緒變為就緒時,核心通過epoll告訴你。然後它會假設你知道檔案描述符已經就緒,並且不會再為那個檔案描述符傳送更多的就緒通知,直到你做了某些操作導致那個檔案描述符不再為就緒狀態了(比如,你在傳送,接收或者接收請求,或者傳送接收的資料少於一定量時導致了乙個ewouldblock 錯誤)。但是請注意,如果一直不對這個fd作io操作(從而導致它再次變成未就緒),核心不會傳送更多的通知(only once)
et模式在很大程度上減少了epoll事件被重複觸發的次數,因此效率要比lt模式高。epoll工作在et模式的時候,必須使用非阻塞套介面,以避免由於乙個檔案控制代碼的阻塞讀/阻塞寫操作把處理多個檔案描述符的任務餓死。
3.總結
假如有這樣乙個例子:
1. 我們已經把乙個用來從管道中讀取資料的檔案控制代碼(rfd)新增到epoll描述符
2. 這個時候從管道的另一端被寫入了2kb的資料
3. 呼叫epoll_wait(2),並且它會返回rfd,說明它已經準備好讀取操作
4. 然後我們讀取了1kb的資料
5. 呼叫epoll_wait(2)……
lt模式:
如果是lt模式,那麼在第5步調用epoll_wait(2)之後,仍然能受到通知。
et模式:
如果我們在第1步將rfd新增到epoll描述符的時候使用了epollet標誌,那麼在第5步調用epoll_wait(2)之後將有可能會掛起,因為剩餘的資料還存在於檔案的輸入緩衝區內,而且資料發出端還在等待乙個針對已經發出資料的反饋資訊。只有在監視的檔案控制代碼上發生了某個事件的時候 et 工作模式才會匯報事件。因此在第5步的時候,呼叫者可能會放棄等待仍在存在於檔案輸入緩衝區內的剩餘資料。
當使用epoll的et模型來工作時,當產生了乙個epollin事件後,
讀資料的時候需要考慮的是當recv()返回的大小如果等於請求的大小,那麼很有可能是緩衝區還有資料未讀完,也意味著該次事件還沒有處理完,所以還需要再次讀取:
while(rs)
else
} else
if(buflen == 0)
if(buflen == sizeof(buf)
else
}
linux中的eagain含義
linux環境下開發經常會碰到很多錯誤(設定errno),其中eagain是其中比較常見的乙個錯誤(比如用在非阻塞操作中)。
從字面上來看,是提示再試一次。這個錯誤經常出現在當應用程式進行一些非阻塞(non-blocking)操作(對檔案或socket)的時候。
例如,以 o_nonblock的標誌開啟檔案/socket/fifo,如果你連續做read操作而沒有資料可讀。此時程式不會阻塞起來等待資料準備就緒返回,read函式會返回乙個錯誤eagain,提示你的應用程式現在沒有資料可讀請稍後再試。
又例如,當乙個系統呼叫(比如fork)因為沒有足夠的資源(比如虛擬記憶體)而執行失敗,返回eagain提示其再呼叫一次(也許下次就能成功)。
EPOLL兩種模式
select epoll 的特點 select 的特點 select 選擇控制代碼的時候,是遍歷所有控制代碼,也就是說控制代碼有事件響應時,select 需要遍歷所有控制代碼才能獲取到哪些控制代碼有事件通知,因此效率是非常低。但是如果連線很少的情況下,select 和epoll的lt 觸發模式相比,...
epoll模型有兩種工作模式
epoll模型有兩種工作模式,et和lt,兩種模式下都有一些細節值得注意,以下是一些思考 一 et模式下 q1 呼叫accept時,到底tcp完成佇列裡有多少個已經建立好的連線?這裡又得分情況來說 沒有連線。這種情況發生在tcp連線被客戶端夭折,即在服務端呼叫accept之前客戶端給出乙個rst。該...
epoll的兩種工作模式LT ET
之前已經介紹過了epoll的工作機制,以及它和select,poll之間的區別,傳送門 接下來我們詳細介紹一下它的兩種工作模式。lt level triggered lt模式,也叫做水平觸發模式。在該模式下,當有事件發生並呼叫epoll wait後,若未及時處理,下一次呼叫epoll wait仍會繼...