libevent源码解析 (四) 事件处理框架

struct event_base {
   const struct eventop *evsel;
  void *evbase; 
   int event_count;  /* counts number of total events */
   int event_count_active; /* counts number of active events */
  int event_gotterm;  /* Set to terminate loop */
   int event_break;  /* Set to terminate loop immediately */
   /* active event management */
  struct event_list **activequeues;
  int nactivequeues;
  /* signal handling info */
  struct evsignal_info sig;
  struct event_list eventqueue;
 struct timeval event_tv;
  struct min_heap timeheap;
 struct timeval tv_cache;
 };

1)evsel和evbase这两个字段的设置可能会让人有些迷惑,这里你可以把evsel和evbase看作是类和静态函数的关系,

比如添加事件时的调用行为:evsel->add(evbase, ev),实际执行操作的是evbase;这相当于class::add(instance, ev),instance就是class的一个对象实例。
evsel指向了全局变量static const struct eventop *eventops[]中的一个;

前面也说过,libevent将系统提供的I/O demultiplex机制统一封装成了eventop结构;因此eventops[]包含了select、poll、kequeue和epoll等等其中的若干个全局实例对象。
evbase实际上是一个eventop实例对象;

先来看看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;
 };

也就是说,在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,来完成自身的初始化、销毁释放;对事件的注册、注销和分发。
比如对于epoll,libevent实现了5个对应的接口函数,并在初始化时并将eventop的5个函数指针指向这5个函数,那么程序就可以使用epoll作为I/O demultiplex机制了,这个在后面会再次提到。

2)activequeues是一个二级指针,前面讲过libevent支持事件优先级,因此你可以把它看作是数组,其中的元素activequeues[priority]是一个链表,链表的每个节点指向一个优先级为priority的就绪事件event。

3)eventqueue,链表,保存了所有的注册事件event的指针。


4)sig是由来管理信号的结构体,将在后面信号处理时专门讲解;


5)timeheap是管理定时事件的小根堆,将在后面定时事件处理时专门讲解;


6)event_tv和tv_cache是libevent用于时间管理的变量,将在后面讲到;
其它各个变量都能因名知意,就不再啰嗦了。

2 ,创建和初始化event_base、

 

创建一个event_base对象也既是创建了一个新的libevent实例,程序需要通过调用event_init()(内部调用event_base_new函数执行具体操作)函数来创建,该函数同时还对新生成的libevent实例进行了初始化。

该函数首先为event_base实例申请空间,然后初始化timer mini-heap,选择并初始化合适的系统I/O 的demultiplexer机制,初始化各事件链表;
函数还检测了系统的时间设置,为后面的时间管理打下基础。

3 ,接口函数

前面提到Reactor框架的作用就是提供事件的注册、注销接口;根据系统提供的事件多路分发机制执行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数来处理事件。
Libevent中对应的接口函数主要就是:

 int  event_add(struct event *ev, const struct timeval *timeout);
 int  event_del(struct event *ev);
 int  event_base_loop(struct event_base *base, int loops);
 void event_active(struct event *event, int res, short events);
 void event_process_active(struct event_base *base); 

本节将按介绍事件注册和删除的代码流程,libevent的事件循环框架将在下一节再具体描述。
对于定时事件,这些函数将调用timer heap管理接口执行插入和删除操作;对于I/O和Signal事件将调用eventopadd和delete接口函数执行插入和删除操作(eventop会对Signal事件调用Signal处理接口执行操作);这些组件将在后面的内容描述。

1)注册事件

int event_add(struct event *ev, const struct timeval *tv)

参数:

    ev:指向要注册的事件;
    tv:超时时间;
函数将ev注册到ev->ev_base上,事件类型由ev->ev_events指明,如果注册成功,ev将被插入到已注册链表中;如果tv不是NULL,则会同时注册定时事件,将ev添加到timer堆上;
如果其中有一步操作失败,那么函数保证没有事件会被注册,可以讲这相当于一个原子操作。这个函数也体现了libevent细节之处的巧妙设计,且仔细看程序代码,部分有省略,注释直接附在代码中。

int event_del(struct event *ev)
{
 struct event_base *base;
 const struct eventop *evsel;
 void *evbase;
 // ev_base为NULL,表明ev没有被注册
 if (ev->ev_base == NULL)
  return (-1);
 // 取得ev注册的event_base和eventop指针
 base = ev->ev_base;
 evsel = base->evsel;
 evbase = base->evbase;
 // 将ev_callback调用次数设置为
 if (ev->ev_ncalls && ev->ev_pncalls) {
  *ev->ev_pncalls = 0;
 }
 
 // 从对应的链表中删除
 if (ev->ev_flags & EVLIST_TIMEOUT)
  event_queue_remove(base, ev, EVLIST_TIMEOUT);
 if (ev->ev_flags & EVLIST_ACTIVE)
  event_queue_remove(base, ev, EVLIST_ACTIVE);
 if (ev->ev_flags & EVLIST_INSERTED) {
  event_queue_remove(base, ev, EVLIST_INSERTED);
  // EVLIST_INSERTED表明是I/O或者Signal事件,
  // 需要调用I/O demultiplexer注销事件
  return (evsel->del(evbase, ev));
 }
 return (0);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值