1. 事件处理对象--event
Libevent中事件处理对象是event结构类型。event结构体封装了句柄、事件类型、回调函数,以及其他必要的标志和数据。
struct event {
// 事件回调结构
struct event_callback ev_evcallback;
/* for managing timeouts */
//如果是超时事件,则表明是小根堆还是通用超时队列
union {
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
} ev_timeout_pos;
// 对于I/O事件处理器,它是文件描述符值;对于信号事件处理器,它是信号值
evutil_socket_t ev_fd;
//该事件处理器从属的event_base实例
struct event_base *ev_base;
//联合体,表示从属的是信号队列还是I/O队列
union {
/* used for io events /
struct {
LIST_ENTRY (event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
/ used by signal events /
struct {
LIST_ENTRY (event) ev_signal_next;
short ev_ncalls;
/ Allows deletes in callback */
short *ev_pncalls;
} ev_signal;
} ev_;
//事件标志
short ev_events;
//当前激活事件类型
short ev_res; /* result passed to event callback */
//超时时间值
struct timeval ev_timeout;
};
事件标志为:
#define EVLIST_TIMEOUT 0x01 超时事件
#define EVLIST_INSERTED 0x02 从属于注册事件队列
#define EVLIST_SIGNAL 0x04 信号事件
#define EVLIST_ACTIVE 0x08 激活事件
#define EVLIST_INTERNAL 0x10 内部事件
#define EVLIST_ACTIVE_LATER 0x20
#define EVLIST_FINALIZING 0x40
#define EVLIST_INIT 0x80 事件处理器已经被初始化
2. 队列
在linux下,Libevent中注册的信号事件和I/O事件,都分别由队列来管理;
- 队列泛型
//用c宏命令的方式实现了泛型
//这里的name是指结构体叫什么名字,然后type是指该结构体是什么类型
define TAILQ_HEAD(name, type) \
struct name {
//tqh_first指的是该队列的首结构体
//tqh_last指的是该队列最后一个元素的地址 \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
//TAILQ_ENTRY并没有给结构体起名字,所以只能是一个匿名结构体,
//它一般是另外一个结构体或者共用体的成员
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
//由这两个结构体组成,就可以实现一个队列
//队列中的元素结构体。它有一个值,并且有前向指针和后向指针
//通过前后像指针,把队列中的节点(元素)连接起来
struct queue_entry_t
{
int value;
//从TAILQ_ENTRY的定义可知,它只能是结构体或者共用体的成员变量
TAILQ_ENTRY(queue_entry_t)entry;
};
//定义一个结构体,结构体名为queue_head_t,成员变量类型为queue_entry_t
//就像有头节点的链表那样,这个是队列头。它有两个指针,分别指向队列的头和尾
TAILQ_HEAD(queue_head_t, queue_entry_t);
在libevent中一些插入、删除都是由宏操作执行的
- event队列
//event_struct.h
TAILQ_HEAD (event_list, event);
//所以event_list的定义展开后如下:
struct event_list
{
struct event *tqh_first;
struct event **tqh_last;
};
- I/O事件或信号事件队列
struct event_signal_map{
void **entries; //用于存放evmap_io 或evmap_signal 的数组 */
int nentries; //entries数组的大小
}
//I/O事件队列
struct evmap_io{
struct event_list events;
ev_uint16_t nread;
ev_uint16_t nwrite;
ev_uint16_t nclose; //已关闭次数
}
//信号事件队列 evmap_signal是event_signal_map数组中的一个元素,这个元素是一个链表,存储相同signal/IO的事件
struct evmap_signal {
struct event_list events;
}
这里可以看出,对于I/O事件或者信号事件是用一个数组来存储的,而每一个数组元素是一个队列,里面存储的是同一个文件描述符的事件队列。也就是说对于同一个文件描述符可以有多个event来进行处理。
3. event_base
它是libevent的Reactor,它存在于主线程,用来管控那些需要处理的各种事件。
struct event_base {
/** 初始化Reactor的时候选择一种后端I/O复用机制
**/
const struct eventop *evsel; // 一些操作函数如:init、add、del
/** 指向I/O复用机制真正存储的数据,它通过evsel成员的init函数来初始化 */
void *evbase; //真正的epoll、poll
/** 事件变化队列,其用途是:如果一个文件描述符上注册的事件被多次修改,则
* 可以使用缓冲来避免重复的系统调用(比如epoll_ctl),它仅能用于时间
* 复杂度为O(1)的I/O复用技术*/
struct event_changelist changelist; //暂时没看到它的用处
/** 指向信号的后端处理机制,目前仅在signal.h文件中定义了一种处理方法
**/
const struct eventop *evsigsel;
/** 信号事件处理器使用的数据结构,其中封装了一个由socketpair创建的管道。它用于信号处理函数和事件多路分发器之间的通信 */
struct evsig_info sig;
/** 添加到event_base所有事件的数量 */
int virtual_event_count;
/** M最大可激活事件的数量 */
int virtual_event_count_max;
/** 所有注册事件的数量 */
int event_count;
/** 可注册事件的最大值 */
int event_count_max;
/** 注册在其中的激活事件数量 */
int event_count_active;
/** 最大存在的激活事件数量 */
int event_count_active_max;
/** 是否执行完活动事件队列上剩余的任务之后就退出事件循环
**/
int event_gotterm;
/** 是否立即退出循环 */
int event_break;
/** 是否应该启动一个新的事件循环 */
int event_continue;
/** 目前正在处理的活动事件队列的优先级 */
int event_running_priority;
/** 事件循环是否已经启动 */
int running_loop;
/** 设置为我们在循环中设置为“活动”的延迟的cbs数。
* 这是一个防止饥饿的黑客;使用event_config_set_max_dispatch_interval
* 的max_callbacks功能会更聪明
*/
int n_deferreds_queued;
/* 活动事件管理 */
/** 活动事件队列数组。索引值越小的队列,优先级越高。
* 高优先级的活动事件队列中的事件处理器将被优先处理
*/
struct evcallback_list *activequeues;
/** 活动事件队列数组的大小 */
int nactivequeues;
/** A list of event_callbacks that should become active the next time
* we process events, but not this time. */
struct evcallback_list active_later_queue;
/* common timeout logic */
/** 下面3个成员用于管理通用定时器队列 */
struct common_timeout_list **common_timeout_queues;
/** The number of entries used in common_timeout_queues */
int n_common_timeouts;
/** The total size of common_timeout_queues. */
int n_common_timeouts_allocated;
/** Mapping from file descriptors to enabled (added) events */
struct event_io_map io;
/** Mapping from signal numbers to enabled (added) events. */
struct event_signal_map sigmap;
/** 时间堆 */
struct min_heap timeheap;
/** Stored timeval: used to avoid calling gettimeofday/clock_gettime
* too often. */
struct timeval tv_cache;
struct evutil_monotonic_timer monotonic_timer;
/** Difference between internal time (maybe from clock_gettime) and
* gettimeofday. */
struct timeval tv_clock_diff;
/** Second in which we last updated tv_clock_diff, in monotonic time. */
time_t last_updated_clock_diff;
#ifndef EVENT__DISABLE_THREAD_SUPPORT
/* 多线程支持 */
/** 当前运行该event_base的事件循环的线程 */
unsigned long th_owner_id;
/** 对event_base的独占锁 */
void *th_base_lock;
/** 条件变量,用于唤醒正在等待某个事件处理完毕的线程 */
void *current_event_cond;
/** 等待current_event_cond的线程数 */
int current_event_waiters;
#endif
/** 当前事件循环正在执行哪个事件处理器的回调函数 */
struct event_callback *current_event;
#ifdef _WIN32
/** IOCP support structure, if IOCP is enabled. */
struct event_iocp_port *iocp;
#endif
/** Flags that this base was configured with */
enum event_base_config_flag flags;
struct timeval max_dispatch_time;
//为了避免优先级反转,导致太多低优先级的一直在run,而高优先级的得不到处理
int max_dispatch_callbacks;
int limit_callbacks_after_prio; //优先级阈值,超过此优先级的,那么在两个
//epoll_wait之间执行的次数不超过max_dispatch_callbacks
/* 下面这组成员变量给工作线程唤醒主线程提供了方法(使用socketpair创建的管道) */
/** True if the base already has a pending notify, and we don't need
* to add any more. */
int is_notify_pending;
/** A socketpair used by some th_notify functions to wake up the main
* thread. */
evutil_socket_t th_notify_fd[2];
/** An event used by some th_notify functions to wake up the main
* thread. */
struct event th_notify;
/** A function used to wake up the main thread from another thread. */
int (*th_notify_fn)(struct event_base *base);
/** Saved seed for weak random number generator. Some backends use
* this to produce fairness among sockets. Protected by th_base_lock. */
struct evutil_weakrand_state weakrand_seed;
/** List of event_onces that have not yet fired. */
LIST_HEAD(once_event_list, event_once) once_events;
};
4. eventop结构体
其封装了I/O复用机制必要的一些操作,比如注册事件、等待事件等。它为event_base支持的所有后端I/O复用机制提供了一个统一的接口。
struct eventop {
/** I/O复用机制的名字 */
const char *name;
/** 函数设置事件库以使用此后端。它应该创建一个新的结构
* 来保存运行后端所需的任何信息,并返回它。
* 返回的指针将由event_init存储到event_base.evbase字段中。
* 失败时,此函数应返回NULL。
* 比如,epoll中的epoll_create,用来初始化
*/
void *(*init)(struct event_base *);
/** 添加事件处理器
*/
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);
/** 这个函数就是循环等待,相当于epoll_wait
*/
int (*dispatch)(struct event_base *, struct timeval *);
/** 释放event_base所分配的资源 */
void (*dealloc)(struct event_base *);
/** Flag: set if we need to reinitialize the event base after we fork.
*/
int need_reinit;
/** I/O复用支持的一些技术,如边缘触发,水平触发等
* provide. */
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;
};