libevent源码深度剖析五

——libevent的核心:事件event
张亮

对事件处理流程有了高层的认识后,本节将详细介绍libevent的核心结构event,以及libevent对event的管理。

1 libevent的核心-event

Libevent是基于事件驱动(event-driven)的,从名字也可以看到event是整个库的核心。event就是Reactor框架中的事件
处理程序组件;它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。
首先给出event结构体的声明,它位于event.h文件中:

  1. struct event {
  2. TAILQ_ENTRY (event) ev_next;
  3. TAILQ_ENTRY (event) ev_active_next;
  4. TAILQ_ENTRY (event) ev_signal_next;
  5. unsigned int min_heap_idx; /* for managing timeouts */
  6. struct event_base *ev_base;
  7. int ev_fd;
  8. short ev_events;
  9. short ev_ncalls;
  10. short *ev_pncalls; /* Allows deletes in callback */
  11. struct timeval ev_timeout;
  12. int ev_pri;  /* smaller numbers are higher priority */
  13. void (*ev_callback)(int, short, void *arg);
  14. void *ev_arg;
  15. int ev_res;  /* result passed to event callback */
  16. int ev_flags;
  17. };

下面简单解释一下结构体中各字段的含义。
1)ev_events:event关注的事件类型,它可以是以下3种类型:
I/O事件: EV_WRITE和EV_READ
定时事件:EV_TIMEOUT
信号:    EV_SIGNAL
辅助选项:EV_PERSIST,表明是一个永久事件
Libevent中的定义为:

  1. #define EV_TIMEOUT 0x01
  2. #define EV_READ  0x02
  3. #define EV_WRITE 0x04
  4. #define EV_SIGNAL 0x08
  5. #define EV_PERSIST 0x10 /* Persistant event */

可以看出事件类型可以使用“|”运算符进行组合,需要说明的是,信号和I/O事件不能同时设置;
还可以看出libevent使用event结构体将这3种事件的处理统一起来;
2)ev_next,ev_active_next和ev_signal_next都是双向链表节点指针;它们是libevent对不同事件类型和在不同的时期,对事件的管理时使用到的字段。
libevent使用双向链表保存所有注册的I/O和Signal事件,ev_next就是该I/O事件在链表中的位置;称此链表为“已注册事件链表”;
同样ev_signal_next就是signal事件在signal事件链表中的位置;
ev_active_next:libevent将所有的激活事件放入到链表active list中,然后遍历active list执行调度,ev_active_next就指明了event在active list中的位置;
2)min_heap_idx和ev_timeout,如果是timeout事件,它们是event在小根堆中的索引和超时值,libevent使用小根堆来管理定时事件,这将在后面定时事件处理时专门讲解
3)ev_base该事件所属的反应堆实例,这是一个event_base结构体,下一节将会详细讲解;
4)ev_fd,对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号;
5)ev_callback,event的回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针,原型为:
void (*ev_callback)(int fd, short events, void *arg)
其中参数fd对应于ev_fd;events对应于ev_events;arg对应于ev_arg;
6)ev_arg:void*,表明可以是任意类型的数据,在设置event时指定;
7)eb_flags:libevent用于标记event信息的字段,表明其当前的状态,可能的值有:

  1. #define EVLIST_TIMEOUT 0x01 // event在time堆中
  2. #define EVLIST_INSERTED 0x02 // event在已注册事件链表中
  3. #define EVLIST_SIGNAL 0x04 // 未见使用
  4. #define EVLIST_ACTIVE 0x08 // event在激活链表中
  5. #define EVLIST_INTERNAL 0x10 // 内部使用标记
  6. #define EVLIST_INIT     0x80 // event已被初始化

8)ev_ncalls:事件就绪执行时,调用ev_callback的次数,通常为1;
9)ev_pncalls:指针,通常指向ev_ncalls或者为NULL;
10)ev_res:记录了当前激活事件的类型;

2 libevent对event的管理

从event结构体中的3个链表节点指针和一个堆索引出发,大体上也能窥出libevent对event的管理方法了,可以参见下面的示意图:
 

每次当有事件event转变为就绪状态时,libevent就会把它移入到active event list[priority]中,其中priority是event的优先级;
接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback()函数执行事件处理;并根据就绪的句柄和事件类型填充cb_callback函数的参数。

3 事件设置的接口函数

要向libevent添加一个事件,需要首先设置event对象,这通过调用libevent提供的函数有:event_set(), event_base_set(), event_priority_set()来完成;下面分别进行讲解。

void event_set(struct event *ev, int fd, short events,
   void (*callback)(int, short, void *), void *arg)
1.设置事件ev绑定的文件描述符或者信号,对于定时事件,设为-1即可;
2.设置事件类型,比如EV_READ|EV_PERSIST, EV_WRITE, EV_SIGNAL等;
3.设置事件的回调函数以及参数arg;
4.初始化其它字段,比如缺省的event_base和优先级;
int event_base_set(struct event_base *base, struct event *ev)
设置event ev将要注册到的event_base;
libevent有一个全局event_base指针current_base,默认情况下事件ev将被注册到current_base上,使用该函数可以指定不同的event_base;
如果一个进程中存在多个libevent实例,则必须要调用该函数为event设置不同的event_base;

