libevent的核心-event
struct event* signal_event = evsignal_new(base, SIGINT, signal_cb, base);
event_add(signal_event, NULL);
Libevent是基于事件驱动(event-driven)的,从名字也可以看到event是整个库的核心。event就是Reactor框架中的事件处理程序组件;它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。
//在event2/event_struct.h中event的结构描述
struct event {
TAILQ_ENTRY (event) ev_next; /*增加下一个事件*/
TAILQ_ENTRY (event) ev_active_next; /*增加下一个活动事件*/
TAILQ_ENTRY (event) ev_signal_next; /*增加下一个信号*/
/*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中的位置;*/
unsigned int min_heap_idx; /* for managing timeouts 表示该event保存在min_heap数组中的索引*/
struct timeval ev_timeout; //用来保存事件的超时时间
/*min_heap_idx和ev_timeout,如果是timeout事件,它们是event在小根堆中的索引和超时值,libevent使用小根堆来管理定时事件*/
struct event_base *ev_base; /* 该事件所属的反应堆实例,这是一个event_base结构体*/
int ev_fd; /*对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号*/
short ev_events; /*event关注的事件类型,它可以是以下3种类型:
I/O事件: EV_WRITE和EV_READ
定时事件:EV_TIMEOUT
信号: EV_SIGNAL
辅助选项:EV_PERSIST,表明是一个永久事件
Libevent中的定义为:
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
可以看出事件类型可以使用“|”运算符进行组合,需要说明的是,信号和I/O事件不能同时设置;
还可以看出libevent使用event结构体将这3种事件的处理统一起来;*/
void *ev_arg; /*回调函数的参数,void*,表明可以是任意类型的数据,在设置event时指定*/
void (*ev_callback)(int fd, short events, void *arg);
/*event的回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针,
其中参数fd对应于ev_fd;events对应于ev_events;arg对应于ev_arg*/
int ev_pri; /* smaller numbers are higher priority */
short ev_ncalls; /*事件就绪执行时,调用ev_callback的次数,通常为1*/
short *ev_pncalls; /* Allows deletes in callback ,指针,通常指向ev_ncalls或者为NULL*/
int ev_res; /* result passed to event callback ,记录了当前激活事件的类型*/
int ev_flags; /*libevent用于标记event信息的字段,表明其当前的状态,可能的值有:
#define EVLIST_TIMEOUT 0x01 // event在time堆中
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中
#define EVLIST_SIGNAL 0x04 // 未见使用
#define EVLIST_ACTIVE 0x08 // event在激活链表中
#define EVLIST_INTERNAL 0x10 // 内部使用标记
#define EVLIST_INIT 0x80 // event已被初始化
#define EVLIST_TIMEOUT 0x01 // event在time堆中
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中
#define EVLIST_SIGNAL 0x04 // 未见使用
#define EVLIST_ACTIVE 0x08 // event在激活链表中
#define EVLIST_INTERNAL 0x10 // 内部使用标记
#define EVLIST_INIT 0x80 // event已被初始化 */
};
注册事件
函数原型: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);
// 如果事件已经是就绪状态则从激活链表中删除
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);
}
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;
}
}