本文为作者原创,转载请注明出处:http://my.oschina.net/fuckphp/blog/505956
Redis 的 ae模块的代码主要分布在 ae.c ae.h 还有 ae_*.c 中,分别实现了epoll、evport、kqueue、select几种网络模型,本文将以epoll为例,对Redis的ae模块进行学习。
Redis ae.c 事件模块笔记
1.ae模块的核心结构
aeEventLoop
aeEveltLoop 主要定义描述了redis ae模块的主循环信息,在整个事件的循环周期内(aeEveltLoop.stop!=0),保存了io事件和时间事件的事件列表等信息。
typedef struct aeEventLoop { int maxfd; /* 当前注册的最大描述符 */ int setsize; /* 最大文件描述符个数 */ long long timeEventNextId; //自增id 用于给每个事件分配id time_t lastTime; /* 用于校准系统时间,每次事件事件执行前更新该时间 */ aeFileEvent *events; /* 存放注册的事件 */ aeFiredEvent *fired; /* 已经触发的事件保存在这里 */ aeTimeEvent *timeEventHead; /* 时间事件单向链表 包含了未来将会事件的事件 */ int stop; void *apidata; /* 用户的自定义数据用于传递给回调函数 */ aeBeforeSleepProc *beforesleep; /* 每一个事件循环都会执行一次 */ } aeEventLoop;
aeFileEvent
aeFileEvent 描述了redis ae模块关于io类事件的主要信息,包含当前事件的读写模式回调函数等,注册一个io事件后aeFileEvent结构被记录在aeEventLoop的events属性中,根据文件描述来决定结构在events属性中的位置
/* File event structure */ typedef struct aeFileEvent { int mask; /* one of AE_(READABLE|WRITABLE) 读写模式的定义*/ aeFileProc *rfileProc; /* 读操作回调函数的定义 */ aeFileProc *wfileProc; /* 写操作回调函数的定义 */ void *clientData; /* 用户自定义数据用于传递给回调函数 */ } aeFileEvent;
aeTimeEvent
aeTimeEvent 描述了redis时间事件的主要信息,Redis的事件事件结构就是一个链表节点,多个时间事件组成链表,Redis每个循环周期会遍历该链表(保存在aeEventLoop.timeEventHead中),进行时间校准后,执行触发的时间事件。
/* Time event structure */ typedef struct aeTimeEvent { long long id; /* 时间事件的id,根据aeEventLoop.timeEventNextId属性自增来生成 */ long when_sec; /* 执行当前事件的时间 单位 秒 */ long when_ms; /* 执行当前事件的时间 单位 毫秒 */ aeTimeProc *timeProc; /* 事件触发时候执行 */ aeEventFinalizerProc *finalizerProc; /* 事件执行完成并且从时间事件链表中删除的时候执行 */ void *clientData; /* 用户自定义数据用于传递给回调函数 */ struct aeTimeEvent *next; /* 指向链表的下一个节点 */ } aeTimeEvent;
aeFiredEvent
aeFiredEvent 保存了每次被触发io事件的文件描述符和读写模式信息
/* A fired event */ typedef struct aeFiredEvent { int fd; /* 触发io事件的文件描述符 */ int mask; /* 描述当前触发的读写模式 */ } aeFiredEvent;
2.ae模块核心结构之间的关系
该部分使用一张简单的图片来表示,具体内容可以结合上文注释来理解(图片偷自 @C_Z 的文章 :) )
3.ae模块执行主流程
创建 aeEventLoop同时初始化 io事件列表和事件事件链表和相关的状态信息,同时创建epoll对象
创建 时间事件 设置时间事件触发的回调函数,执行时间并加入到主循环的timeEventHead链表中
创建 io事件 根据 设置 epoll监听的文件表述符描述符读写状态同时更新到aeEventLoop的events事件列表相应位置中(根据文件描述符来定位指针的偏移量)
主流流程执行
偷懒。。稍候补图。。。。
执行 aeBeforeSleepProc 回调函数
读取最近一个时间事件的距离事件
执行epoll_wait 监听文件描述符,监听超时时间截止到下一个时间事件触发之前或者有io事件触发
将触发的io事件放入aeEventLoop.FiredEvent中
读取触发的io事件列表并执行对应的读写事件
校准系统时间,如果系统时间与lastTime不符合实际情况,则将时间事件链表中所有事件都标记为立即执行
执行每一一个时间事件的回调函数
根据回调函数返回值决定,当前事件该从时间事件链表删除还是重新设置下次执行时间
如果时间事件从俩表删除则执行 aeEventFinalizerProc 回调函数
进入下一个循环周期 知道 aeEventLoop.stop == 0
Redis2.8.9源码 src/ae.h src/ae.c src/ae_epoll.c