在libevent中最重要的结构体莫过于event和event_base了,下面对于这2个结构体进行分析。

1、结构体event,位于:event.h

 struct event {
/*
* 双向链表节点指针
* 是libevent对不同事件类型和在不同的时期 对事件的管理时使用到的字段
*/
/*增加下一个事件*/
TAILQ_ENTRY (event) ev_next;
/*增加下一个活动事件*/
TAILQ_ENTRY (event) ev_active_next;
/*增加下一个信号*/
TAILQ_ENTRY (event) ev_signal_next; /*
* timeout事件 表示该event保存在min_heap数组中的索引
*/
/* for managing timeouts */
unsigned int min_heap_idx; /*
* 该事件所属的反应堆实例
* 一个全局变量,管理着事件,如事件总数,事件列表
*/
struct event_base *ev_base; /*
* 对于I/O事件,是绑定的文件描述符
* 对于signal事件,是绑定的信号
*/
int ev_fd; /*
* event关注的事件类型,它可以是以下3种类型:
* I/O事件:EV_WRITE和EV_READ
* 定时事件:EV_TIMEOUT
* 信号:EV_SIGNAL
* 辅助选项:EV_PERSIST,表明是一个永久事件
*/
short ev_events; /*
* 事件就绪执行时,调用ev_callback的次数,通常为1
*/
short ev_ncalls; /*
* 指针,通常指向ev_ncalls或者为NULL
*/
/* Allows deletes in callback */
short *ev_pncalls; /*
* timeout事件 超时值
*/
struct timeval ev_timeout; /*
* 优先级,越小越高
*/
/* smaller numbers are higher priority */
int ev_pri; /*
* event的回调函数,被ev_base调用,执行事件处理程序
* 各参数说明如下:
* 参数1: ev_fd,表示触发事件的文件句柄,比如一个socket对象
* 参数2: ev_events,表示事件发生的结果,可能是超时、可读、可写
* 参数3: ev_arg,表示传递给事件的参数
*/
void (*ev_callback)(int, short, void *arg); /*
* 回调函数的参数
*/
void *ev_arg; /*
* 记录了当前激活事件的类型
*/
/* result passed to event callback */
int ev_res; /*
* libevent用于标记event信息的字段,表明其当前的状态
*/
int ev_flags;
};

2、结构体event_base,位于:event_internal.h

 struct event_base {
/*
* 表示选择的事件引擎,可能为: epoll, poll, select
*/
const struct eventop *evsel; /*
* 全局对象,evbase实际上是一个eventop实例对象,执行具体任务
* 在函数event_base_new()中被初始化base->evbase = base->evsel->init(base)
*/
void *evbase; /* counts number of total events */
int event_count; /* counts number of active events */
int event_count_active; /* Set to terminate loop */
int event_gotterm; /* Set to terminate loop immediately */
int event_break; /*
* libevent支持事件优先级,因此可以把它看作是数组
* 其中的元素activequeues[priority]是一个链表
* 链表的每个节点指向一个优先级为priority的就绪事件event
*/
/* active event management */
struct event_list **activequeues; int nactivequeues; /*
* 用来管理信号的结构体
*/
/* signal handling info */
struct evsignal_info sig; /*
* 链表,保存了所有的注册事件event的指针
*/
struct event_list eventqueue; /*
* 系统的当前时间
*/
struct timeval event_tv; /*
* 管理定时事件的小根堆
*/
struct min_heap timeheap; /*
* 与event::ev_timeout进行比较,确定事件是否超时
*/
struct timeval tv_cache;
};

3、结构体eventop,位于:event_internal.h

 struct eventop {
const char *name; /*
* 初始化
*/
void *(*init)(struct event_base *); /*
* 注册事件
*/
int (*add)(void *, struct event *); /*
* 删除事件
*/
int (*del)(void *, struct event *); /*
* 事件分发
*/
int (*dispatch)(struct event_base *, void *, struct timeval *); /*
* 注销,释放资源
*/
void (*dealloc)(struct event_base *, void *); /* set if we need to reinitialize the event base */
int need_reinit;
};

