对于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);
<!--more-->

  1. //文件事件回调函数
  2. typedef struct aeFileEvent {
  3. int mask; /* one of AE_(READABLE|WRITABLE) */ //文件事件类型 读/写
  4. aeFileProc *rfileProc;
  5. aeFileProc *wfileProc;
  6. void *clientData;
  7. } aeFileEvent;
  8. /* A fired event */
  9. typedef struct aeFiredEvent {
  10. int fd; ////已出现的事件的文件号对应的事件描述在aeEventLoop.events[]中的下标
  11. int mask; //文件事件类型 AE_WRITABLE||AE_READABLE
  12. } aeFiredEvent;
  13. typedef struct aeTimeEvent {
  14. long long id; /* time event identifier. */ //由aeEventLoop.timeEventNextId进行管理
  15. long when_sec; /* seconds */
  16. long when_ms; /* milliseconds */
  17. aeTimeProc *timeProc;
  18. aeEventFinalizerProc *finalizerProc;
  19. void *clientData;
  20. struct aeTimeEvent *next;
  21. } aeTimeEvent;
  22. /* State of an event based program */
  23. typedef struct aeEventLoop {
  24. int maxfd; //监听的最大文件号
  25. int setsize; //跟踪的文件描述符最大数量
  26. long long timeEventNextId; //定时器事件的ID编号管理(分配ID号所用)
  27. time_t lastTime; /* Used to detect system clock skew */
  28. aeFileEvent *events; //注册的文件事件,这些是需要进程关注的文件
  29. aeFiredEvent *fired; //poll结果,待处理的文件事件的文件号和事件类型
  30. aeTimeEvent *timeEventHead; //定时器时间链表
  31. int stop; //时间轮询是否结束?
  32. void *apidata; //polling API 特殊的数据
  33. aeBeforeSleepProc *beforesleep; //休眠前的程序
  34. } 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中,一个函数一个函数解析

  1. //定义了几个宏,根据不同的宏加载
  2. //不同的网络模型
  3. #ifdef HAVE_EVPORT
  4. #include "ae_evport.c"
  5. #else
  6. #ifdef HAVE_EPOLL
  7. #include "ae_epoll.c"
  8. #else
  9. #ifdef HAVE_KQUEUE
  10. #include "ae_kqueue.c"
  11. #else
  12. #include "ae_select.c"
  13. #endif
  14. #endif
  15. #endif

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

  1. aeEventLoop *aeCreateEventLoop(int setsize) {
  2. aeEventLoop *eventLoop;
  3. int i;
  4. //创建eventloop
  5. if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
  6. //为进程要注册的文件开辟空间
  7. eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
  8. //为激活的要处理的文件开辟空间
  9. eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
  10. //开辟失败报错
  11. if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
  12. //设置监听事件总数
  13. eventLoop->setsize = setsize;
  14. //更新为当前时间
  15. eventLoop->lastTime = time(NULL);
  16. eventLoop->timeEventHead = NULL;
  17. eventLoop->timeEventNextId = 0;
  18. eventLoop->stop = 0;
  19. eventLoop->maxfd = -1;
  20. eventLoop->beforesleep = NULL;
  21. //将不同模式的api注册到eventloop里
  22. if (aeApiCreate(eventLoop) == -1) goto err;
  23. /* Events with mask == AE_NONE are not set. So let's initialize the
  24. * vector with it. */
  25. for (i = 0; i < setsize; i++)
  26. //将所有文件事件类型初始为空
  27. eventLoop->events[i].mask = AE_NONE;
  28. return eventLoop;
  29. err:
  30. if (eventLoop) {
  31. zfree(eventLoop->events);
  32. zfree(eventLoop->fired);
  33. zfree(eventLoop);
  34. }
  35. return NULL;
  36. }
  1. //事件队列大小和重置
  2. //获取eventloop事件队列大小
  3. int aeGetSetSize(aeEventLoop *eventLoop) {
  4. return eventLoop->setsize;
  5. }
  6. //重新设置大小
  7. int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
  8. int i;
  9. if (setsize == eventLoop->setsize) return AE_OK;
  10. if (eventLoop->maxfd >= setsize) return AE_ERR;
  11. //不同的网络模型调用不同的resize
  12. if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
  13. //重新开辟空间
  14. eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
  15. eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
  16. eventLoop->setsize = setsize;
  17. /* Make sure that if we created new slots, they are initialized with
  18. * an AE_NONE mask. */
  19. //重新初始化事件类型
  20. for (i = eventLoop->maxfd+1; i < setsize; i++)
  21. eventLoop->events[i].mask = AE_NONE;
  22. return AE_OK;
  23. }

