學一下redis源碼中epoll的使用,由於功力有限不足之處望大家指正。
/*
* 創建一個新的 epoll 實例,並將它賦值給 eventLoop
*/
static int aeApiCreate(aeEventLoop *eventLoop) {
aeApiState *state = zmalloc(sizeof(aeApiState));
if (!state) return -1;
// 初始化事件槽空間
state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
if (!state->events) {
zfree(state);
return -1;
}
// 創建 epoll 實例
state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
if (state->epfd == -1) {
zfree(state->events);
zfree(state);
return -1;
}
// 賦值給 eventLoop
eventLoop->apidata = state;
return 0;
}
/*
* 事件狀態
*/
typedef struct aeApiState {
// epoll_event 實例描述符
int epfd;
// 事件槽
struct epoll_event *events;
} aeApiState;
/* State of an event based program
*
* 事件處理器的狀態
*/
typedef struct aeEventLoop {
// 目前已註冊的最大描述符
int maxfd; /* highest file descriptor currently registered */
// 目前已追蹤的最大描述符
int setsize; /* max number of file descriptors tracked */
// 用於生成時間事件 id
long long timeEventNextId;
// 最後一次執行時間事件的時間
time_t lastTime; /* Used to detect system clock skew */
// 已註冊的文件事件
aeFileEvent *events; /* Registered events */
// 已就緒的文件事件
aeFiredEvent *fired; /* Fired events */
// 時間事件
aeTimeEvent *timeEventHead;
// 事件處理器的開關
int stop;
// 多路複用庫的私有數據
void *apidata; /* This is used for polling API specific data */
// 在處理事件前要執行的函數
aeBeforeSleepProc *beforesleep;
} aeEventLoop;
aeApiCreate函數爲epoll_event分配空間,epoll_create創建一個epoll句柄,參數size用來告訴內核監聽的文件描述符的個數,epoll句柄就是函數的返回值。該返回值保存在aeEventLoop的apidata裏面。
/*
* 關聯給定事件到 fd
*/
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
aeApiState *state = eventLoop->apidata;
struct epoll_event ee;
/* If the fd was already monitored for some event, we need a MOD
* operation. Otherwise we need an ADD operation.
*
* 如果 fd 沒有關聯任何事件,那麼這是一個 ADD 操作。
*
* 如果已經關聯了某個/某些事件,那麼這是一個 MOD 操作。
*/
int op = eventLoop->events[fd].mask == AE_NONE ?
EPOLL_CTL_ADD : EPOLL_CTL_MOD;
// 註冊事件到 epoll
ee.events = 0;
mask |= eventLoop->events[fd].mask; /* Merge old events */
if (mask & AE_READABLE) ee.events |= EPOLLIN;
if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
ee.data.u64 = 0; /* avoid valgrind warning */
ee.data.fd = fd;
if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
return 0;
}
epoll_ctl是事件註冊函數,註冊要監聽的事件類型。state->epfd是epoll_create的返回值,op表示動作,EPOLL_CTL_ADD:註冊新的fd到epfd中;EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;EPOLL_CTL_DEL:從epfd中刪除一個fd;第三個參數是需要監聽的fd,第四個參數是告訴內核需要監聽什麼事。通信每過一個階段,監聽事件的狀態也會發生改變,通過epoll_ctl來改變。
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 */
};
/*
* 獲取可執行事件
*/
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
printf("---------epoll aeApiPoll----------\n");
aeApiState *state = eventLoop->apidata;
int retval, numevents = 0;
// 等待時間
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);//epoll_wait用於向用戶進程返回ready list
// 有至少一個事件就緒?
if (retval > 0) {
int j;
// 爲已就緒事件設置相應的模式
// 並加入到 eventLoop 的 fired 數組中
numevents = retval;
for (j = 0; j < numevents; j++) {
int mask = 0;
struct epoll_event *e = state->events+j;
if (e->events & EPOLLIN) mask |= AE_READABLE;
if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
if (e->events & EPOLLERR) mask |= AE_WRITABLE;
if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
eventLoop->fired[j].fd = e->data.fd;
eventLoop->fired[j].mask = mask;
}
}
// 返回已就緒事件個數
return numevents;
}
epoll_wait等待事件的產生參數events用來從內核得到事件的集合,setsize告之內核這個events有多大,這個 maxevents的值不能大於創建epoll_create()時的size,參數tvp是超時時間。