event_base是libevent的事件驱动,也是Reactor模式的直接体现。任何使用libevent的代码最开始都需要创建一个base,之后的任何接口函数都和这个base关联着,下面是struct event_base的定义


struct event_base {/* io多路复用函数的统一接口 */const struct eventop *evsel;/* io多路复用函数的数据 */void *evbase;/* 信号处理的统一接口 */const struct eventop *evsigsel;/* 信号处理的数据 */struct evsig_info sig;/* 注册到base中的event,不包括内部event */int event_count;/* 激活的event个数 */int event_count_active;/* 存储套接字/描述符以及对应的事件的map */struct event_io_map io;/* 存储信号的map */struct event_signal_map sigmap;/* 注册队列 */struct event_list eventqueue;/* 激活队列 */struct event_list *activequeues;/* 最小堆 */struct min_heap timeheap;/* ... */
};

struct event_base中主要包括上述变量,io多路复用操作,信号操作,各种map,队列,最小堆等等
其他的部分就是在程序设计过程中进行的各种判断,各种标志多一些
event_base的初始化是通过内部调用event_base_with_new_config实现的,函数意思是带有一些配置构造一个event_base,配置主要包括的就是用户不想要libevent使用的io复用函数名字。
函数中主要还是各种初始化,然后选择合适的io复用函数等


用户通过event_base_dispatch函数开启事件驱动的主循环,内部调用event_base_loop执行无线循环,也没有什么特别的

  • 选择io复用函数的阻塞时长
  • 调用io函数
  • 判断最小堆中的event
  • 处理所有激活的event

下面主要看一下如何处理激活event

