AE 事件循环器: 一个简单的事件驱动的编程库,程序库封装了网络模型:evport、epoll、kqueue、select
- 通过宏来控制引入不同模型的.c文件实现,不同操作系统定义不同的宏,编译时会编译对应的文件
- linux系统使用epoll,apple/Mac/bsd系统使用kqueue,__sun使用evport(宏__sun实际对应Solaris系统),
- 其他操作系统如windows则使用select。
- 根据不同的宏加载不同的网络IO模型(调用所支持的多路复用层evport、epoll、kqueue、select)
Redis通过将对应事件注册到eventloop中,然后不断循环检测有无事件触发, 目前Redis支持超时事件和网络IO读写事件的注册
// 文件事件码
#define AE_NONE 0 /* No events registered. 没有注册事件*/
#define AE_READABLE 1 /* Fire when descriptor is readable. 可读取时触发*/
#define AE_WRITABLE 2 /* Fire when descriptor is writable. 可写入时触发*/
/*
* 对于可写,如果在同一事件循环迭代中已触发可读事件,则永远不要触发该事件。
* 当您希望在发送回复之前将内容保留到磁盘上并且希望以组的方式进行操作时很有用。
* 就是读的时候不要改动
*/
#define AE_BARRIER 4
#define AE_FILE_EVENTS 1 // 文件相关事件
#define AE_TIME_EVENTS 2 // 时间相关事件
#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS) // ALL包括文件和时间相关事件
#define AE_DONT_WAIT 4 // 能不sleep就不sleep,如果没有定时事件,会一直sleep直到IO事件发生
#define AE_CALL_AFTER_SLEEP 8
#define AE_NOMORE -1
#define AE_DELETED_EVENT_ID -1
/* File event structure 文件事件,主要是用于网络IO事件,触发后会生成事件结构aeFiredEvent,用于后续处理*/
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */
aeFileProc *rfileProc; // 读函数
aeFileProc *wfileProc; // 写函数
void *clientData; // 客户端数据
} aeFileEvent;
/* Time event structure 时间事件,主要用于一些后台事件*/
typedef struct aeTimeEvent {
long long id; /* 事件Id ,递增的 time event identifier. */
long when_sec; /* 执行事件的时间(秒数) seconds */
long when_ms; /* 执行事件的时间(毫秒数) milliseconds */
aeTimeProc *timeProc; // 处理的函数
aeEventFinalizerProc *finalizerProc; // 时间事件从链表中移除时执行的函数,非必须
void *clientData; // 客户端传入的数据
struct aeTimeEvent *prev; // 前一个时间事件的指针 ,时间事件用的是链表,文件事件用的是数组
struct aeTimeEvent *next; // 下一个时间事件的指针
} aeTimeEvent;
/* A fired event aeFileEvent事件触发后会创建事件结构aeFiredEvent,用于后续处理 */
typedef struct aeFiredEvent {
int fd; // 描述符
int mask;
} aeFiredEvent; // 就绪事件
/* State of an event based program */
// ae 事件的总结构体
typedef struct aeEventLoop {
int maxfd; /* 当前注册的最高的fd .highest file descriptor currently registered */
int setsize; /* 允许的fd的最大数量 max number of file descriptors tracked */
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; // 事件执行前的前置函数
aeBeforeSleepProc *aftersleep; // 事件执行后的后置函数
} aeEventLoop;
// 主要负责eventloop结构的创建和初始化以及模型的初始化
aeEventLoop *aeCreateEventLoop(int setsize);
// 释放event loop
void aeDeleteEventLoop(aeEventLoop *eventLoop);
// 停止事件分配
void aeStop(aeEventLoop *eventLoop);
// 创建监听事件
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData);
// 删除文件事件
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask);
// 获取fd文件事件对应的类型(读或写)
int aeGetFileEvents(aeEventLoop *eventLoop, int fd);
// 创建时间事件
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc);
// 删除时间事件
int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id);
// 分配事件
int aeProcessEvents(aeEventLoop *eventLoop, int flags);
// 等待milliseconds直到fd对应的事件可读或可写
int aeWait(int fd, int mask, long long milliseconds);
// ae事件轮询主函数
void aeMain(aeEventLoop *eventLoop);
// 获取当前网络模型
char *aeGetApiName(void);
// 事件执行前的回调函数
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep);
// 事件执行前的回调函数
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep);
// 获取eventloop的事件个数
int aeGetSetSize(aeEventLoop *eventLoop);
// 重置eventloop 事件个数
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
aeCreateEventLoop:用于eventloop结构的创建和初始化以及模型的初始化
// setsize 表示eventloop可以监听的网络事件fd的个数,不含超时事件,网络事件超过setsize,将不能注册
// fd为0、1、2分别表示标准输入、标准输出和错误输出
// 主要负责eventloop结构的创建和初始化以及模型的初始化
aeEventLoop *aeCreateEventLoop(int setsize) {
aeEventLoop *eventLoop;
int i;
// 创建
if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
// 为要注册的事件分配空间
eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
// 为激活的待处理的事件分配空间
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
// 空间分配失败
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
// 设置监听事件总数
eventLoop->setsize = setsize;
// 更新为当前时间
eventLoop->lastTime = time(NULL);
//
eventLoop->timeEventHead = NULL;
eventLoop->timeEventNextId = 0;
eventLoop->stop = 0;
eventLoop->maxfd = -1;
eventLoop->beforesleep = NULL;
eventLoop->aftersleep = NULL;
// 调用API,根据宏定义,调用相应的API(分别是ae_select/ae_kqueue/ae_evport/ae_epoll文件中的API)
if (aeApiCreate(eventLoop) == -1) goto err;
/* Events with mask == AE_NONE are not set. So let's initialize the
* vector with it.
* 用一个连续数组,初始化监听事件
* 初始化时,设置mask == AE_NONE 表示没有注册事件
* */
for (i = 0; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return eventLoop; // 返回事件循环
err:
if (eventLoop) {
// error的话就释放eventloop ,返回NULL
zfree(eventLoop->events);
zfree(eventLoop->fired);
zfree(eventLoop);
}
return NULL;
}
aeCreateFileEvent:创建监听事件
// 创建监听事件
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
// 判断fd 是否大于eventloop设置的事件个数
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
}
// 取出相应的事件,作为下标记录事件,避免循环查找
aeFileEvent *fe = &eventLoop->events[fd];
// 添加IO事件,不同的模型调用不同的aeApiAddEvent函数
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
return AE_ERR;
// 文件类型按位或
fe->mask |= mask;
// 根据最终的类型设置读写回调函数
if (mask & AE_READABLE) fe->rfileProc = proc;
if (mask & AE_WRITABLE) fe->wfileProc = proc;
fe->clientData = clientData; // 读写操作的clientdata
// 如果fd 大于当前最大的eventloop->maxfd
if (fd > eventLoop->maxfd)
eventLoop->maxfd = fd;
return AE_OK;
}
aeDeleteFileEvent:删除监听事件
// 删除监听事件
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{
if (fd >= eventLoop->setsize) return;
// 获取对应的监听事件
aeFileEvent *fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return;
/* We want to always remove AE_BARRIER if set when AE_WRITABLE
* is removed. */
if (mask & AE_WRITABLE) mask |= AE_BARRIER;
// 删除IO事件,不同的网络模型调用不同的aeApiDelEvent函数
aeApiDelEvent(eventLoop, fd, mask);
// 清除对应的类型标记
fe->mask = fe->mask & (~mask);
// 如果删除的是 maxfd并且对应的事件为空,就更新maxfd
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
/* Update the max fd 更新最大的fd */
int j;
for (j = eventLoop->maxfd-1; j >= 0; j--)
if (eventLoop->events[j].mask != AE_NONE) break;
eventLoop->maxfd = j;
}
}
processTimeEvents:处理时间事件
/* Process time events 处理时间事件 */
static int processTimeEvents(aeEventLoop *eventLoop) {
int processed = 0;
aeTimeEvent *te;
long long maxId;
time_t now = time(NULL);
/* If the system clock is moved to the future, and then set back to the
* right value, time events may be delayed in a random way. Often this
* means that scheduled operations will not be performed soon enough.
*
* Here we try to detect system clock skews, and force all the time
* events to be processed ASAP when this happens: the idea is that
* processing events earlier is less dangerous than delaying them
* indefinitely, and practice suggests it is.
* 如果上次处理定时器事件的时间比当前时间还要大,说明事件有偏差,强制所有时间事件尽快处理
* 将所有时间事件的间隔秒数设置为0
* */
if (now < eventLoop->lastTime) {
te = eventLoop->timeEventHead;
while(te) {
te->when_sec = 0; // 将所有时间事件的间隔秒数设置为0
te = te->next; // 下一个时间事件
}
}
// 更新最近处理时间事件的时间
eventLoop->lastTime = now;
te = eventLoop->timeEventHead;
maxId = eventLoop->timeEventNextId-1; // 获取最大的时间事件id
while(te) {
long now_sec, now_ms;
long long id;
/* Remove events scheduled for deletion. */
// 移除已经标记删除的时间事件
if (te->id == AE_DELETED_EVENT_ID) {
aeTimeEvent *next = te->next;
if (te->prev)
te->prev->next = te->next;
else
eventLoop->timeEventHead = te->next;
if (te->next)
te->next->prev = te->prev;
if (te->finalizerProc)
te->finalizerProc(eventLoop, te->clientData);
zfree(te);
te = next;
continue;
}
/* Make sure we don't process time events created by time events in
* this iteration. Note that this check is currently useless: we always
* add new timers on the head, however if we change the implementation
* detail, this check may be useful again: we keep it here for future
* defense.
* 保证不处理时间事件处理过程中创建的时间事件,暂时没用,因为添加时间事件总是在头部添加
* */
if (te->id > maxId) {
te = te->next;
continue;
}
aeGetTime(&now_sec, &now_ms);
if (now_sec > te->when_sec ||
(now_sec == te->when_sec && now_ms >= te->when_ms))
{
int retval;
id = te->id;
// 调用时间事件处理函数
retval = te->timeProc(eventLoop, id, te->clientData);
processed++;
if (retval != AE_NOMORE) {
// 如果时间事件还需要重复使用,就需要设置新的触发时间
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
} else { //否则,标记时间事件失效
te->id = AE_DELETED_EVENT_ID;
}
}
te = te->next;
}
return processed;
}
aeProcessEvents:处理正在进行的时间事件和文件事件,没有特殊标志的函数休眠直到一些文件事件被触发,或下一个时间事件发生,返回处理的事件个数
flags =0 什么都不处理
flags =AE_ALL_EVENTS处理所有事件
flags =AE_FILE_EVENTS处理所有文件事件
flags =AE_TIME_EVENTS处理所有时间事件
flags =AE_DONT_WAIT 返回ASAP,直到所有不需要被等待的可能被处理的事件都被处理
flags =AE_CALL_AFTER_SLEEP 调用休眠后函数
aeWait:等待milliseconds,直到有可读或者可写事件触发或者异常
/*
* 等待milliseconds,直到有可读或者可写事件触发或者异常
* */
int aeWait(int fd, int mask, long long milliseconds) {
struct pollfd pfd;
int retmask = 0, retval;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fd;
if (mask & AE_READABLE) pfd.events |= POLLIN;
if (mask & AE_WRITABLE) pfd.events |= POLLOUT;
// poll(将当前的文件指针放到等待队列
if ((retval = poll(&pfd, 1, milliseconds))== 1) {
if (pfd.revents & POLLIN) retmask |= AE_READABLE; // POLLIN 有数据可读,
if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE; // POLLOUT 数据可写
if (pfd.revents & POLLERR) retmask |= AE_WRITABLE; // POLLERR 指定描述符发生错误
if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE; // POLLHUP 指定文件描述符挂起事件
return retmask;
} else {
return retval;
}
}
aeMain:aeMain是redis服务器进程的循环函数
// aeMain是redis服务器进程的循环函数
void aeMain(aeEventLoop *eventLoop) {
// stop 初始为0
eventLoop->stop = 0;
while (!eventLoop->stop) {
// 调用beforesleep函数
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
// 派发所有的事件,第二个参数决定处理哪类事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}