标签:
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已被初始化 */ };
从event结构体中的3个链表节点指针和一个堆索引出发,大体上也能窥出libevent对event的管理方法了,可以参见下面的示意图:
每次当有事件event转变为就绪状态时,libevent就会把它移入到active event list[priority]中,其中priority是event的优先级;接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback()函数执行事件处理;并根据就绪的句柄和事件类型填充cb_callback函数的参数。
要向libevent添加一个事件,需要首先设置event对象,这通过调用libevent提供的函数有:event_set(), event_base_set(), event_priority_set()来完成;
void event_set(struct event *ev, int fd, short events,void (*callback)(int, short, void *), void *arg)
int event_base_set(struct event_base *base, struct event *ev)
int event_priority_set(struct event *ev, int pri)
//event_base 定义在event-internal.h文件中 struct event_base { const struct eventop *evsel; //表示选择的事件引擎,可能为:epoll, poll, select void *evbase; //全局对象 /* 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 *); // 注销,释放资源 int need_reinit;// set if we need to reinitialize the event base }; 也就是说,在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,来完成自身的初始化、销毁释放;对事件的注册、注销和分发。 比如对于epoll,libevent实现了5个对应的接口函数,并在初始化时并将eventop的5个函数指针指向这5个函数,那么程序就可以使用epoll作为I/O demultiplex机制了 */ 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; /*是一个二级指针,前面讲过libevent支持事件优先级,因此你可以把它看作是数组, 其中的元素activequeues[priority]是一个链表, 链表的每个节点指向一个优先级为priority的就绪事件event。*/ struct evsignal_info sig; /* signal handling info,是来管理信号的结构体 */ struct event_list eventqueue;//链表,保存了所有的注册事件event的指针。 struct min_heap timeheap; //用来检测事件是否超时的堆栈,是管理定时事件的小根堆 struct timeval event_tv; //系统的当前时间 struct timeval tv_cache; //与event::ev_timeout进行比较,确定事件是否超时 //event_tv和tv_cache是libevent用于时间管理的变量 };
创建一个event_base对象也既是创建了一个新的libevent实例,程序需要通过调用event_init()(内部调用event_base_new()函数执行具体操作)函数来创建,该函数同时还对新生成的libevent实例进行了初始化。
该函数首先为event_base实例申请空间;然后初始化timer mini-heap;选择并初始化合适的系统I/O 的demultiplexer机制;初始化各事件链表;函数还检测了系统的时间设置,为后面的时间管理打下基础。
前面提到Reactor框架的作用就是提供事件的注册、注销接口;根据系统提供的事件多路分发机制执行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数来处理事件。Libevent中对应的接口函数主要就是:
下面将按介绍事件注册和删除的代码流程,libevent的事件循环框架将在下一文中再具体描述。
对于定时事件,这些函数将调用timer heap管理接口执行插入和删除操作;对于I/O和Signal事件将调用eventop的add和delete接口函数执行插入和删除操作(eventop会对Signal事件调用Signal处理接口执行操作);
[libevent]event,event_base结构体描述
标签:
原文地址:http://blog.csdn.net/shimazhuge/article/details/45082455