IO復用模型 epoll

2021-09-11 07:39:32 字數 4299 閱讀 1379

參見: 

1. epoll模型簡介

epoll是linux多路服用io介面select/poll的加強版,e對應的英文單詞就是enhancement,中文翻譯為增強,加強,提高,充實的意思。所以epoll模型會顯著提高程式在大量併發連線中只有少量活躍的情況下的系統cpu利用率。

epoll把使用者關心的檔案描述符上的時間放在核心的乙個事件表中,無需像select和poll那樣每次呼叫都重複傳入檔案描述符集。

epoll在獲取事件的時候,無需遍歷整個被監聽的檔案描述符集合,而是遍歷那些被核心io事件非同步喚醒而加入ready佇列的描述符集合。

所以,epoll是linux大規模高併發網路程式的首選模型。

2.epoll模型的api

epoll使用一組函式來完成任務

2.1 函式epoll_create

建立乙個epoll控制代碼,控制代碼的英文是handle,相通的意思是把手,把柄。

#include

int epoll_create(int size);

//返回值:若成功,返回乙個非負的檔案描述符,若出錯,返回-1。

該函式返回乙個檔案描述符,用來唯一標示核心中這個事件表,sizeof引數提示核心要監聽的檔案描述符個數,這與記憶體大小有關。

返回的檔案描述符將是其他所有epoll系統呼叫的第乙個引數,以指定要訪問的核心時間表,所以用該返回的檔案描述符相當與其他epoll呼叫的把手、把柄一樣。

2.2 函式epoll_ctl

該函式用來操作epoll的核心事件表

#include

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

//返回值:若成功,返回0,若出錯返回-1。

epfd就是函式epoll_create建立的控制代碼。

op是指定操作型別,有一下三種 

epoll_ctl_add,向epfd註冊fd的上的event

epoll_ctl_mod,修改fd已註冊的event

epoll_ctl_del,從epfd上刪除fd的event 

fd是操作的檔案描述符

event指定核心要監聽事件,它是struct epoll_event結構型別的指標。epoll_event定義如下:

struct epoll_event ;
events成員描述事件型別,將以下巨集定義通過位或方式組合

epollin :表示對應的檔案描述符可以讀(包括對端socket正常關閉)

pollout:表示對應的檔案描述符可以寫

epollpri:表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來)

epollerr:表示對應的檔案描述符發生錯誤

epollhup:表示對應的檔案描述符被結束通話;

epollet: 將epoll設為邊緣觸發(edge triggered)模式,這是相對於水平觸發(level triggered)來說的

epolloneshot:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到epoll佇列裡

data用於儲存使用者資料,是epoll_data_t結構型別,該結構定義如下:

typedef union epoll_data  epoll_data_t;
epoll_data_t是乙個聯合體,fd指定事件所從屬的目標檔案描述符。ptr可以用來指定fd相關的使用者資料,但兩者不能同時使用。

2.3 函式epoll_wait

函式epoll_wait用來等待所監聽檔案描述符上有事件發生

#include

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

//返回值:若成功,返回就緒的檔案描述符個數,若出錯,返回-1,時間超時返回0

epfd就是函式epoll_create建立的控制代碼

timeout是超時事件,-1為阻塞,0為立即返回,非阻塞,大於0是指定的微妙

events是乙個 傳入傳出引數,它是epoll_event結構的指標,用來從核心得到事件的集合

maxevents告知核心events的大小,但不能大於epoll_create()時建立的size

3. lt和et模式

lt(level triggered,電平觸發):lt模式是epoll預設的工作模式,也是select和poll的工作模式,在lt模式下,epoll相當於乙個效率較高的poll。 

採用lt模式的檔案描述符,當epoll_wait檢測到其上有事件發生並將此事件通知應用程式後,應用程式可以不立即處理此事件,當下一次呼叫epoll_wait是,epoll_wait還會將此事件通告應用程式。

et(edge triggered,邊沿觸發):當呼叫epoll_ctl,向引數event註冊epollet事件時,epoll將以et模式來操作該檔案描述符,et模式是epoll的高效工作模式. 

對於採用et模式的檔案描述符,當epoll_wait檢測到其上有事件發生並將此通知應用程式後,應用程式必須立即處理該事件,因為後續的epoll_wait呼叫將不在向應用程式通知這一事件。et模式降低了同意epoll事件被觸發的次數,效率比lt模式高。

epoll 的介面非常簡單,一共就三個函式:

1. int epoll_create(int size);

建立乙個 epoll 的控制代碼, size 用來告訴核心這個監聽的數目一共有多大。這個引數不同於 select() 中的第乙個引數,給出最大監聽的 fd+1 的值。需要注意的是,當建立好 epoll 控制代碼後,它就是會占用乙個 fd 值,在 linux 下如果檢視 /proc/ 程序 id/fd/ ,是能夠看到這個 fd 的,所以在使用完 epoll 後,必須呼叫 close() 關閉,否則可能導致 fd被耗盡。

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll 的事件註冊函式,它不同與 select() 是在監聽事件時告訴核心要監聽什麼型別的事件,而是在這裡先註冊要監聽的事件型別。

第乙個引數是 epoll_create() 的返回值,

第二個引數表示動作,用三個巨集來表示:

epoll_ctl_add :註冊新的 fd 到 epfd 中;

epoll_ctl_mod :修改已經註冊的 fd 的監聽事件;

epoll_ctl_del :從 epfd 中刪除乙個 fd ;

第三個引數是需要監聽的 fd ,

第四個引數是告訴核心需要監聽什麼事, struct epoll_event 結構如下:

struct epoll_event

bzero(&server_addr, sizeof(server_addr));

server_addr.sin_family = af_inet;

server_addr.sin_port = htons(myport);

server_addr.sin_addr.s_addr = htonl(inaddr_any);

//bind

if(bind(socklisten, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)

//listen

if(listen(socklisten, 5) < 0)

// epoll 初始化

epollfd = epoll_create(max_events);

struct epoll_event event;

event.events = epollin|epollet;

event.data.fd = socklisten;

//add event

if(epoll_ctl(epollfd, epoll_ctl_add, socklisten, &event) < 0)

//epoll

while(1)

else if(ret == 0)

//直接獲取了事件數量,給出了活動的流,這裡是和poll區別的關鍵

int i = 0;

for(i=0; i

IO多路復用之epoll模型

基於select和poll輪詢方式的低效性,epoll為了解決這個不足,應運而生,epoll可以告訴伺服器到底是哪些事件就緒了,epoll返回乙個事件集合陣列,告訴我們是前多少個事件就緒了,這樣我們只需要遍歷就緒事件集就可以了。就像我們學生向老師提問的案例一樣,當有若干學生向老師舉手時,老師把這些學...

epoll 實現I O復用

epoll是linux特有的i o復用函式,它能顯著提高程式在大量併發連線中只有少量活躍的情況下的系統cpu利用率 並且epoll使用一組函式來完成任務,而不是單個函式,它無須遍歷整個被偵聽的描述符集,只要遍歷那些核心i o時間非同步喚醒而加入ready佇列的描述符集合即可。但epoll需要使用乙個...

IO復用 epoll函式

由於poll 和select 的侷限,2.6核心以上引用了event poll機制 就是說的epoll 雖然比前2個實現複雜得多,epoll解決了它們共有的基本效能問題,並增加了新的特性。poll 和select 每次呼叫的時候,都需要所有被監聽的檔案的描述符。核心必須遍歷所有被監視的檔案描述符。當...