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操作,只要還有沒有處理的事件就會一直通知.以 來說明問題 首先...