3. 主要结构体
3.1 event_base
结构体event_base是libevent的Reactor,可以说是libevent的核心。它定义在event-internal.h中,代码及作用用下所示:
//定于Reactor,位于event-internal.h
struct event_base {
//Reactor初始化时采用的I/O复用机制
const struct eventop *evsel;
//指向I/O复用机制真正存储的数据,通过evsel成员的init函数来初始化
void *evbase;
//事件变化队列,当一个文件描述符上注册的事件被多次修改,则可以使用缓冲来避免重复的系统调用.仅能用于
//时间复杂度为O(1)的I/O复用技术(比如epoll_ctl)
struct event_changelist changelist;
//指向信号的后端处理机制
const struct eventop *evsigsel;
//信号事件处理器使用的数据结构,封装了一个由socketpair创建的管道,用于信号处理函数和多路分发器之间的通信
struct evsig_info sig;
//添加到本event_base的虚拟事件的数量?虚拟事件是什么
int virtual_event_count;
//添加到本event_base的所有事件的数量
int event_count;
//本event_base激活事件的数量
int event_count_active;
//是否执行完活动事件队列上剩余的任务之后就退出事件循环
int event_gotterm;
//是否立即退出事件循环,而不管是否还有任务需要处理
int event_break;
//是否立即启动一个新的事件循环
int event_continue;
//当前event_base正在处理的活动事件队列的优先级
int event_running_priority;
//事件循环是否已经启动
int running_loop;
//活动事件队列数组,索引值越小,优先级越高。高优先级的活动事件队列中的事件处理器优先处理
struct event_list *activequeues;
//活动事件队列数组的大小,即该event_base一共有多少个不同优先级的活动事件队列
int nactivequeues;
//通用定时器队列
struct common_timeout_list **common_timeout_queues;
//通用定时器队列中元素个数
int n_common_timeouts;
//通用定时器队列所占空间总大小
int n_common_timeouts_allocated;
//存放延迟回调函数的链表。事件循环每次成功处理完一个活动事件队列中的所有事件之后,就调用一次延迟回调函数
struct deferred_cb_queue defer_queue;
//文件描述符和I/O事件关系映射表
struct event_io_map io;
//信号值和信号事件之间的映射关系表
struct event_signal_map sigmap;
//注册时间队列,存放I/O事件处理器和信号事件处理器
struct event_list eventqueue;
//记录事件循环上一次检测的时间点
struct timeval event_tv;
//最小堆、时间堆
struct min_heap timeheap;
//存储时间点,避免太频繁调用gettimeofday/clock_gettime
struct timeval tv_cache;
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
//clock_gettime和gettimeofday的差值
struct timeval tv_clock_diff;
//最后一次更新tv_clock_diff的时间
time_t last_updated_clock_diff;
#endif
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
//运行当前事件循环的线程号
unsigned long th_owner_id;
//event_base独占锁
void *th_base_lock;
//当前事件循环正在执行哪个事件处理器的回调函数
struct event *current_event;
//条件变量,用于唤醒正在等待事件处理完毕的线程
void *current_event_cond;
//等待被唤醒的线程数
int current_event_waiters;
#endif
#ifdef WIN32
struct event_iocp_port *iocp;
#endif
//event_base的一些配置参数
enum event_base_config_flag flags;
//以下用以给工作线程唤醒主线程
//若主线程处于唤醒状态,设为true
int is_notify_pending;
//唤醒主线程的双向管道
evutil_socket_t th_notify_fd[2];
//通知事件
struct event th_notify;
//其它线程唤醒主线程时调用的方法
int (*th_notify_fn)(struct event_base *base);
};
3.2 eventop
eventop结构体封装了I/O复用机制必要的一些操作,比如注册事件,等待事件等,它为event_base支持的所有后端复用机制提供了一个统一的接口。eventop定义在event-internal.h中,具体如下:
struct eventop {
//I/O复用名称
const char *name;
//为一个事件循环初始化使用该I/O复用机制所需的所有资源,返回指向这些数据的指针,
//存储在event_base.evbase中,失败返回null
void *(*init)(struct event_base *);
//注册事件,event_base表示事件循环,fd表示事件源,old表示该事件源上已注册的方法events表示本次要注册的方法
//fdinfo表示evmap中一个与事件源相关的结构,长度由fdinfo_len定义,当该事件源是第一次添加时,fdinfo_len为0
//函数成功返回0,失败返回-1
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
//删除一个已注册的事件
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
// 检测所有注册事件是否已就绪,为每个已就绪事件的event_active方法
int (*dispatch)(struct event_base *, struct timeval *);
//释放相关内存
void (*dealloc)(struct event_base *);
//标识程序执行fork后是否需要重新初始化
int need_reinit;
//I/O技术支持的一些特性,可选如下三个值的按位或:EV_FEATURE_ET(边沿触发事件),EV_FEATURE_01(事件监测算法是O(1))和
//EV_FEATURE_FDS(不仅能监听socket上的事件,还能监听其他类型的文件描述符上的事件)
enum event_method_feature features;
//有的I/O复用机制需要为每个I/O事件队列和信号事件队列分配额外的内存,以避免同一个文件描述符被重复插入I/O复用机制的事件表中。
//evmap_io_add(或evmap_io_del)函数在调用eventop的add(或del)方法时,将这段内存的起始地址作为第5个参数传递给add(或del)方法,
//下面这个成员指定了这段内存的长度
size_t fdinfo_len;
};
对于不同的I/O复用机制来说,只需要定义一个eventop对象,初始化对应的成员即可。当event_base初始化的时候,会结合当前系统环境和I/O复用优先级选择一种I/O复用技术。
3.3 event
接下来看libevent的事件处理器结构struct event。struct event定义在include/event2/event_struct.h中,具体如下所示:
<span style="white-space:pre"> </span>struct event {
//活动事件队列,所有被激活的事件处理器通过该成员串联成一个尾队列
//活动事件队列不止一个,不同优先级的事件处理器被激活后将插入不同的活动事件队列中
//在事件循环中,Reactor将按照优先级从高到低遍历所有活动事件队列,并以此处理其中的事件处理器
TAILQ_ENTRY(event) ev_active_next;
//注册事件队列 所有已注册的事件处理器通过该成员串联成一个尾队列
TAILQ_ENTRY(event) ev_next;
//仅用于定时器。对于通用定时器,存储在尾队列中,ev_next_*指定尾队列中所处位置
//其它定时器存在于时间堆中,min_heap_idx指定在时间堆中的位置
//可通过event.c中的is_common_timeout函数判断是否为通用定时器
union{
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
}ev_timeout_pos;
//事件处理器从属的event_base实例
struct event_base *ev_base;
//事件处理器从属的事件队列
union{
// I/O事件队列
struct{
//所有具有相同文件描述符的I/O事件处理器通过本成员串联成尾队列
TAILQ_ENTRY(event) ev_io_next;
//超时时间,过了该时间后该I/O事件失效
struct timeval ev_timeout;
}ev_io;
// 信号事件队列
struct{
//所有具有相同信号值的信号处理事件处理器通过ev.ev_signal成员串联成尾队列
TAILQ_ENTRY(event) ev_signal_next;
//指定信号值事件发生时,执行多少次对应的回调函数
short ev_ncalls;
//指向ev_ncalls或NULL,用于回调函数删除或修改
short *ev_pncalls;
}ev_signal;
}_ev;
//注册的事件类型按位或(读写和信号不能同时设置),包括如下事件
/* #define EV_TIMEOUT 0x01 定时事件
#define EV_READ 0x02 可读事件
#define EV_WRITE 0x04 可写事件
#define EV_SIGNAL 0x08 信号事件
#define EV_PERSIST 0x10 永久事件
#define EV_ET 边沿触发事件,需要I/O复用系统调用支持
*/
short ev_events;
//记录当前激活事件的类型
short ev_res;
//事件标志
/* #define EVLIST_TIMEOUT 0x01 事件处理器从属于通用定时器队列或时间堆
#define EVLIST_INSERTED 0x02 事件处理器从属于注册事件队列
#define EVLIST_ACTIVE 0x08 事件处理器从属于活动事件队列
#define EVLIST_INTERNAL 0x10 内部使用
#define EVLIST_INIT 0x80 事件处理器已被初始化
#define EVLIST_ALL (0xf000 | 0x9f) 定义所有标志
*/
short ev_flags;
//指定时间优先级,值越小优先级越高
ev_uint8 ev_pri;
//指定event_base执行事件处理器的回调函数的行为
/* #define EV_CLOSURE_NONE 0 默认行为
#define EV_CLOSURE_SIGNAL 1 执行信号事件处理器的函数时
#define EV_CLOSURE_PERSIST 2 执行回调函数后,再次将事件加入注册事件队列中
*/
e_uint8 ev_closure;
//仅对定时器有效,指定定时器的超时值
struct timeval ev_timeout;
//事件回调函数,由event_base调用。入参分别为ev_fd, ev_res和ev_arg
void (*ev_callback)(evutil_socket_t, short, void *arg);
//回调函数参数
void *ev_arg;
};
event事件处理器封装了句柄、事件类型、回调函数以及其它必要的标志和数据。在event结构体中,涉及到的一个结构体是尾队列,一般通过以下宏来定义
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; \ //指向下个元素
struct type **tqe_prev; \ //前一个元素的next指针的地址
}
#define TAILQ_HEAD(name, type) \
struct name{ \
struct type *tqh_first; \ //指向第一个元素
struct type **tqh_last; \ //指向最后一个元素的next指针的地址
}
TAILQ_ENTRY表示队列的一个节点,TAILQ_HEAD表示该队列的头,该宏定义可以在event_struct.h和compat/sys/queue.h中找到