Epoll學習筆記

2021-06-04 07:27:20 字數 3562 閱讀 8021

epoll 是linux核心中的一種可擴充套件io事件處理機制,最早在 linux 2.5.44核心中引入,可被用於代替posix select 和 poll 系統呼叫,並且在具有大量應用程式請求時能夠獲得較好的效能( 此時被監視的檔案描述符數目非常大,與舊的 select 和 poll 系統呼叫完成操作所需 o(n) 不同, epoll能在o(1)時間內完成操作,所以效能相當高),epoll 與 freebsd的kqueue類似,都向使用者空間提供了自己的檔案描述符來進行操作。

epoll有2種工作方式:lt和et。

lt(level triggered)是預設的工作方式,並且同時支援block和no-block socket.在這種做法中,核心告訴你乙個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行io操作。如果你不作任何操作,核心還是會繼續通知你的,所以,這種模式程式設計出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表.

et (edge-triggered)是高速工作方式,只支援no-block socket。在這種模式下,當描述符從未就緒變為就緒時,核心通過epoll告訴你。然後它會假設你知道檔案描述符已經就緒,並且不會再為那個檔案描述符傳送更多的就緒通知,直到你做了某些操作導致那個檔案描述符不再為就緒狀態了(比如,你在傳送,接收或者接收請求,或者傳送接收的資料少於一定量時導致了乙個ewouldblock 錯誤)。但是請注意,如果一直不對這個fd作io操作(從而導致它再次變成未就緒),核心不會傳送更多的通知(only once),不過在tcp協議中,et模式的加速效用仍需要更多的benchmark確認.

epoll函式:

1、int epoll_create(int size);

建立乙個epoll的控制代碼,size用來告訴核心需要監聽的數目一共有多大。當建立好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的事件註冊函式,第乙個引數是 epoll_create() 的返回值,第二個引數表示動作,使用如下三個巨集來表示:

epoll_ctl_add //註冊新的fd到epfd中;

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

epoll_ctl_del //從epfd中刪除乙個fd;

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

typedef union epoll_data

epoll_data_t;

struct epoll_event ;

events 可以是以下幾個巨集的集合:

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

epollout //表示對應的檔案描述符可以寫;

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

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

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

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

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

當對方關閉連線(fin), epollerr,都可以認為是一種epollin事件,在read的時候分別有0,-1兩個返回值。

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

引數events用來從核心得到事件的集合,maxevents 告之核心這個events有多大,這個 maxevents 的值不能大於建立 epoll_create() 時的size,引數 timeout 是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函式返回需要處理的事件數目,如返回0表示已超時。

epoll_wait執行的原理是

等侍註冊在epfd上的socket fd的事件的發生,如果發生則將發生的sokct fd和事件型別放入到events陣列中。

單個epoll並不能解決所有問題,特別是你的每個操作都比較費時的時候,因為epoll是序列處理的。

epoll 開發框架:

while(1)

setnonblocking(client);//設定非阻塞

ev.events = epollin | epollet;//設定要處理的事件型別

ev.data.fd = client;

if (epoll_ctl(kdpfd, epoll_ctl_add, client, &ev) < 0)

}else

do_use_fd(events[n].data.fd);//fd中是要處理的資料。dosomething}}

這兩個網頁說的蠻清楚的。

附上服務端**:

#include #include #include #include #include #include #include using namespace std;

class ctcpserver

else

}virtual ~ctcpserver()

}public:

int run()

setnonblock(nlistensocket);

sockaddr_in serveraddress;

memset(&serveraddress, 0, sizeof(sockaddr_in));

serveraddress.sin_family = af_inet;

if(null == m_strboundip)

else }

serveraddress.sin_port = htons(m_nserverport);

int on = 1;

setsockopt(nlistensocket,sol_socket,so_reuseaddr,&on,sizeof(on));

if(bind(nlistensocket, (sockaddr *)&serveraddress, sizeof(sockaddr_in)) == -1)

if(listen(nlistensocket, m_nlengthofqueueoflisten) == -1)

int efd;

struct epoll_event ev;

struct epoll_event events[maxepollsize];

efd = epoll_create(maxepollsize);

ev.events = epollin|epollet;

ev.data.fd = nlistensocket;

if(epoll_ctl(efd,epoll_ctl_add,nlistensocket,&ev)<0)

{ cout<<"epoll_ctl() error"<

epoll 學習筆記

epoll有兩種模式,edge triggered 簡稱et 和 level triggered 簡稱lt 在採用這兩種模式時要注意的是,如果採用et模式,那麼僅當狀態發生變化時才會通知,而採用lt模式類似於原來的 select poll操作,只要還有沒有處理的事件就會一直通知.以 來說明問題 首先...

epoll學習筆記

epoll學習筆記 epoll有兩種模式,edge triggered 簡稱et 和 level triggered 簡稱lt 在採用這兩種模式時要注意的是,如果採用et模式,那麼僅當狀態發生變化時才會通知,而採用lt模式類似於原來的select poll操作,只要還有沒有處理的事件就會一直通知.以...

epoll學習筆記

epoll有兩種模式,edge triggered 簡稱et 和 level triggered 簡稱lt 在採用這兩種模式時要注意的是,如果採用et模式,那麼僅當狀態發生變化時才會通知,而採用lt模式類似於原來的 select poll操作,只要還有沒有處理的事件就會一直通知.以 來說明問題 首先...