libevent源码分析一--io事件响应
这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应。这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应。
1. select
libevent实现io事件的backend实际上使用的是io复用接口,如select, poll, epoll等,这里以最简单的select为例进行说明。首先简单介绍一下select接口:
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
readfds, writefds, exceptfds均是文件描述符集合指针,调用该函数后,若readfds集合中的fd可读,或者writefds集合中的fd可写,或者exceptfds集合中的fd发生错误,或者阻塞的时间达到了timeout,函数返回。
函数返回0,返回结果readfds集合中包含了入参readfds中现在读不会被阻塞的fd,返回结果writefds包含了对应的写不会被阻塞的fd,exceptfds包含了所有发生异常的fd。如果超时,函数返回-1,这些集合为空。
可以看到,select调用需要传入感兴趣的io的文件描述符fd,而libevent中大家熟悉的是event_base,event结构体,event添加到event_base后就可以等待事件触发了,libevent是如何关联event, event_base与select的呢?
事实上,在libevent的源码select.c的,定义了一个数据结构struct selectop,
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
int resize_out_sets;
fd_set *event_readset_in;
fd_set *event_writeset_in;
fd_set *event_readset_out;
fd_set *event_writeset_out;
};
它保存了libevent每次调用select需要使用到的入参与出参,这里保存的文件描述符是与加入到event_base中的event对应的。每当一个新的io的event加入到event_base, 内部都会将event对应的文件描述符fd加入到event_readset_in与event_writeset_in中,这取决于event感兴趣的是读事件还是写事件。相应的函数调用栈是:
event_add->event_add_nolock_->evmap_io_add_->select_add
当libevent使用select作为io的backend时,event_base中的成员evbase即指向动态分配的结构selectop,那么所有感兴趣的io事件的文件描述符都已经保存在了属于event_base的selectop结构中,调用select时即可使用。
2. event_io_map
前面分析了select使用的入参如何保存,但除了事件对应的文件描述符,libevent同样需要保存结构event,因为event中还记录了许多其它的信息,如事件发生时调用的回调函数,事件的超时时间等。因此,加入到event_base中的event结构也需要保存,event_base中的成员struct event_io_map io的作用正是用来保存添加的io事件的event结构。
struct event_io_map是一个hash表,如果是在非windows环境下,这个hash表可以简单地以一个动态数组实现。为描述简单,这里假设是在非windows环境下。其定义如下:
struct event_signal_map {
/* An array of evmap_io * or of evmap_signal *; empty entries are
* set to NULL. */
void **entries;
/* The number of entries available in entries */
int nentries;
};
它包含一个动态数组entries,数组长度以nentries表示。动态数组的内容是指向动态分配的结构evmap_io的指针。event对应的文件描述符fd作为它在动态数组中的索引,而同一个fd可能有多个感兴趣的事件加入到同一个event_base中,因此将它们连接起来构成双向链表以解决冲突,这个双向链表就是struct evmap_io,即以event的fd作为索引即可以找到这些事件组成的双向链表。event结构中的成员ev_io包含两个指针即双向链表的前驱指针与后继指针。event_base中的成员io的存储结构可以用图2-1表示,

图2-1 event_io_map
3. event_base_loop
包含回调信息的event结构已经存入了event_base的io成员,对于select,event相应的文件描述符也已经保存在了event_base的evbase成员指向的struct selectop结构中。那么libevent最终是如何等待读写事件的发生并最终调用相应的回调函数的呢?答案是event_base_loop函数。
首先,针对io事件,event_base_loop每一次循环,都会调用后端的dispatch函数,针对select后端,这个dispatch函数是select_dispatch,而select_dispatch又会调用select函数。然后,当select返回结果后,select_dispatch根据文件描述符从event_base的hash表io中将触发的event的回调函数加入到event_base的待执行回调函数链表,这个待执行回调函数链表由event_base的成员struct evcallback_list *activequeues保存。最后,event_base_loop在dispatch之后即会执行链表上的回调函数,完成事件响应。
4. activequeues
activequeues是一个evcallback_list类型的动态数组,用来实现事件的优先级,数组每一个成员都是一个待执行回调函数的链表。event_base_loop中执行这些链表上的函数时,以索引0开始按递增的顺序扫描数组,若数组成员指向的链表不为空,则依次执行上面的回调函数,索引越小,对应链表上的回调函数越先被执行,构成了事件的优先级。activequeues的结构可使用图4-1表示,