/** 由base主循环调用,用来处理在base的激活队列中的event,* 根据优先级遍历base的激活队列数组,对于每一个队列,调用event_process_active_single_queue处理*/
static int
event_process_active(struct event_base *base)
{/* Caller must hold th_base_lock */struct event_list *activeq = NULL;int i, c = 0;for (i = 0; i < base->nactivequeues; ++i) {if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {base->event_running_priority = i;activeq = &base->activequeues[i];c = event_process_active_single_queue(base, activeq);}}return c;
}
/** 处理激活/超时event的主要函数* 首先根据event是否是永久event判断是否需要将event从base的所有队列中删除* 如果是永久队列,则只需要从base的激活队列中删除* 然后才开始调用event的回调函数** 注意:此处需要处理具有超时时间的event,因为最小堆中记录的是绝对时间,* 所以如果这个event是具有超时时间的event,那么它是在从base中所有队列中删除后才加入到* 激活队列中的,见timeout_process()* 所以需要再将其添加到base中,重新调用event_add_internal即可,见event_persist_closure* * 返回处理了多少个非内部event*/
static int
event_process_active_single_queue(struct event_base *base,struct event_list *activeq)
{struct event *ev;int count = 0;for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {/* 如果是永久event,只需要从激活队列中删除 */if (ev->ev_events & EV_PERSIST)event_queue_remove(base, ev, EVLIST_ACTIVE);/* 否则就要从base的所有队列中删除,包括激活队列, 因为只处理一次 */elseevent_del_internal(ev);/* * 如果是信号或者永久event需要单独处理,而其他event在删除完之后直接调用回调函数就可以了* 以后就不再管这个event了,因为只处理一次* ev_closure用于判断是否是永久/信号event,在event_new中赋值*/switch (ev->ev_closure) {case EV_CLOSURE_SIGNAL:/* 信号的特殊处理,调用用户的信号处理函数 */event_signal_closure(base, ev);break;case EV_CLOSURE_PERSIST://io event/* 处理io事件,主要是特别处理有超时时间的event,需要重新计算绝对时间,然后调用回调函数 */event_persist_closure(base, ev);break;}}return count;
}

函数处理当前队列中的每一个event,首先将其从激活队列中删除,如果是一次性事件,则从base中删除
然后就根据信号/其他永久io事件调用相应的处理函数

总结
event_base是整个事件驱动的载体,但是使用时仅仅需要event_base_new,和event_base_dispatch即可,内部实现了对事件的监控,对超时event的统一处理,对所有激活event按优先级处理,处理时再细分是永久/一次性event,io/signal event等。这种先统一再分散的好处是可以在一个函数中统一处理激活event,实现更好的封装接口,也更容易理清思路。
对信号的处理需要特别注意,因为不是一有信号发生马上调用用户的回调函数,中间其实进行了两次base循环,这是为了将信号统一到event的结果,不然就直接sigaction绑定不是很好?
具有超时时间的event在处理时也是需要注意的部分,需要重新计算超时时间,然后使用event_add_internal重新添加(event在timeout_process已经被删除了),这就回到了event_add调用的event_add_internal上,如果不是超时event就仅仅是调用回调函数而已。
整体思路很容易理清,重点是细节方面,需要仔细琢磨

libevent源码学习-----event_base事件循环相关推荐

  1. Libevent 源码学习笔记(1)event 与 event_base

    目录 event event_base eventop evcb_closure event_callback event_changelist evsig_info event_io_map eve ...

  2. libevent源码学习-----event操作

    libevent核心结构是event_base和event,接下来主要介绍event结构 /* event的定义的主要部分 */ struct event {/* ... *//* event监听的描 ...

  3. libevent源码学习-----阅读心得

    框架设计思路 libevent使用统一事件源将所有问题都转化为event,比如将套接字/信号/描述符都在内部转化为event,由相应的io多路复用函数进行监控. 为了提供对超时event的支持,lib ...

  4. libevent源码学习-----时间管理

    libevent监听的event有以下几种 文件描述符/套接字,没有设定超时时长 信号 文件描述符/套接字,设定超时时长 对于时间,libevent内部的时间管理是通过最小堆实现的,原因如下 既然某些 ...

  5. libevent源码学习----io多路复用的封装和使用

    因为是非阻塞监听事件的发生,所以内部其实还是采用io多路复用函数实现的. 又因为可供选择的io函数很多,linux下有epoll, poll, select等,window下有ICOP, select ...

  6. libevent源码学习-----事件驱动流程分析

    libevent中事件驱动的大体流程如下 /* 创建事件驱动 */ struct event_base* base = event_base_new(); /**创建一个事件*@param base: ...

  7. Libevent源码学习笔记一:event2/event.h

    一.libevent标准使用方法: 每个程序使用Libevent必须include <event2/event.h> 头文件,并 传给 -levent  链接器.如果只是想使用主要的eve ...

  8. libevent源码学习-----Reactor模型

    libevent内部采用了reactor模型 所谓reactor模型,其实就是一套事件注册机制,用来解决单线程的阻塞问题.reactor核心思想是将事件和相应事件发生时想要调用的函数都记录下来,在事件 ...

  9. libevent源码学习-----统一事件源及信号绑定函数

    libevent在对文件描述符,套接字进行监控时直接放到event,这些event通过io多路复用函数进行监控,然而对应信号来说io复用函数却无能为力,为了解决问题,libevent采用统一事件源的方 ...

最新文章

  1. Windows Server 2008 R2 如何启动内核调试
  2. ext3文件系统反删除利器ext3grep应用实战
  3. outlook邮件中图片无显示解决方法
  4. lvs+keepalived详解
  5. Java线程并发常用工具类使用
  6. 笨办法学 Python · 续 练习 27:`tr`
  7. android仿新浪引导界面
  8. 【bozj2287】【[POJ Challenge]消失之物】维护多值递推
  9. oracle索引实验报告,Oracle之索引(Index)实例讲解
  10. 五子棋人机交互c语言代码,Windows人机交互程序设计教学课件-第10课 五子棋程序.ppt...
  11. win7系统移动磁盘合并卷
  12. Tableau 南丁格尔玫瑰图
  13. iPad谷歌浏览器怎么开摄像头_谷歌浏览器网页截图的步骤_谷歌浏览器怎么截图...
  14. 自媒体账号如何注册申请
  15. 《21天学通HTML+CSS+JavaScript Web开发(第7版)》——2.8 作业
  16. matlab 简单算例,(简单算例)基于Matlab的电力系统潮流编程计算.pdf
  17. 初识c语言—学习笔记
  18. 又一个阿里离职的 P10 大佬
  19. 使用TCPDF插件生成pdf以及pdf的中文处理
  20. 从单块架构到分布式架构之数据库集群(三)

热门文章

  1. mysql load data into_MySQL 之 LOAD DATA INFILE 快速导入数据
  2. C语言学习之有一个分数序列2/1、3/2、5/3、8/5、13/8、21/13,……求出这个数列的前20项之和。
  3. tomcat的三种部署方式
  4. Android动态权限申请
  5. SQLserver查看某个视图的创建语句
  6. 20155210 Exp5 MSF基础应用
  7. 你对一个程序员有多尊重
  8. 22 Notification 通知栏代码
  9. [转]Github 中被 Fork 最多的库
  10. Python 代码混淆和加密技术