libevent事件处理框架 event_base

本文讲解的部分对应于Reactor 框架组件。在libevent中,Reactor 框架组件表现为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 用于时间管理的变量,将在后面讲到;

创建和初始化event_base
创建一个event_base 对象也既是创建了一个新的libevent 实例,程序需要通过调用event_init() (内部调用event_base_new 函数执行具体操作)函数来创建,该函数同时还对新生成的libevent 实例进行了初始化。
该函数首先为event_base实例申请空间,然后初始化timer mini-heap ,选择并初始化合适的系统I/O 的demultiplexer 机制,初始化各事件链表;
函数还检测了系统的时间设置,为后面的时间管理打下基础。

接口函数

前面提到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 )
{
struct event_base * base = ev-> ev_base; // 要注册到的event_base
const struct eventop * evsel = base-> evsel ;
void * evbase = base-> evbase; // base 使用的系统I/O 策略
// 新的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_INSERTE|DEVLIST_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_TIMEOU) T
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()负责将事件插入到对应的链表中,下面是程序代码:

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_INSERTE: D// 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_TIMEOU: T// 定时事件,加入堆
min_heap_push(&base-> timeheap , ev);
break ;
}
}

保证注册事件原子操作的方法:
如果事件有定时选项,则注册事件时要同时在事件链表(I/O,信号)中注册该事件,并且在定时最小堆(timer heap)中添加定时事件,为了保证操作的原子性,由于在事件链表(I/O,信号)中注册事件可能会失败,所以先调用timer heap接口在堆上预留一个位置,预留成功后定时事件的添加就肯定不会失败,然后再向事件链表中注册该事件,如果注册成功,则在timer heap内添加定时事件,如果注册失败,timer heap内只是预留了位置(堆扩充),内部元素不变。
如果注册成功, ev将被插入到已注册链表中; 如果tv 不是NULL ,则会同时注册定时事件, 将ev 添加到timer堆上;这样在如果其中有一步操作失败, 那么函数保证没有事件会被注册, 可以讲这相当于一个原子操作。

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_TIMEOU);T
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
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值