前段时间对Libevent的源码进行了阅读,现整理如下:
介绍
libevent是一个轻量级的开源高性能事件驱动网络库,是一个典型的Reactor模型。其主要特点有事件驱动,高性能,跨平台,统一事件源等等。
网上关于libevent的源码分析有很多相关博客,本人在学习过程中也是借助了网络。所以,在此,关于libevent中的许多具体实现部分就不做介绍,主要是从相关数据结构层面上去分析。仅供参考。
event结构体
- libevent中的事件处理类型是event结构类型,event结构体封装了句柄,事件类型,回调函数,以及其他必要的标志和数据,是整个libevent库的核心。
该结构的定义如下:
struct event{
/*
* ev_next, ev_active_next都是双向链表节点指针
* 它们是libevent对不同事件类型和在不同时期,对事件的管理时使用到的字段
*
* libevent使用双向链表保存所有注册的IO和signal事件
* ev_next 就是该IO事件在链表中的位置,称此链表为已注册事件链表
* ev_active_next: libevent将所有激活事件放入链表active list中,然后遍历active list
* 执行调度,ev_active_next就指明了event在active list中的位置
*/
TAILQ_ENTRY(event) ev_next;
TAILQ_ENTRY(event) ev_active_next;
/*
* _ev 是一个联合体,所有具有相同描述符的IO事件通过ev.ev_io.ev_io_next成员串联成一个
* 尾队列,称之为IO事件队列,所有具有相同信号值的信号事件通过ev.ev_signal.ev_signal_next
* 串联成一个尾队列,称之为信号事件队列。ev.ev_signal.ev_ncalls成员指定时间发生时,Reactor
* 需要执行多少次该事件对应的回调函数,ev.ev_signal.ev_pcalls要么是NULL,要么执行ev.ev_signal.ev_ncalls
*/
union{
struct {
TAILQ_ENTRY(event) ev_io_next;
struct timeva l ev_timeout;
}ev_io;
struct {
TAILQ_ENTRY(event) ev_signal_next;
short ev_ncalls;
short *ev_pcalls;
}ev_signal;
} _ev;
/*
* ev_timeout_pos是一个联合体,它仅用于定时事件处理器,老版本libevnet中使用最小堆管理
* 定时器,但是开发者认为有时简单链表的管理更加高效。所以新版本中引入了“通用定时器”的
* 概念。这些定时器不是存储在时间堆中,而是存储在尾队列中,我们称之为通用定时器队列。
* 对于通用定时器而言,ev_timeout_pos中的ev_next_with_common_timeout成员指出了该定时器
* 在队列中的位置;对于其他定时器,min_heap_idx成员指出了该定时器在时间堆中的位置。一个
* 定时器是否是通用定时器,取决于其超时值的大小。具体参考event.c中的is_common_timeout函数。
*/
union{
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
}ev_timeout_pos;
//如果是超时事件ev_timeout超时值
struct timeva l ev_timeout;
//ev_base :该事件所属的反应堆实例,这是一个event_base结构体
struct event_base *ev_base;
//对于IO事件,是绑定的文件描述符,对于signal事件,是绑定的信号
int ev_fd;
/*
* ev_events : event关注的事件类型,它可以是以下三种类型:
* IO事件:EV_WRITE / EV_READ
* 定时事件: EV_TIMEOUT
* 信号: EV_SIGANL
*辅助选项: EV_PERSIST, 表明是一个永久事件
*/
short ev_events; //各个事件可以使用 "|"运算符进行组合,信号和IO事件不能同时设置
//事件就绪执行时,调用ev_callback的次数,通常为1
short ev_ncalls;
//指针,指向ev_ncalls或NULL
short *ev_pncalls; //allows deletes in callback
int ev_pri; //smaller numbers are higher priority
//ev_callback:event回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针
//其中fd对应ev_fd, events对应ev_events, arg对应ev_arg
void (*ev_callback)(int , short, void *arg);
//void* 表明可以是任意类型,在设置event时指定
void *ev_arg;
//记录了当前激活事件的类型
int ev_res; //result passed to event callback
/*
* libevent用于标记event信息的字段,表明当前的状态
*/
int ev_flags;
};
从event结构的定义可以看出,event中封装了句柄,回调函数,和事件类型。包括该事件在相应链表或时间堆中的索引位置。宏TAILQ_ENTRY是尾队列的节点类型,其定义为:
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; \ /*下一个元素*/
struct type **tqe_prev; \ /*前一个元素的地址*/
}
每当有事件event转变为就绪状态时,libevent就会把它移入到active event list[priority]中,其中priority是event的优先级;接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback()函数执行事件处理。
事件处理框架 event_base