上一篇讲完了initServer的大体流程,其中aeCreateEventLoop(),这个函数

没有详细说明,我们在这一篇里讲述Ae.h和Ae.c, 这里面的api阐述了如何创建

eventLoop和添加文件读写事件等等。

ae.h中的解释

//文件读写事件回调函数
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask); //定时器回调函数
typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
//事件结束回调函数,析构一些资源
typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
//不是很清楚,应该是进程结束前做的回调函数
typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop); //文件事件回调函数
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) */ //文件事件类型 读/写
aeFileProc *rfileProc;
aeFileProc *wfileProc;
void *clientData;
} aeFileEvent; /* A fired event */
typedef struct aeFiredEvent {
int fd; ////已出现的事件的文件号对应的事件描述在aeEventLoop.events[]中的下标
int mask; //文件事件类型 AE_WRITABLE||AE_READABLE
} aeFiredEvent; typedef struct aeTimeEvent {
long long id; /* time event identifier. */ //由aeEventLoop.timeEventNextId进行管理
long when_sec; /* seconds */
long when_ms; /* milliseconds */
aeTimeProc *timeProc;
aeEventFinalizerProc *finalizerProc;
void *clientData;
struct aeTimeEvent *next;
} aeTimeEvent; /* State of an event based program */
typedef struct aeEventLoop {
int maxfd; //监听的最大文件号
int setsize; //跟踪的文件描述符最大数量
long long timeEventNextId; //定时器事件的ID编号管理(分配ID号所用)
time_t lastTime; /* Used to detect system clock skew */
aeFileEvent *events; //注册的文件事件,这些是需要进程关注的文件
aeFiredEvent *fired; //poll结果,待处理的文件事件的文件号和事件类型
aeTimeEvent *timeEventHead; //定时器时间链表
int stop; //时间轮询是否结束?
void *apidata; //polling API 特殊的数据
aeBeforeSleepProc *beforesleep; //休眠前的程序
} aeEventLoop; /* Prototypes */
//创建eventLoop结构
aeEventLoop *aeCreateEventLoop(int setsize);
//删除eventloop
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);
//获取文件事件对应类型(读或写)
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);
//等待millionseconds直到文件描述符可读或者可写
int aeWait(int fd, int mask, long long milliseconds);
//ae事件轮询主函数
void aeMain(aeEventLoop *eventLoop);
//获取当前网络模型
char *aeGetApiName(void);
//进程休眠前回调函数
void aeSetBeforeSleepProc(aeEventLoop *eventLoop,
aeBeforeSleepProc *beforesleep);
//获取eventloop所有的事件个数
int aeGetSetSize(aeEventLoop *eventLoop);
//重新设置eventloop事件个数
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);

ae.cpp中,一个函数一个函数解析

//定义了几个宏,根据不同的宏加载
//不同的网络模型
#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

aeCreateEventLoop,主要负责eventloop结构的创建和初始化,以及模型的初始化

aeEventLoop *aeCreateEventLoop(int setsize) {
aeEventLoop *eventLoop;
int i;
//创建eventloop
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;
//将不同模式的api注册到eventloop里
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;
return eventLoop; err:
if (eventLoop) {
zfree(eventLoop->events);
zfree(eventLoop->fired);
zfree(eventLoop);
}
return NULL;
}

//事件队列大小和重置

//获取eventloop事件队列大小
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;
//不同的网络模型调用不同的resize
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; /* Make sure that if we created new slots, they are initialized with
* an AE_NONE mask. */
//重新初始化事件类型
for (i = eventLoop->maxfd+; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return AE_OK;
}

删除eventloop和stop事件轮询

//删除eventloop结构
void aeDeleteEventLoop(aeEventLoop *eventLoop) {
aeApiFree(eventLoop);
zfree(eventLoop->events);
zfree(eventLoop->fired);
zfree(eventLoop);
} //设置eventloop停止标记
void aeStop(aeEventLoop *eventLoop) {
eventLoop->stop = ;
}

创建监听事件

//创建监听事件
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
//判断fd大于eventloop设置的事件队列大小
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
} //取出对应的aeFileEvent事件
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
fe->clientData = clientData;
//如果fd大于当前最大的eventLoop maxfdfd
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);
//如果删除的fd是maxfd,并且对应的事件为空,那么更新maxfd
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;
}

事件派发函数

//派发事件的函数
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
int processed = , numevents; /* Nothing to do? return ASAP */
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return ; //为了休眠,直到有时间事件触发,即便是没有文件事件处理,我们也会
//调用对应的事件时间
//这部分不是很清楚,知道大体意思是设置时间,
//为了aeApiPoll设置等待的时间
if (eventLoop->maxfd != - ||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j;
aeTimeEvent *shortest = NULL;
struct timeval tv, *tvp; 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 */
}
}
//调用不同的网络模型poll事件
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 = ;
//可读就绪事件
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++;
}
}
/* Check time events */
//处理所有定时器事件
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop); return processed; /* return the number of processed file/time events */
}
/等待millionseconds,直到有可读或者可写事件触发
int aeWait(int fd, int mask, long long milliseconds) {
struct pollfd pfd;
int retmask = , retval; memset(&pfd, , sizeof(pfd));
pfd.fd = fd;
if (mask & AE_READABLE) pfd.events |= POLLIN;
if (mask & AE_WRITABLE) pfd.events |= POLLOUT; if ((retval = poll(&pfd, , milliseconds))== ) {
if (pfd.revents & POLLIN) retmask |= AE_READABLE;
if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
return retmask;
} else {
return retval;
}
}
//ae主函数
void aeMain(aeEventLoop *eventLoop) {
//stop初始为0
eventLoop->stop = ;
while (!eventLoop->stop) {
//调用beforesleep函数
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
//派发所有的事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
} //获取api名字
char *aeGetApiName(void) {
return aeApiName();
} //sleep之前的回调函数
void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
eventLoop->beforesleep = beforesleep;
}

