epoll
是linux核心中的一種可擴充套件io事件處理機制,最早在 linux 2.5.44核心中引入,可被用於代替posix select 和 poll 系統呼叫,並且在具有大量應用程式請求時能夠獲得較好的效能( 此時被監視的檔案描述符數目非常大,與舊的 select 和 poll 系統呼叫完成操作所需 o(n) 不同, epoll能在o(1)時間內完成操作,所以效能相當高),epoll 與 freebsd的kqueue類似,都向使用者空間提供了自己的檔案描述符來進行操作。
int epoll_create(int size);
建立乙個epoll的控制代碼,size用來告訴核心需要監聽的數目一共有多大。當建立好epoll控制代碼後,它就是會占用乙個fd值,在linux下如果檢視/proc/程序id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須呼叫close() 關閉,否則可能導致fd被耗盡。
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_dataepoll_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事件有兩種模型level triggered (lt)和edge triggered (et):
lt(level triggered,水平觸發模式)是預設的工作方式,並且同時支援 block 和 non-block socket。在這種做法中,核心告訴你乙個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行io操作。如果你不作任何操作,核心還是會繼續通知你的,所以,這種模式程式設計出錯誤可能性要小一點。
et(edge-triggered,邊緣觸發模式)是高速工作方式,只支援no-block socket。在這種模式下,當描述符從未就緒變為就緒時,核心通過epoll告訴你。然後它會假設你知道檔案描述符已經就緒,並且不會再為那個檔案描述符傳送更多的就緒通知,等到下次有新的資料進來的時候才會再次出發就緒事件。
我們將實現乙個簡單的tcp 伺服器,該迷你伺服器將會在標準輸出上列印處客戶端傳送的資料,首先我們建立並繫結乙個 tcp 套接字:
staticintcreate_and_bind (char *port)
for (rp = result; rp != null; rp = rp->ai_next)
close (sfd);
}if (rp == null)
freeaddrinfo (result);
return sfd;
}
create_and_bind() 包含了如何建立 ipv4 和 ipv6 套接字的**塊,它接受一字串作為埠引數,並在 result 中返回乙個 addrinfo 結構,
struct addrinfo;
如果函式成功則返回套接字,如果失敗,則返回 -1,
下面,我們將乙個套接字設定為非阻塞形式,函式如下:
staticintmake_socket_non_blocking (int sfd)
flags |= o_nonblock;
s = fcntl (sfd, f_setfl, flags);
if (s == -1)
return
0;}
接下來,便是主函式**,主要用於事件迴圈:
#define maxevents 64intmain (int argc, char *ar**)
sfd = create_and_bind (ar**[1]);
if (sfd == -1)
abort ();
s = make_socket_non_blocking (sfd);
if (s == -1)
abort ();
s = listen (sfd, somaxconn);
if (s == -1)
efd = epoll_create1 (0);
if (efd == -1)
event.data.fd = sfd;
event.events = epollin | epollet;
s = epoll_ctl (efd, epoll_ctl_add, sfd, &event);
if (s == -1)
/*buffer where events are returned
*/events = calloc (maxevents, sizeof
event);
/*the event loop
*/while (1)
else
if (sfd == events[i].data.fd)
else
}s = getnameinfo (&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
ni_numerichost | ni_numericserv);
if (s == 0)
/*make the incoming socket non-blocking and add it to the
list of fds to monitor.
*/s = make_socket_non_blocking (infd);
if (s == -1)
abort ();
event.data.fd = infd;
event.events = epollin | epollet;
s = epoll_ctl (efd, epoll_ctl_add, infd, &event);
if (s == -1)
}continue;
}else
break;
}else
if (count == 0)
/*write the buffer to standard output
*/s = write (1, buf, count);
if (s == -1)
}if (done)}}
}free (events);
close (sfd);
return exit_success;
}
main() 首先呼叫create_and_bind() 建立套接字,然後將其設定為非阻塞的,再呼叫listen(2)。之後建立乙個epoll 例項 efd(檔案描述符),並將其加入到sfd的監聽套接字中以邊沿觸發方式等待事件輸入。
外層的 while 迴圈是主事件迴圈,它呼叫了epoll_wait(2),此時執行緒仍然被阻塞等待事件,當事件可用時,epoll_wait(2) 將會在events引數中返回可用事件。
epoll 例項 efd 在每次事件到來並需要新增新的監聽時就會得到更新,並刪除死亡的鏈結。
當事件可用時,可能有一下三種型別:
(全文完)
epoll使用詳解(精髓)
epoll i o event notification facility 在linux的網路程式設計中,很長的時間都在使用select來做事件觸發。在linux新的核心中,有了一種替換它的機制,就是epoll。相比於select,epoll最大的好處在於它不會隨著監聽fd數目的增長而降低效率。因為...
epoll使用詳解(精髓)
epoll i o event notification facility 在linux的網路程式設計中,很長的時間都在使用select來做事件觸發。在linux新的核心中,有了一種替換它的機制,就是epoll。相比於select,epoll最大的好處在於它不會隨著監聽fd數目的增長而降低效率。因為...
epoll使用詳解(精髓)
epoll i o event notification facility 在linux的網路程式設計中,很長的時間都在使用select來做事件觸發。在linux新的核心中,有了一種替換它的機制,就是epoll。相比於select,epoll最大的好處在於它不會隨著監聽fd數目的增長而降低效率。因為...