Libevent学习笔记(二)—— event_base使用概略
event_base保存Libevent事件循环所需的信息,可以说是Libevent的核心组件,在这篇post里我们概略性地探究一下这个结构体。
event_base 结构
event_base 的声明位于 event-internal.h 中。在 Libevent 中这种带有 - internal 后缀的头文件表示内部函数和结构体,不对外开放使用,用户直接 #include <event.h> 即可。
event_base 的成员可以分成下面几类来理解:
- 与 backend 相关的数据和函数指针,比如
evsel、evbase、evsigsel等,毕竟我们了解,Lievent 支持多种多路复用机制(在 Libevent-book 里被称为 backend)。 - 事件、事件循环和事件回调的状态信息,比如某些活跃事件的数量,再比如该
event_base所属的事件循环是否需要 break 或者 continue。需要注意的是布尔语义的状态变量(像event_break、event_continue)仍然是 int 类型,这是因为在 C99 之前,C 语言还不支持内置的 bool 类型,Libevent 的开发者需要保证后向兼容性。 - timeout 相关逻辑,Libevent 使用 queue(
common_timeout_list)和 min_heap(timeheap)来等待触发事件。 - 对多线程的支持,目前版本中
event_base还不支持跨线程使用,因此设置了锁(thread_base_lock)和条件变量(current_event_cond)来提供多线程支持。 - 一些目前看来还不是很重要的变量,比如支持 IOCP 的结构等等。
使用 event_config 定制 event_base
通常情况下我们直接使用 event_base_new() 得到一个默认的 event_base 即可,我们在 Hello World 用例里就是这么做的,但我们也可以使用 event_config 实现更精细的控制,然后使用 event_base_new_with_config(const struct event_config *cfg) 获得定制化的 event_base 。
struct event_config {
TAILQ_HEAD(event_configq, event_config_entry) entries;
/*
TAIQ_HEAD是一个宏:
#define TAILQ_HEAD(name, type)
struct name {
struct type *tqh_first; // first element
struct type **tqh_last; // addr of last next element
}
拆开之后是下面这样:
struct event_configq {
struct event_config_entry *tqh_first;
struct event_config_entry **tqh_last;
} entries;
*/
int n_cpus_hint;
struct timeval max_dispatch_interval;
int max_dispatch_callbacks;
int limit_callbacks_after_prio;
enum event_method_feature require_features;
enum event_base_config_flag flags;
};event_method_feature 和 event_base_config_flag 分别定义了 backend 支持的 feature 和 event_base 的行为,Libevent-book 对这两类枚举值有更详细的介绍。我们可以使用 event_config 的这些接口来对其成员进行相应操作:
int event_config_avoid_method(struct event_config *cfg, const char *method);
int event_config_require_features(struct event_config *cfg,
enum event_method_feature feature);
int event_config_set_flag(struct event_config *cfg,
enum event_base_config_flag flag);
/* 这个接口目前只对Windoiws系统的IOCP协议有意义 */
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)
int event_config_set_max_dispatch_interval(struct event_config *cfg,
const struct timeval *max_interval, int max_callbacks,
int min_priority);事件管理
Libevent 提供下面这些接口来进行 event_base 相关的事件管理,这里当然省略了特殊情况下实现类似功能的不同接口:
int event_add(struct event *ev, const struct timeval *timeout);
int event_del(struct event *ev);
void event_active(struct event *ev, int res, short ncalls);有一点让我迷惑的是,我们需要使用 event_new() 方法得到一个新的 event ,这里我们需要传入其对应的 event_base (作为 event 的成员),然后在上述接口里,实际上使用 ev 指针找到 event_base 再进行操作。相比于这种来回颠倒,似乎还是 OOP 中类似于 base->add_event(ev) 的表达更直观😅。