删除eventloop和stop事件轮询

  1. //删除eventloop结构
  2. void aeDeleteEventLoop(aeEventLoop *eventLoop) {
  3. aeApiFree(eventLoop);
  4. zfree(eventLoop->events);
  5. zfree(eventLoop->fired);
  6. zfree(eventLoop);
  7. }
  8. //设置eventloop停止标记
  9. void aeStop(aeEventLoop *eventLoop) {
  10. eventLoop->stop = 1;
  11. }

创建监听事件

  1. //创建监听事件
  2. int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
  3. aeFileProc *proc, void *clientData)
  4. {
  5. //判断fd大于eventloop设置的事件队列大小
  6. if (fd >= eventLoop->setsize) {
  7. errno = ERANGE;
  8. return AE_ERR;
  9. }
  10. //取出对应的aeFileEvent事件
  11. aeFileEvent *fe = &eventLoop->events[fd];
  12. //添加读写事件到不同的模型
  13. if (aeApiAddEvent(eventLoop, fd, mask) == -1)
  14. return AE_ERR;
  15. //文件类型按位或
  16. fe->mask |= mask;
  17. //根据最终的类型设置读写回调函数
  18. if (mask & AE_READABLE) fe->rfileProc = proc;
  19. if (mask & AE_WRITABLE) fe->wfileProc = proc;
  20. //fe中读写操作的clientdata
  21. fe->clientData = clientData;
  22. //如果fd大于当前最大的eventLoop maxfdfd
  23. if (fd > eventLoop->maxfd)
  24. eventLoop->maxfd = fd;
  25. return AE_OK;
  26. }

删除监听事件

  1. void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
  2. {
  3. if (fd >= eventLoop->setsize) return;
  4. aeFileEvent *fe = &eventLoop->events[fd];
  5. if (fe->mask == AE_NONE) return;
  6. //网络模型里删除对应的事件
  7. aeApiDelEvent(eventLoop, fd, mask);
  8. //清除对应的类型标记
  9. fe->mask = fe->mask & (~mask);
  10. //如果删除的fd是maxfd,并且对应的事件为空,那么更新maxfd
  11. if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
  12. /* Update the max fd */
  13. int j;
  14. for (j = eventLoop->maxfd-1; j >= 0; j--)
  15. if (eventLoop->events[j].mask != AE_NONE) break;
  16. eventLoop->maxfd = j;
  17. }
  18. }
  1. //获取文件类型
  2. int aeGetFileEvents(aeEventLoop *eventLoop, int fd) {
  3. if (fd >= eventLoop->setsize) return 0;
  4. aeFileEvent *fe = &eventLoop->events[fd];
  5. //返回对应的类型标记
  6. return fe->mask;
  7. }

事件派发函数

  1. //派发事件的函数
  2. int aeProcessEvents(aeEventLoop *eventLoop, int flags)
  3. {
  4. int processed = 0, numevents;
  5. /* Nothing to do? return ASAP */
  6. if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
  7. //为了休眠,直到有时间事件触发,即便是没有文件事件处理,我们也会
  8. //调用对应的事件时间
  9. //这部分不是很清楚,知道大体意思是设置时间,
  10. //为了aeApiPoll设置等待的时间
  11. if (eventLoop->maxfd != -1 ||
  12. ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
  13. int j;
  14. aeTimeEvent *shortest = NULL;
  15. struct timeval tv, *tvp;
  16. if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
  17. shortest = aeSearchNearestTimer(eventLoop);
  18. if (shortest) {
  19. long now_sec, now_ms;
  20. /* Calculate the time missing for the nearest
  21. * timer to fire. */
  22. aeGetTime(&now_sec, &now_ms);
  23. tvp = &tv;
  24. tvp->tv_sec = shortest->when_sec - now_sec;
  25. if (shortest->when_ms < now_ms) {
  26. tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
  27. tvp->tv_sec --;
  28. } else {
  29. tvp->tv_usec = (shortest->when_ms - now_ms)*1000;
  30. }
  31. if (tvp->tv_sec < 0) tvp->tv_sec = 0;
  32. if (tvp->tv_usec < 0) tvp->tv_usec = 0;
  33. } else {
  34. /* If we have to check for events but need to return
  35. * ASAP because of AE_DONT_WAIT we need to set the timeout
  36. * to zero */
  37. if (flags & AE_DONT_WAIT) {
  38. tv.tv_sec = tv.tv_usec = 0;
  39. tvp = &tv;
  40. } else {
  41. /* Otherwise we can block */
  42. tvp = NULL; /* wait forever */
  43. }
  44. }
  45. //调用不同的网络模型poll事件
  46. numevents = aeApiPoll(eventLoop, tvp);
  47. for (j = 0; j < numevents; j++) {
  48. //轮询处理就绪事件
  49. aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
  50. int mask = eventLoop->fired[j].mask;
  51. int fd = eventLoop->fired[j].fd;
  52. int rfired = 0;
  53. //可读就绪事件
  54. if (fe->mask & mask & AE_READABLE) {
  55. rfired = 1;
  56. fe->rfileProc(eventLoop,fd,fe->clientData,mask);
  57. }
  58. //可写就绪事件
  59. if (fe->mask & mask & AE_WRITABLE) {
  60. if (!rfired || fe->wfileProc != fe->rfileProc)
  61. fe->wfileProc(eventLoop,fd,fe->clientData,mask);
  62. }
  63. processed++;
  64. }
  65. }
  66. /* Check time events */
  67. //处理所有定时器事件
  68. if (flags & AE_TIME_EVENTS)
  69. processed += processTimeEvents(eventLoop);
  70. return processed; /* return the number of processed file/time events */
  71. }
  1. /等待millionseconds,直到有可读或者可写事件触发
  2. int aeWait(int fd, int mask, long long milliseconds) {
  3. struct pollfd pfd;
  4. int retmask = 0, retval;
  5. memset(&pfd, 0, sizeof(pfd));
  6. pfd.fd = fd;
  7. if (mask & AE_READABLE) pfd.events |= POLLIN;
  8. if (mask & AE_WRITABLE) pfd.events |= POLLOUT;
  9. if ((retval = poll(&pfd, 1, milliseconds))== 1) {
  10. if (pfd.revents & POLLIN) retmask |= AE_READABLE;
  11. if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
  12. if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
  13. if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
  14. return retmask;
  15. } else {
  16. return retval;
  17. }
  18. }
  1. //ae主函数
  2. void aeMain(aeEventLoop *eventLoop) {
  3. //stop初始为0
  4. eventLoop->stop = 0;
  5. while (!eventLoop->stop) {
  6. //调用beforesleep函数
  7. if (eventLoop->beforesleep != NULL)
  8. eventLoop->beforesleep(eventLoop);
  9. //派发所有的事件
  10. aeProcessEvents(eventLoop, AE_ALL_EVENTS);
  11. }
  12. }
  13. //获取api名字
  14. char *aeGetApiName(void) {
  15. return aeApiName();
  16. }
  17. //sleep之前的回调函数
  18. void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
  19. eventLoop->beforesleep = beforesleep;
  20. }

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