这就是ae文件里大体的几个api,其他的没理解的还在研究。

我的微信公众号:

对于redis框架的理解(三)的更多相关文章

  1. 对于redis框架的理解(四)

    上一篇讲述了eventloop的结构和创建,添加文件事件删除文件事件,派发等等. 而eventloop主要就是调用不同网络模型完成事件监听和派发的. 这一篇主要讲述epoll网络模型,redis是如何 ...

  2. 对于redis框架的理解(二)

    之前梳理过redis main函数主体流程 大体是 initServerConfig() -> loadServerConfig() -> daemonize() -> initSe ...

  3. redis 单线程的理解

    单线程模型 Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程.其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会 ...

  4. iOS10通知框架UserNotification理解与应用

    iOS10通知框架UserNotification理解与应用 一.引言 关于通知,无论与远程Push还是本地通知,以往的iOS系统暴漏给开发者的接口都是十分有限的,开发者只能对标题和内容进行简单的定义 ...

  5. redis之(二十一)redis之深入理解Spring Redis的使用

    关于spring redis框架的使用,网上的例子很多很多.但是在自己最近一段时间的使用中,发现这些教程都是入门教程,包括很多的使用方法,与spring redis丰富的api大相径庭,真是浪费了这么 ...

  6. Nginx Http框架的理解

    Nginx Http框架的理解 HTTP框架是Nginx基础框架的一部分,Nginx的其它底层框架如master-worker进程模型.event模块.mail 模块等. HTTP框架代码主要有2个模 ...

  7. Redis 小白指南(三)- 事务、过期、消息通知、管道和优化内存空间

    Redis 小白指南(三)- 事务.过期.消息通知.管道和优化内存空间 简介 <Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍> 讲的是 Redis 的介绍,以及如何 ...

  8. Hadoop框架基础(三)

    ** Hadoop框架基础(三) 上一节我们使用eclipse运行展示了hdfs系统中的某个文件数据,这一节我们简析一下离线计算框架MapReduce,以及通过eclipse来编写关于MapReduc ...

  9. C#使用Thrift作为RPC框架入门(三)之三层架构

    前言 这是我们讲解Thrift框架的第三篇文章,前两篇我们讲了Thrift作为RPC框架的基本用法以及架构的设计.为了我们更好的使用和理解Thrift框架,接下来,我们将来学习一下Thrift框架提供 ...

随机推荐

  1. VLP16线用户手册.md

    VLP16线用户手册 文档  传感器数据 分组类型和定义 传感器产生两种类型的数据包:数据包和位置数据包.位置包有时也被称为遥测包或GPS包. 数据包包括传感器测量到的三维数据以及返回光脉冲的表面的校 ...

  2. PRML学习笔记第一章

    [转] PRML笔记 - 1.1介绍 模式识别的目标 自动从数据中发现潜在规律,以利用这些规律做后续操作,如数据分类等. 模型选择和参数调节 类似的一族规律通常可以以一种模型的形式为表达,选择合适模型 ...

  3. 算法笔记(c++)--桶排序题目

    算法笔记(c++)--桶排序 记得题目是排序,输入n个1-1000的数字然后去重然后排序. 桶排序没毛病 #include<iostream> using namespace std; i ...

  4. spring-boot Jpa配置

    spring.jpa.hibernate.ddl-auto ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空 ddl-auto:create-drop---- ...

  5. 亮眼的购物季数据,高涨的 Amazon Prime

    依照往年的惯例,亚马逊公布了 2013 购物季的销售数据.据 The Verge 的报道,今年,仅仅网购星期一(Cyber Monday)一天就在全球范围内销售出 3680 万件商品,而去年这一数字为 ...

  6. USACO 1.3.2 Barn Repair 修理牛棚(贪心)

    Description 在一个夜黑风高,下着暴风雨的夜晚,农民约翰的牛棚的屋顶.门被吹飞了. 好在许多牛正在度假,所以牛棚没有住满. 剩下的牛一个紧挨着另一个被排成一行来过夜. 有些牛棚里有牛,有些没 ...

  7. OOP 学习笔记汇总

    1.1 引用 1.2 const关键字 1.3 动态内存分配 1.4 内联函数和重载函数函数参数缺省值 1.5 类和对象的基本概念与用法1 2.1 类和对象的基本概念2

  8. ACM-ICPC 2018 沈阳赛区网络预赛

    Supreme Number 1000ms 131072K   A prime number (or a prime) is a natural number greater than 111 tha ...

  9. lintcode-496-玩具工厂

    496-玩具工厂 工厂模式是一种常见的设计模式.请实现一个玩具工厂 ToyFactory 用来产生不同的玩具类.可以假设只有猫和狗两种玩具. 您在真实的面试中是否遇到过这个题? Yes 样例 ToyF ...

  10. lintcode-389-判断数独是否合法

    389-判断数独是否合法 请判定一个数独是否有效. 该数独可能只填充了部分数字,其中缺少的数字用 . 表示. 注意事项 一个合法的数独(仅部分填充)并不一定是可解的.我们仅需使填充的空格有效即可. 说 ...