關於epollhtml
上一篇博文提到過select的使用方法。今天說一下epoll的使用方法。epoll相對於select而言有以下幾個方面的優勢:python
對於要監聽的fd的數量沒有限制linux
內核會保存須要監聽的fd,無需每次都進行初始化數組
只返回產生事件(可讀取、可寫等)的fd,無需遍歷監聽的全部fdsocket
使用epoll主要用進行一步阻塞的調用。其基本的步驟爲:函數
首先經過epoll_create調用建立一個epoll文件描述符(epfd)ui
經過epoll_ctl對這個描述符進行設置,具體包括:添加須要監聽的文件描述符、socket描述符等;設置相關監聽的類型(例如監聽是否可讀:EPOLLIN,)等spa
經過epoll_wait進行事件的監聽htm
看一段代碼示例:事件
#include <sys/epoll.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main(int argc, char* argv[])
{
//epoll_event用於epoll_ctl中與關聯的fd進行設置,events用於epoll_wait返回能夠處理的fd的信息
struct epoll_event epollEvent, events[10];
//監聽時間設置爲可讀時觸發
epollEvent.events = EPOLLIN ;
epollEvent.data.fd = 0;
char buf[100];
//建立epfd
int epollFd = epoll_create(10);
const char* errStr = "epoll_Create failed\n";
//若是建立失敗,則返回
if(epollFd < 0)
{
write(2, errStr, 20);
exit(-1);
}
errStr = "epoll_ctl failed\n";
//監聽標準輸入, 將stdin添加至監聽fd集合中
if(-1 == epoll_ctl(epollFd, EPOLL_CTL_ADD, 0, &epollEvent))
{
write(2, errStr, 17);
exit(-1);
}
int nfds;
const char* splitLine = "----------\n";
while(1)
{
//當stdin可讀時,epoll_wait返回,超時時間設置爲一直阻塞,知道有可用的fd時返回
nfds = epoll_wait(epollFd, events, 10, -1);
int i;
//對可用的fd進行處理
for(i = 0; i < nfds; ++i)
{
int readCount = read(events[i].data.fd, buf, 5);
if(0 == readCount)
{
close(epollFd);
exit(0);
}
write(1, buf, readCount);
write(1, splitLine, 11);
}
//epoll_ctl(epollFd, EPOLL_CTL_MOD, 0, &epollEvent);
}
close(epollFd);
return 0;
}
輔助的python腳本
#!/usr/bin/env python
#coding:utf-8
import time
output_str = "hello world"
for i in range(0, 5):
print output_str
time.sleep(2)
gcc -o test_epoll test_epoll.c -g -Wall
python cat_py.py | ./test_epoll
下面具體講解一下每一個函數的用法,詳情請參考linux的官方文檔(http://man7.org/linux/man-pages/man7/epoll.7.html),
頭文件:#include <sys/epoll.h>
函數原型:int epoll_create(int size);
size爲須要監聽的文件描述的數量,內核用該值去分分配相應的空間,可是自從內核2.6.8以後,使用動態的空間分配方式,size的取值已經被忽略,可是必須設置爲大於0的數值。該函數的返回值爲epoll的文件描述符,該描述符將用於後續的epoll_ctl和epoll_wait等的操做。
函數原型: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
其中epfd爲epoll_create的返回值,就是epoll的文件描述符,全部須要監聽的fd都須要與epfd進行關聯。
op爲EPOLL_CTL_ADD、EPOLL_CTL_MOD和EPOLL_CTL_DEL三者之一。EPOLL_CTL_ADD用於添加須要監聽的文件描述符,即參數中的fd,將fd添加到須要監聽的文件描述符集合中。EPOLL_CTL_MOD用於改變與fd進行關聯的event。EPOLL_CTL_DEL用於將fd從監聽的集合中刪除。
對於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 */
};
此處主要設置epoll_event中的events,events的取值主要由EPOLLIN、EPOLLOUT、EPOLLRDHUP和EPOLLET等具體參考http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
EPOLLIN監聽fd是否可讀,EPOLLOUT監聽fd是否可寫。須要着重指出的是:epoll默認的事件觸發機制是水平觸發,所以若是使用EPOLLET則設置爲邊緣觸發,對於邊緣觸發,在一次觸發之後(epoll_wait返回處理後)要須要再次調用epoll_ctl使用EPOLL_CTL_MOD進行事件關聯。不然若是本次處理不徹底,例若有2k的字節可讀,而只讀取了1k,若是再也不次設置,則再次調用epoll_wait後不會返回,於是可能會形成數據的丟失。epoll_data與fd想關聯,保存着fd相關的信息。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epfd爲epoll的fd,events爲返回的對應fd的event的數組,maxevents爲一次能夠處理的fd的最大數量,一般設置爲須要監聽的fd的數量,timeout爲超時返回的秒數。若是爲0,則當即返回,若是爲-1則一直阻塞,直到有須要處理的fd則返回。epoll_wait的返回值爲目前可用的fd的數量。另外,若是發生錯誤則返回-1並設置errno。