对事件的详细处理流程了,本节将分析libevent的事件处理框架event_base和libevent注册、
删除事件的具体流程,可结合前一节libevent对event的管理。
1 事件处理框架-event_base
回想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*); // 删除事件 19 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和 20 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_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); 21 // 如果事件已经是就绪状态则从激活链表中删除 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); }