Redis统一的时间管理器,同时管理文件事件和定时器,
这个管理器的定义:
#if defined(__APPLE__)
#define HAVE_TASKINFO 1
#endif /* Test for backtrace() */
#if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__))
#define HAVE_BACKTRACE 1
#endif /* Test for polling API */
#ifdef __linux__
#define HAVE_EPOLL 1
#endif #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1
#endif #ifdef __sun
#include <sys/feature_tests.h>
#ifdef _DTRACE_VERSION
#define HAVE_EVPORT 1
#endif
#endif /*检查具体使用哪个时间模型,Redis支持四个事件模型*/
#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
#define AE_NONE 0 /*该状态表示事件中该位置没使用*/
#define AE_READABLE 1/*设置了读事件*/
#define AE_WRITABLE 2/*设置了写事件*/ #define AE_FILE_EVENTS 1/*需要监控文件的读事件*/
#define AE_TIME_EVENTS 2/*需要监控文件的写事件*/
#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)/*读写事件同事监控*/
#define AE_DONT_WAIT 4/*处理事件时是否设置延迟时间*/
定时器结构体和文件事件结构体
/* File event structure */
typedef struct aeFileEvent
{
int mask; /*需要监控的读写事件标志位*/
aeFileProc *rfileProc;/*读写事件处理函数*/
aeFileProc *wfileProc;
void *clientData;/*事件参数*/
} aeFileEvent; /* Time event structure */
typedef struct aeTimeEvent
{
long long id; /*定时器事件的ID*/
long when_sec; /*触发的秒*/
long when_ms; /*触发的毫秒*/
aeTimeProc *timeProc;/*指定处理函数*/
aeEventFinalizerProc *finalizerProc;/*// 定时事件清理函数,当删除定时事件的时候会被调用*/
void *clientData;
struct aeTimeEvent *next;/*下一个定时器对象,整体采用链表维护,作者好像知道链表维护的效率不是很高,推荐使用跳表,为啥不推荐使用红黑树嘞。*/
} aeTimeEvent;
事件管理器的定义,在结构体中维护了两个数组,一个存放设置的文件描述符和设置,一个存放经过检测,满足条件的文件描述符和对应的触发状态,在处理的时候直接处理
fired中的数据就好。
typedef struct aeEventLoop
{
int maxfd; /*事件管理器能管理的文件描述符的最大值*/
int setsize; /*管理器能管理的文件描述符的个数*/
long long timeEventNextId; /*定时器用的*/
time_t lastTime; /*定时器最后一次处理时间,定时器中用的,防止修改了系统时间,影响了定时器的判断。*/
aeFileEvent *events; /*原始事件数组,这里面存放的是开始的文件事件,保存文件描述符,需要监控的事件等信息*/
aeFiredEvent *fired; /*触发事件数组,这里面放的是经过系统判断,有对应事件发生的文件描述符的存放数组,和上面一样,都采用数组维护*/
aeTimeEvent *timeEventHead;/*定时器链表*/
int stop;/*该事件管理器是否有效*/
void *apidata; /*因为事件管理器支持很多模型,每个模型都有自己的特殊数据格式,这个变量就是用来存储模型自己的数据,拿epoll举个例子,这个地方会存储aeApiState节点的信息*/
aeBeforeSleepProc *beforesleep;
} aeEventLoop;
具体的接口函数:
/*创建一个事件管理器,需要初始化文件事件,触发事件,定时器事件等,stop值默认为0,最大文件描述符值为-1,并将所有的文件描述符的监控事件类型设置为NULL。 */
1 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 = ;
eventLoop->stop = ;/*设置停用表示为无效*/
eventLoop->maxfd = -;/*设置最大文件描述符的初始化*/
eventLoop->beforesleep = NULL;
if (aeApiCreate(eventLoop) == -)
goto err;
/* Events with mask == AE_NONE are not set. So let's initialize the
* vector with it. */
for (i = ; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;/*AE_NONE代表这个事件没有启用*/
return eventLoop; err:
if (eventLoop) {
zfree(eventLoop->events);
zfree(eventLoop->fired);
zfree(eventLoop);
}
return NULL;
}
/*返回管理器能管理的事件的个数*/
int aeGetSetSize(aeEventLoop *eventLoop)
{
return eventLoop->setsize;
}
/*重置管理器能管理的事件个数*/
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize)
{
int i;
/*如果新大小等于现有的大小则直接返回成*/
if (setsize == eventLoop->setsize)
return AE_OK;
/*如果重置的大小小于当前管理器中最大的文件描述符大小,则不能进行重置,否则会丢失已经注册的事件。*/
if (eventLoop->maxfd >= setsize)
return AE_ERR;
/*调用已经封装好的重置大小函数*/
if (aeApiResize(eventLoop,setsize) == -)
return AE_ERR;
/*重置记录内存块大小*/
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
eventLoop->setsize = setsize; /*将新增部分的事件标记置为无效*/
for (i = eventLoop->maxfd+; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return AE_OK;
}
/*删除事件控制器*/
void aeDeleteEventLoop(aeEventLoop *eventLoop)
{
/*调用封装好的删除事件函数*/
aeApiFree(eventLoop);
/*逐个释放内存*/
zfree(eventLoop->events);
zfree(eventLoop->fired);
zfree(eventLoop);
}
/*增加一个文件事件,参数为事件控制器,文件描述符,事件掩码,处理函数,函数参数,对一个描述符添加多个事件的时候要挨个添加*/
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
if (fd >= eventLoop->setsize) /*检查新事件的描述符大小是否超过了设置的大小,如果超了,已经申请的内存空间中没有位置,返回错误*/
{
errno = ERANGE;
return AE_ERR;
}
aeFileEvent *fe = &eventLoop->events[fd]; /*设置读写事件的掩码和事件处理函数*/
if (aeApiAddEvent(eventLoop, fd, mask) == -)
return AE_ERR;
fe->mask |= mask;
if (mask & AE_READABLE) fe->rfileProc = proc;
if (mask & AE_WRITABLE) fe->wfileProc = proc;
fe->clientData = clientData;
if (fd > eventLoop->maxfd)/*更新文件最大描述符*/
eventLoop->maxfd = fd;
return AE_OK;
}
/*删除文件事件中指定文件描述符的指定事件*/
void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
{
/*检查文件描述符是否超限*/
if (fd >= eventLoop->setsize) return;
/*检查该位置是否启用了*/
aeFileEvent *fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return; /*先把事件从处理模型中去掉*/
aeApiDelEvent(eventLoop, fd, mask);
fe->mask = fe->mask & (~mask);/*去掉读写掩码*/
/*检查是否需要更新管理器中的最大文件描述符的值*/
if (fd == eventLoop->maxfd && fe->mask == AE_NONE)
{
/* Update the max fd */
int j;
/*从最大位置反向找启用的位置,更新最大文件描述符*/
for (j = eventLoop->maxfd-; j >= ; j--)
if (eventLoop->events[j].mask != AE_NONE) break;
eventLoop->maxfd = j;
}
}
/*获取某个文件描述符的注册事件*/
int aeGetFileEvents(aeEventLoop *eventLoop, int fd)
{
if (fd >= eventLoop->setsize) return ;
aeFileEvent *fe = &eventLoop->events[fd]; return fe->mask;
}
/*处理控制器中的所有时间,算是最核心的函数了,参数2为要处理的事件类型*/
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
int processed = , numevents; /*参数2设置的是不处理任何事件就直接返回*/
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS))
return ; /* Note that we want call select() even if there are no
* file events to process as long as we want to process time
* events, in order to sleep until the next time event is ready
* to fire. */
if (eventLoop->maxfd != - || ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT)))
{
int j;
aeTimeEvent *shortest = NULL;
struct timeval tv, *tvp; /*处理文件时间事件,先检查是否需要设置延时时间,
延时时间的计算方法,如果有定时器事件,就设定定时器事件里面距离触发事件最近的时间?
否则设置成NULL,无限期等待。*/
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
shortest = aeSearchNearestTimer(eventLoop);
if (shortest)
{
long now_sec, now_ms; /* Calculate the time missing for the nearest
* timer to fire. */
aeGetTime(&now_sec, &now_ms);
tvp = &tv;
tvp->tv_sec = shortest->when_sec - now_sec;
if (shortest->when_ms < now_ms)
{
tvp->tv_usec = ((shortest->when_ms+) - now_ms)*;
tvp->tv_sec --;
}
else
{
tvp->tv_usec = (shortest->when_ms - now_ms)*;
}
if (tvp->tv_sec < ) tvp->tv_sec = ;
if (tvp->tv_usec < ) tvp->tv_usec = ;
}
else
{
/* If we have to check for events but need to return
* ASAP because of AE_DONT_WAIT we need to set the timeout
* to zero */
if (flags & AE_DONT_WAIT)
{
tv.tv_sec = tv.tv_usec = ;
tvp = &tv;
}
else
{
/* Otherwise we can block */
tvp = NULL; /* wait forever */
}
} /*调用统一的事件监控接口,并处理*/
numevents = aeApiPoll(eventLoop, tvp);
for (j = ; j < numevents; j++)
{
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = ; /* note the fe->mask & mask & ... code: maybe an already processed
* event removed an element that fired and we still didn't
* processed, so we check if the event is still valid. */
if (fe->mask & mask & AE_READABLE)
{
rfired = ;
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
}
if (fe->mask & mask & AE_WRITABLE)
{
if (!rfired || fe->wfileProc != fe->rfileProc)
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
}
processed++;
}
}
/*检查处理定时器事件*/
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop); return processed; /* return the number of processed file/time events */
}
/*事件管理器总函数,没啥可说的*/
void aeMain(aeEventLoop *eventLoop)
{
eventLoop->stop = ;
while (!eventLoop->stop)
{
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}