热门评论

热门文章

  1. Linux环境搭建和编码

    喜欢(594) 浏览(13991)
  2. Qt环境搭建

    喜欢(517) 浏览(25506)
  3. vscode搭建windows C++开发环境

    喜欢(596) 浏览(86133)
  4. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(6239)
  5. 使用hexo搭建个人博客

    喜欢(533) 浏览(12241)

最新评论

  1. 利用C11模拟伪闭包实现连接的安全回收 搁浅:看chatgpt说 直接传递 shared_from_this() 更安全 提问: socket_.async_read_some(boost::asio::buffer(data_, BUFFSIZE), // 接收客户端发生来的数据 std::bind(&Session::handle_read, this, std::placeholders::_1, std::placeholders::_2, shared_from_this())); socket_.async_read_some(boost::asio::buffer(data_, BUFFSIZE), std::bind(&Session::handle_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); 这两种方式有区别吗? 回答 : 第一种方式:this 是裸指针,可能会导致生命周期问题,虽然 shared_from_this() 提供了一定的保护,但 this 依然存在风险。 第二种方式:完全使用 shared_ptr 来管理生命周期,更加安全。 通常,第二种方式更推荐使用,因为它可以确保在异步操作完成之前,Session 对象的生命周期得到完全管理,避免使用裸指针的潜在风险。
  2. 基于锁实现线程安全队列和栈容器 secondtonone1:我是博主,你认真学习的样子的很可爱,哈哈,我画的是链表由空变成1个的情况。其余情况和你思考的类似,只不过我用了一个无效节点表示tail的指向,最初head和tail指向的都是这个节点。
  3. slice介绍和使用 恋恋风辰:切片作为引用类型极大的提高了数据传递的效率和性能,但也要注意切片的浅拷贝隐患,算是一把双刃剑,这世间的常态就是在两极之间寻求一种稳定。
  4. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  5. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  6. 创建项目和编译 secondtonone1:谢谢支持
  7. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  8. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  9. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW
  10. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  11. 类和对象 陈宇航:支持!!!!
  12. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!
  13. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  14. 聊天项目(13) 重置密码功能 Doraemon:万一一个用户多个邮箱呢 有可能的
  15. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好
  16. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  17. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里
  18. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  19. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  20. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  21. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  22. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  23. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  24. C++ 线程安全的单例模式演变 183******95:单例模式的析构函数何时运行呢? 实际测试里:无论单例模式的析构函数为私有或公有,使用智能指针和辅助回收类,两种方法都无法在main()结束前调用单例的析构函数。
  25. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  26. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  27. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  28. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o

个人公众号

个人微信