实际真正使用这个结构体是在event.c中,根据系统支持的不同系统调用来选取一组上述的eventop操作,这是用C语言模拟多态的典型用法。

 /* In order of preference */
static const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_DEVPOLL
&devpollops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};

有了上面的定义,比如我们系统使用epoll相关的系统调用,则会有对应的一组函数,位于:epoll.c

 const struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_dispatch,
epoll_dealloc,
/* need reinit */
};

上面提到的这些结构体对应的UML图如下:

从这个关系中我们可以很容易的看出他们在Reactor模型中各自代表了什么。

先看来一下Reactor模式的UML图:

               图1

                        图2

在Reactor模式中,有5个关键的参与者:

  • 描述符(handle):由操作系统提供的资源,用于识别每一个事件,如Socket描述符、文件描述符、信号的值等。在Linux中,它用一个整数来表示。事件可以来自外部,如来自客户端的连接请求、数据等。事件也可以来自内部,如信号、定时器事件。
  • 同步事件多路分离器(event demultiplexer):事件的到来是随机的、异步的,无法预知程序何时收到一个客户连接请求或收到一个信号。所以程序要循环等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术实现。在linux系统上一般是select、poll、epol_waitl等系统调用,用来等待一个或多个事件的发生。I/O框架库一般将各种I/O复用系统调用封装成统一的接口,称为事件多路分离器。调用者会被阻塞,直到分离器分离的描述符集上有事件发生。
  • 事件处理器(event handler):I/O框架库提供的事件处理器通常是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般声明为虚函数,以支持用户拓展。
  • 具体的事件处理器(concrete event handler):是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。
  • Reactor 管理器(reactor):定义了一些接口,用于应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。 Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生,Reactor管理器先是分离每个事件,然后调度事件处理器,最后调用相关的模 板函数来处理这个事件。

通过上面2张Reactor的UML图及描述,可以知道在libevent里面结构体和Reactor的对应关系:

  • 描述符(handle):对于I/O事件就是socket,对于信号就是绑定的信号。
  • 同步事件多路分离器(event demultiplexer):对应epoll、select、poll、kqueue、evport、devpoll等不同的系统调用。
  • 事件处理器(event handler):对应结构体event,该结构体中的成员void (*ev_callback)(int, short, void *arg)就是回调函数的模板,也就是面向对象中的虚函数。
  • 具体的事件处理器(concrete event handler):对应的具体回调函数都是通过函数event_set()去设置的。
  • Reactor 管理器(reactor):对应结构体event_base,里面根据系统选择系统调用的一组函数进行注册,并dispatch分发。

                        图3

本文参考自:

http://blog.csdn.net/sparkliang/article/details/4957744 (图1)

http://blog.csdn.net/u013074465/article/details/46276967 (图2及原始Reactor模型的描述)

http://blog.csdn.net/chinabhlt/article/details/43452977(图3)