图4-1 event_base的activequeues成员结构
libevent处理io事件的流程简化总结为:首先将io事件的event结构加入到event_base的io成员中,并将对应的fd保存到evbase指向的结构中;然后event_base_loop调用dispatch,将触发的事件的回调函数添加到activequeues多优先级链表上;最后event_base_loop中执行activequeues上的回调函数,完成事件响应。
5. 部分概念说明表
|
event_base |
libevent中的基本结构,所有的event添加到该结构的实例中,再调用event_base_loop处理其中的事件 |
|
event |
libevent中表达一个事件的结构,包含了文件描述符,回调函数等信息 |
|
struct selectop |
select后端的结构,存储select函数需要的参数信息的结构,event_base中使用evbase指向这些信息 |
|
select.c |
libevent中实现select后端的源码文件 |
|
struct event_io_map |
event_base中io成员的类型,用来存储io类型的event结构的hash表 |
|
struct evmap_io |
双向链表描述结构,用于io类型的event |
|
activequeues |
event_base结构中的成员,evcallback_list类型的动态数组,保存待执行的回调函数的链表。 |
select后端的定义在select.c源码文件中
event/event_callback结构定义在源码文件event_struct.h中
event_base结构定义在源码文件event-internal.h中
event_add/event_base_loop/event_add_nolock函数定义在event.c函数中
evmap_io_add_函数与evmap_io/event_io_map结构定义在evmap.c文件中
libevent源码分析一--io事件响应的更多相关文章
- libevent源码分析二--timeout事件响应
libevent不仅支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件. 1. min_heap ...
- libevent源码分析三--signal事件响应
libevent支持io事件,timeout事件,signal事件,这篇文件将分析libevent是如何组织signal事件,以及如何实现signal事件响应的. 1. sigmap 类似于io事件 ...
- Libevent源码分析 (1) hello-world
Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...
- 【转】libevent源码分析
libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...
- [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)
一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...
- Vue.js 源码分析(四) 基础篇 响应式原理 data属性
官网对data属性的介绍如下: 意思就是:data保存着Vue实例里用到的数据,Vue会修改data里的每个属性的访问控制器属性,当访问每个属性时会访问对应的get方法,修改属性时会执行对应的set方 ...
- UiAutomator源码分析之注入事件
上一篇文章<UiAutomator源码分析之UiAutomatorBridge框架>中我们把UiAutomatorBridge以及它相关的类进行的描述,往下我们会尝试根据两个实例将这些类给 ...
- [置顶] Android源码分析-点击事件派发机制
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17339857 概述 一直想写篇关于Android事件派发机制的文章,却一直没 ...
- Libevent源码分析—event, event_base
event和event_base是libevent的两个核心结构体,分别是反应堆模式中的Event和Reactor.源码分别位于event.h和event-internal.h中 1.event: s ...
随机推荐
- Android 系统Action大全
常用Action说明: String ADD_SHORTCUT_ACTION 动作:在系统中添加一个快捷方式.. “android.intent.action.ADD_SHORTCUT” String ...
- OJ笔记
1.未考虑程序没有输出导致的格式错误: 原代码:(即使没有输出,ans集合元素为0,也输出了空格) set<int>::iterator it=ans.begin(); while(it! ...
- request登录案例
一.案例需求 1.编写login.html登录页面 username & password 两个输入框 2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表 3.使 ...
- IIS服务器部署web应用《一》
最近了解到开发用iis部署环境,于是了解了下. IIS用于部署web应用,其简单,配置方便,可以用作本地机器作为服务器进行部署.且所在部署系统为windows,便于使用iis. 端口80需要修改为别的 ...
- ubuntu14.04 安装python3.7
下载源码:https://www.python.org/downloads/release/python-373/ 一.先安装需要的包zlib1g,libffisudo apt-get update ...
- 如何在Docker容器之间拷贝数据
[编者的话]在容器之间拷贝数据是Docker一个重要而且基本的功能.拷贝数据到其他容器是一个经常使用到的场景,如当服务器遇到不可预见的“灾难”(注:断电,宕机)时,起到备份数据的作用.本文作者详细介绍 ...
- Spring系列:下载
Spring 官网:http://projects.spring.io/spring-framework/ Spring下载地址:https://repo.spring.io/simple/libs- ...
- 第08组 Beta冲刺(2/4)
队名 八组评分了吗 组长博客链接(2分) 组员1李昕晖(组长) 过去两天完成了哪些任务 文字/口头描述 12月9号了解各个小组的进度与难以攻破的地方,晚上安排开会,安排新的冲刺任务. 重新分配小组及个 ...
- SpringBoot——Profile多环境支持
1.多profile文件形式 主配置文件编写时, 文件名可以是application-{profile}.properties/yml 默认使用的application.properties的配置. ...
- docker 镜像制作
# wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-5.3.1-Linux-x86_64.sh# bash A ...