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
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
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表示已超時。