libevent学习笔记七:libevent事件处理框架剖析

libevent学习笔记七:libevent事件处理框架剖析

     前面几节已经对libevent的事件处理框架和event结构体做了描述和介绍,现在是需要进一步的深入剖析的时候了,更深入的对libevent事件的详细处理流程进行解析和理解,本节具体的将分析libevent的事件处理框架event_base和libevent注册、删除事件的流程,可结合前一节libevent对event的管理的流程进行理解。

1 、事件处理框架-event_base

    在Reactor模式一节已经介绍了Reactor的几个基本组件,本节深入讲解的部分对应于Reactor框架组件。在libevent中,在event_base结构体详细的进行了展现,结构体声明如下,这个结构存在于event-internal.h文件中:

  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); 

 

      这里仅就介绍事件注册和删除的代码流程进行说明,对于定时事件,这些函数将调用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_add(struct event *ev, const struct timeval *tv)
  {n
   struct event_base *base = ev->ev_base; // 要注册到的event_base
   const struct eventop *evsel = base->evsel;
   void *evbase = base->evbase; // base使用的系统I/O策略shan
   // 新的timer事件,调用timer heap接口在堆上预留一个位置
   // 注:这样能保证该操作的原子性:
   // 向系统I/O机制注册可能会失败,而当在堆上预留成功后,
   // 定时事件的添加将肯定不会失败;
   // 而预留位置的可能结果是堆扩充,但是内部元素并不会改变
   if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
     if (min_heap_reserve(&base->timeheap,1 + min_heap_size(&base->timeheap)) == -1)
       return (-1);  /* ENOMEM == errno */
   }
   // 如果事件ev不在已注册或者激活链表中,则调用evbase注册事件
   if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
     !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
      res = evsel->add(evbase, ev);
      if (res != -1) // 注册成功,插入event到已注册链表中
        event_queue_insert(base, ev, EVLIST_INSERTED);
  }
  // 准备添加定时事件
  if (res != -1 && tv != NULL) {
    struct timeval now;
    // EVLIST_TIMEOUT表明event已经在定时器堆中了,删除旧的
    if (ev->ev_flags & EVLIST_TIMEOUT)
     event_queue_remove(base, ev, EVLIST_TIMEOUT);
     // 如果事件已经是就绪状态则从激活链表中删除
     if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) {
       // 将ev_callback调用次数设置为0
       if (ev->ev_ncalls && ev->ev_pncalls) {
        *ev->ev_pncalls = 0;
       }
       event_queue_remove(base, ev, EVLIST_ACTIVE);
     }
     // 计算时间,并插入到timer小根堆中
     gettime(base, &now);
     evutil_timeradd(&now, tv, &ev->ev_timeout);
     event_queue_insert(base, ev, EVLIST_TIMEOUT);
    }
    return (res);
   }
  
   event_queue_insert()负责将事件插入到对应的链表中,下面是程序代码;
   event_queue_remove()负责将事件从对应的链表中删除,这里就不再重复贴代码了;
   void event_queue_insert(struct event_base *base, struct event *ev, int queue)
   {
    // ev可能已经在激活列表中了,避免重复插入
    if (ev->ev_flags & queue) {
     if (queue & EVLIST_ACTIVE)
      return;
    }
    // ...
    ev->ev_flags|= queue; // 记录queue标记
    switch (queue) {
     case EVLIST_INSERTED: // I/O或Signal事件,加入已注册事件链表
       TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
       break;
     case EVLIST_ACTIVE: // 就绪事件,加入激活链表
       base->event_count_active++;
       TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], ev, ev_active_next);
       break;
    case EVLIST_TIMEOUT: // 定时事件,加入堆
       min_heap_push(&base->timeheap, ev);
       break;
    }
 }

2)删除事件:

删除函数原型为:

 int  event_del(struct event *ev);

   函数将删除事件ev,对于I/O事件,从I/O 的demultiplexer上将事件注销;对于Signal事件,将从Signal事件链表中删除;对于定时事件,将从堆上删除;同样删除事件的操作则不一定是原子的,删除时间事件之后,有可能从系统I/O机制中注销会失败。

  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);
}

4、总结
     本文详细说明和分析了event_base这一重要结构体,可以看到libevent对系统的I/O demultiplex机制的封装event_op结构,并结合源代码分析了事件的注册和删除处理。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jyl_sh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值