libevent源码学习_event结构体的更多相关文章

  1. libevent源码学习

    怎么快速学习开源库比如libevent? libevent分析 - sparkliang的专栏 - 博客频道 - CSDN.NET Libevent源码分析 - luotuo44的专栏 - 博客频道 ...

  2. libevent源码学习(11):超时管理之min_heap

    目录min_heap的定义向min_heap中添加eventmin_heap中event的激活以下源码均基于libevent-2.0.21-stable.       在前文中,分析了小顶堆min_h ...

  3. libevent源码学习(10):min_heap数据结构解析

    min_heap类型定义min_heap函数构造/析构函数及初始化判断event是否在堆顶判断两个event之间超时结构体的大小关系判断堆是否为空及堆大小返回堆顶event分配堆空间堆元素的上浮堆元素 ...

  4. libevent源码学习(8):event_signal_map解析

    目录event_signal_map结构体向event_signal_map中添加event激活event_signal_map中的event删除event_signal_map中的event以下源码 ...

  5. libevent源码学习(9):事件event

    目录在event之前需要知道的event_baseevent结构体创建/注册一个event向event_base中添加一个event设置event的优先级激活一个event删除一个event获取指定e ...

  6. libevent源码学习(6):事件处理基础——event_base的创建

    目录前言创建默认的event_baseevent_base的配置event_config结构体创建自定义event_base--event_base_new_with_config禁用(避免使用)某一 ...

  7. libevent源码学习(7):event_io_map

    event_io_map 哈希表操作函数 hashcode与equals函数 哈希表初始化 哈希表元素查找 哈希表扩容 哈希表元素插入 哈希表元素替换 哈希表元素删除 自定义条件删除元素 哈希表第一个 ...

  8. [Go语言]从Docker源码学习Go——结构和函数的定义

    Docker在最近很火,而作为Docker的开发语言-Go也再次被大家提到. 已经使用Docker一段时间了,但是对于源码,尤其是其开发语言Go却一直是一知半解. 最近准备利用空余时间从Docker源 ...

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

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

随机推荐

  1. luogu P1126 机器人搬重物

    题目描述 机器人移动学会(RMI)现在正尝试用机器人搬运物品.机器人的形状是一个直径1.6米的球.在试验阶段,机器人被用于在一个储藏室中搬运货物.储藏室是一个N*M的网格,有些格子为不可移动的障碍.机 ...

  2. [BZOJ3894]文理分科(最小割)

    (1) 对每个位置建一个点F1,S向这个点连art[i][j]的边,这个点向T连science[i][j]的边. (2) 对每个位置再建一个点F2,S向这个点连same_art[i][j]的边,这个点 ...

  3. jdk_配置

    如果不知道jdk安装目录,可以使用如下命令查找到jdk安装目录 linux /usr/libexec/java_home windows版 新建系统变量JAVA_HOME(自己jdk的安装目录)C:\ ...

  4. VS2017安装错误:工作负荷不完整,未能安装包“sqlcmdlnutils,version=15.1.61703.130,chip=x64,language=zh-CN”。

    场景:已安装的VS2017维护安装MVC4时出现如下错误: 看问题描述是由于sqlcmdlnutils安装失败影响到其它组件的安装,于是单独下载此安装包进行安装,发现安装一切正常,继续维护VS2017 ...

  5. JAVA常见算法题(二十六)

    package com.xiaowu.demo; import java.util.Scanner; /** * Java实现将阿拉伯数字转为汉字 * * @author WQ * */ public ...

  6. flask-assets使用介绍

    作用:对css.js静态文件进行打包,打包成一个文件,然后去除文件里的换行.空行等进行压缩: 而且 Flask-Assets 还会使用特定的 HTTP Response Header, 让浏览器缓存这 ...

  7. 'telnet' 不是内部或外部命令,也不是可运行的程序 或批处理文件。

    Win7或者win8等是默认没有安装telnet功能,所以你直接用telnet命令是用不了的: 下面介绍在win8下面如何操作:“控制面板”-->“程序”(在左下角)-->程序和功能--- ...

  8. IE 下 input 不响应 change 事件的处理

    很多时候,我们都需要通过 input 来上传文件,通过 change 事件获取用户上传的文件,然后做一些额外的处理,最后上传到服务器. 可是事情往往就是没有那么美好.是的,IE 下 input 在选择 ...

  9. [Angular] Tree shakable provider

    When we create a Service, Angluar CLI will helps us to add: @#Injectable({ providedIn: 'root' }) It ...

  10. 开源 免费 java CMS - FreeCMS1.9 移动APP管理 栏目配置

    项目地址:http://www.freeteam.cn/ 栏目配置 管理员能够在这里设设置栏目是否是否同意移动app訪问.栏目页的布局等属性. 从左側管理菜单点击栏目配置进入. 选择须要管理的栏目后点 ...