redis学习笔记: ae
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis学习笔记: ae相关的知识,希望对你有一定的参考价值。
redis是基于事件驱动的,相应的实现都在ae.c当中。
其实个人对于“事件驱动”的理解不是那么明显,只能说从它的实现上来看稍微有一些感觉:
先由外部模块注册感兴趣的事件以及callback,在poll返回时判断是否有相应模块感兴趣的事件,如果有的话就调用注册的callback
/* 代码中的注释是File event structure,个人理解就是外部模块感兴趣的内容以及处理方式。目前用到这个结构的包括:网络事件以及unix套接字上的内部通信 */
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) */
aeFileProc *rfileProc;
aeFileProc *wfileProc;
void *clientData;
} aeFileEvent;
/* 代码中的注释是Time event structure,也就是定时处理的事件,用单链表的形式组织起来。finalizerProc这个成员还不理解有什么作用。目前主进程中应该只有serverCron这一个需要定时处理的事件 */
typedef struct aeTimeEvent {
long long id; /* time event identifier. */
long when_sec; /* seconds */
long when_ms; /* milliseconds */
aeTimeProc *timeProc;
aeEventFinalizerProc *finalizerProc;
void *clientData;
struct aeTimeEvent *next;
} aeTimeEvent;
/* 代码中的注释是A fired event,其实就是在poll返回时,每一个发生的事件 */
typedef struct aeFiredEvent {
int fd; /* 发生事件的套接字,目前只有inet和unix */
int mask; /* fd上发生的事件 */
} aeFiredEvent;
/* 代码中的注释是State of an event based program,个人理解就是对所有事件的管理结构,整个主进程只有一个 */
typedef struct aeEventLoop {
int maxfd; /* 当前最大的fd,目前只有select有用 */
int setsize; /* 这个值就是下面events,fired两个数组的大小 */
long long timeEventNextId;
time_t lastTime; /* Used to detect system clock skew */
aeFileEvent *events; /* 外部注册的感兴趣的事件 */
aeFiredEvent *fired; /* poll返回的事件 */
aeTimeEvent *timeEventHead; /* 定时器事件 */
int stop; /* 如果是1就要退出事件处理流程 */
void *apidata; /* This is used for polling API specific data */
aeBeforeSleepProc *beforesleep; /* 进入poll之前需要处理的事情 */
} aeEventLoop;
ae.c里面使用如下的方式来决定系统使用的poll机制:
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
虽然每个c文件对应的poll机制不同,但都定义了自己的aeApiState以及实现的都是相同的api:
aeApiState,
每种poll机制内部使用的相关结构体,例如:select用到的fdset, epoll用到的epoll_fd以及events数组
aeApiCreate,
poll机制的初始化,每个poll机制都会在这里分配一个新的aeApiState结构,并做一些特定的初始化操作
例如:对于select来说应该是就是初始化fdset,用于select的相关调用;对于epoll来说,需要创建epoll的fd以及epoll使用的events数组
aeApiResize,
调整poll机制中能处理的事件数目,例如:对于select来说,其实只要不超过fdset的最大值(一般系统默认是1024)它就什么都不做,否则返回错误;对于epoll来说,就是重新分配events数组
这个函数只在config阶段会被调用
aeApiFree,
对于select来说,主要就是释放aeApiState的空间
对于epoll来说,主要就是关闭epoll的fd, 释放aeApiState以及events的空间
aeApiAddEvent,
对于select来说,就是往某个fd_set里面增加fd
对于epoll来说,就是在events中增加/修改感兴趣的事件
aeApiDelEvent,
对于select来说,就是从某个fd_set里面删除fd
对于epoll来说,就是在events中删除/修改感兴趣的事件
aeApiPoll,
主要的poll入口,比如select或者epoll_wait
aeApiName
返回poll机制的名字,比如select或者epoll
ae.c里面实现的主流程其实也很简单
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
beforesleep主要是一些(进入poll之前的)准备工作或者是处理上一轮poll中未完成任务的最后一步。后面再仔细看这一部分
aeProcessEvents,第二个参数是AE_ALL_EVENTS,所以在里面会(按顺序)处理file和time两类事件:
调用aeApiPoll时,需要指定超时时间或者死等。自然地,它会从aeTimeEvent的单链表中找出距离当前最近的定时器事件的超时时间,以该时间做为超时时间调用具体的poll函数(select/epoll_wait)。否则,如果没有找到任何超时事件,则会让poll函数进入死等。
不过要注意的是,如果aeProcessEvents的第二个参数指定了AE_DONT_WAIT,那么就不能在poll函数上等,会直接把时间设置为0,也就是具体的poll函数会立刻超时。
aeApiPoll返回之后,处理file事件(如果有的话)。最后,再调用processTimeEvents处理time事件(如果aeProcessEvents的第二个参数指定了AE_TIME_EVENTS标记)
主要的处理流程大致就是这样,后面有机会再具体分析。
以上是关于redis学习笔记: ae的主要内容,如果未能解决你的问题,请参考以下文章