int event_priority_set(struct event *ev, int pri)
设置event ev的优先级,没什么可说的,注意的一点就是:当ev正处于就绪状态时,不能设置,返回-1。

4 小结

本节讲述了libevent的核心event结构,以及libevent支持的事件类型和libevent对event的管理模型;接下来将会描述libevent的事件处理框架,以及其中使用的重要的结构体event_base;

libevent源码深度剖析五的更多相关文章

  1. libevent源码深度剖析十

    libevent源码深度剖析十 ——支持I/O多路复用技术 张亮 Libevent的核心是事件驱动.同步非阻塞,为了达到这一目标,必须采用系统提供的I/O多路复用技术,而这些在Windows.Linu ...

  2. libevent源码深度剖析六

    libevent源码深度剖析六 ——初见事件处理框架 张亮 前面已经对libevent的事件处理框架和event结构体做了描述,现在是时候剖析libevent对事件的详细处理流程了,本节将分析 lib ...

  3. libevent 源码深度剖析十三

    libevent 源码深度剖析十三 —— libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多 ...

  4. libevent源码深度剖析十二

    libevent源码深度剖析十二 ——让libevent支持多线程 张亮 Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libev ...

  5. libevent源码深度剖析十一

    libevent源码深度剖析十一 ——时间管理 张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值 ...

  6. libevent源码深度剖析九

    libevent源码深度剖析九 ——集成定时器事件 张亮 现在再来详细分析libevent中I/O事件和Timer事件的集成,与Signal相比,Timer事件的集成会直观和简单很多.Libevent ...

  7. libevent源码深度剖析八

    libevent源码深度剖析八 ——集成信号处理 张亮 现在我们已经了解了libevent的基本框架:事件管理框架和事件主循环.上节提到了libevent中I/O事件和Signal以及Timer事件的 ...

  8. libevent源码深度剖析七

    libevent源码深度剖析七 ——事件主循环 张亮 现在我们已经初步了解了libevent的Reactor组件——event_base和事件管理框架,接下来就是libevent事件处理的中心部分 — ...

  9. libevent源码深度剖析四

    libevent源码深度剖析四 ——libevent源代码文件组织 1 前言 详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将是大有裨益的.本节内容不多,我想并不 ...

随机推荐

  1. everything 全盘文件查找工具及正则表达式的使用

    首先需要开启 everything 工具在(字符串)查找时,对正则表达式功能的支持: [菜单栏]⇒ [Search]⇒ 勾选[Enable Regex] ctrl + i:字符大小写敏感/不敏感 1. ...

  2. DEV控件 皮肤问题

    今天用cnPack清理了下整个工程的引用单元,清理完,问题来了,TcxPageControl不透明了. 折腾了一会,找到原因,清理单元时将dxSkinscxPCPainter也清掉了,导致皮肤无法正常 ...

  3. Python Requests快速入门

    迫不及待了吗?本页内容为如何入门Requests提供了很好的指引.其假设你已经安装了Requests.如果还没有, 去 安装 一节看看吧. 首先,确认一下: Requests 已安装 Requests ...

  4. imageView添加阴影和边框

    注意:大量设置阴影会造成卡顿!!! 用上这句之后流畅度大大增加:imageV.layer.shouldRasterize = YES; 例: // 设置阴影 imageV.layer.shadowOf ...

  5. openfaas 架构介绍

     此为官方介绍   Overview of OpenFaaS Function Watchdog You can make any Docker image into a serverless fun ...

  6. fn project Message Queues 配置

      Message Queues A message queue is used to coordinate asynchronous function calls that run through ...

  7. Android中的基类—抽取出来公共的方法

    在Android中,一般来说一个应用会存在几十个页面,并且一个应用一般也会使用一个特定的主题,其中的页面的风格也是一致的,并且页面中的动画效果.页面的切换效果等也应该保持同样的风格,那么就需要一个基类 ...

  8. SQL Server 占用CPU较高的解决方法

    触发原因:月底系统结账的时候对ERP的操作较多,有用户反馈系统之间的数据传输很久没有同步.随即到服务器上查看,没有发现有程序导致的问题,看了一下CPU的使用率,发现SQL Server占用率在百分之九 ...

  9. mysql 统计sql

    1.按照月份统计数据 SELECT DATE_FORMAT(d.create_time,'%Y-%m') months,COUNT(id) AS scannum FROM detail d GROUP ...

  10. 使用妹子UI开发的体验分享

    前阵子看到一个类似bootstrap的前端UI框架,好奇心驱使下,去琢磨了一些,最终决定网站改版用这个UI试试效果: 首页+头部: 投稿页: 现成拷贝过来的评论列表: 总结: 上手难度: (熟悉boo ...