typedef struct aeEventLoop {
int maxfd; /* 当前注册的最大文件描述符 */
int setsize; /* 允许的最大文件描述符 */
long long timeEventNextId;
time_t lastTime; /* Used to detect system clock skew */
aeFileEvent *events; /* 注册的事件数组指针 */
aeFiredEvent *fired; /* 已完成的事件数组指针 */
aeTimeEvent *timeEventHead; /* 时间事件链表头指针 */
int stop;
void *apidata; /* 用来存io复用模型返回已完成事件的数组指针 */
aeBeforeSleepProc *beforesleep;
} aeEventLoop;
redis启动初始化时就会创建一个aeEventLoop的实例
aeEventLoop *aeCreateEventLoop(int setsize)
这个函数工作为:
设置最大文件描述符大小 为客户端数量(默认10000)加上128 (以保证安全)
直接申请events和fired的所需的内存空间,大小都为sizeof(aeFileEvent) setsize 可能会有些许的空间浪费 但问题不大
其他变量初始化
返回一个事件循环的指针
其中aeFileEvent数据结构如下
/* File event structure */
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) 标准这是一个读事件还是写事件*/
aeFileProc *rfileProc; /* 读事件时执行的函数 */
aeFileProc *wfileProc; /* 写事件时执行的函数 */
void *clientData;
} aeFileEvent;
aeTimeEvent数据结构如下
typedef struct aeTimeEvent {
long long id; /* 事件id 是不断递增的 */
long when_sec; /* 执行事件的时间(秒数) */
long when_ms; /* 执行事件的时间(毫秒数) */
aeTimeProc *timeProc; /* 执行的函数 */
aeEventFinalizerProc *finalizerProc; /* 时间事件从链表中删除时执行的函数 非必须 */
void *clientData;
struct aeTimeEvent *next; /* 下一个时间事件的指针 */
} aeTimeEvent;
在创建aeEventLoop之后会注册第一个时间事件函数(serverCron),做一些异步/定时的操作,其中注册事件的函数为
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc)
这个函数工作为:
创建一个时间事件(aeTimeEvent), 并把这个新创建的时间事件放到链表的头部 (时间事件的存储结构是链表而不是如同文件事件一样是数组)
返回新建事件的id
注册第一个时间事件serverCron之后注册 监听端口(tcp socket) 的文件描述符fd和处理连接的函数acceptTcpHandler 为可读的文件事件 注册文件事件的函数为
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
这个函数工作为:
将文件事件的mask和proc 放到对应的eventLoop->events[fd]内,(索引值就是fd),同时使用epoll_clt注册该fd (也可以是select kqueue,编译时根据操作系统确定)
返回自定义的成功/失败值(AE_OK AE_ERR)
时间事件和文件事件的执行函数为 (只要eventLoop->stop为0就会一直循环执行)
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
这个函数主要工作为:
首先获取距当前时间 最近一次将要执行的时间事件的 时间(如果负数 则赋为0)(会遍历整个链表)
然后执行aeApiPoll函数 获取已经可读/写的文件描述符 (存在eventLoop的fired数组中), 以epoll_wait为例, epoll_wait调用的等待时间为上述获取时间。 (若没有时间事件或为0 则不进行阻塞 无论有无事件完成都立即返回)
之后遍历eventLoop->fired数组 (索引值不等于fd, 数组长度由aeApiPoll返回值确定),执行对应的事件的fd在eventLoop->events中注册的读/写函数
最后执行processTimeEvents函数,遍历时间事件链表,先检查被标记为删除的事件并删除(如果设置finalizerProc就执行),再执行扔在链表里且时间已到的事件。根据timePorc的返回值是否等于AE_NOMORE判断执行之后是否对该事件标记为删除。