对于redis框架的理解(三)
上一篇讲完了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框架的理解(三)的更多相关文章
- 对于redis框架的理解(四)
上一篇讲述了eventloop的结构和创建,添加文件事件删除文件事件,派发等等. 而eventloop主要就是调用不同网络模型完成事件监听和派发的. 这一篇主要讲述epoll网络模型,redis是如何 ...
- 对于redis框架的理解(二)
之前梳理过redis main函数主体流程 大体是 initServerConfig() -> loadServerConfig() -> daemonize() -> initSe ...
- redis 单线程的理解
单线程模型 Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程.其中执行命令阶段,由于Redis是单线程来处理命令的,所有每一条到达服务端的命令不会立刻执行,所有的命令都会 ...
- iOS10通知框架UserNotification理解与应用
iOS10通知框架UserNotification理解与应用 一.引言 关于通知,无论与远程Push还是本地通知,以往的iOS系统暴漏给开发者的接口都是十分有限的,开发者只能对标题和内容进行简单的定义 ...
- redis之(二十一)redis之深入理解Spring Redis的使用
关于spring redis框架的使用,网上的例子很多很多.但是在自己最近一段时间的使用中,发现这些教程都是入门教程,包括很多的使用方法,与spring redis丰富的api大相径庭,真是浪费了这么 ...
- Nginx Http框架的理解
Nginx Http框架的理解 HTTP框架是Nginx基础框架的一部分,Nginx的其它底层框架如master-worker进程模型.event模块.mail 模块等. HTTP框架代码主要有2个模 ...
- Redis 小白指南(三)- 事务、过期、消息通知、管道和优化内存空间
Redis 小白指南(三)- 事务.过期.消息通知.管道和优化内存空间 简介 <Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍> 讲的是 Redis 的介绍,以及如何 ...
- Hadoop框架基础(三)
** Hadoop框架基础(三) 上一节我们使用eclipse运行展示了hdfs系统中的某个文件数据,这一节我们简析一下离线计算框架MapReduce,以及通过eclipse来编写关于MapReduc ...
- C#使用Thrift作为RPC框架入门(三)之三层架构
前言 这是我们讲解Thrift框架的第三篇文章,前两篇我们讲了Thrift作为RPC框架的基本用法以及架构的设计.为了我们更好的使用和理解Thrift框架,接下来,我们将来学习一下Thrift框架提供 ...
随机推荐
- VLP16线用户手册.md
VLP16线用户手册 文档 传感器数据 分组类型和定义 传感器产生两种类型的数据包:数据包和位置数据包.位置包有时也被称为遥测包或GPS包. 数据包包括传感器测量到的三维数据以及返回光脉冲的表面的校 ...
- PRML学习笔记第一章
[转] PRML笔记 - 1.1介绍 模式识别的目标 自动从数据中发现潜在规律,以利用这些规律做后续操作,如数据分类等. 模型选择和参数调节 类似的一族规律通常可以以一种模型的形式为表达,选择合适模型 ...
- 算法笔记(c++)--桶排序题目
算法笔记(c++)--桶排序 记得题目是排序,输入n个1-1000的数字然后去重然后排序. 桶排序没毛病 #include<iostream> using namespace std; i ...
- spring-boot Jpa配置
spring.jpa.hibernate.ddl-auto ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空 ddl-auto:create-drop---- ...
- 亮眼的购物季数据,高涨的 Amazon Prime
依照往年的惯例,亚马逊公布了 2013 购物季的销售数据.据 The Verge 的报道,今年,仅仅网购星期一(Cyber Monday)一天就在全球范围内销售出 3680 万件商品,而去年这一数字为 ...
- USACO 1.3.2 Barn Repair 修理牛棚(贪心)
Description 在一个夜黑风高,下着暴风雨的夜晚,农民约翰的牛棚的屋顶.门被吹飞了. 好在许多牛正在度假,所以牛棚没有住满. 剩下的牛一个紧挨着另一个被排成一行来过夜. 有些牛棚里有牛,有些没 ...
- OOP 学习笔记汇总
1.1 引用 1.2 const关键字 1.3 动态内存分配 1.4 内联函数和重载函数函数参数缺省值 1.5 类和对象的基本概念与用法1 2.1 类和对象的基本概念2
- ACM-ICPC 2018 沈阳赛区网络预赛
Supreme Number 1000ms 131072K A prime number (or a prime) is a natural number greater than 111 tha ...
- lintcode-496-玩具工厂
496-玩具工厂 工厂模式是一种常见的设计模式.请实现一个玩具工厂 ToyFactory 用来产生不同的玩具类.可以假设只有猫和狗两种玩具. 您在真实的面试中是否遇到过这个题? Yes 样例 ToyF ...
- lintcode-389-判断数独是否合法
389-判断数独是否合法 请判定一个数独是否有效. 该数独可能只填充了部分数字,其中缺少的数字用 . 表示. 注意事项 一个合法的数独(仅部分填充)并不一定是可解的.我们仅需使填充的空格有效即可. 说 ...