在 Libevent 中,event_base 是 Reactor
模式的核心实例,而 event_base_loop
则是驱动整个系统的引擎。本篇我们将深入源码(基于
2.1.12-stable),揭开它们的神秘面纱。
1.
event_base: Reactor 的载体
struct event_base
是一个不透明的结构体(Opaque Struct),其定义在
event-internal.h 中。它维护了 Reactor
运行所需的所有状态。
1.1. 核心成员一览
struct event_base {
// 1. 后端引擎 (Backend)
const struct eventop *evsel; // 当前使用的后端接口 (如 epollops)
void *evbase; // 后端特定的数据 (如 epoll_fd)
// 2. 事件队列
struct event_list *activequeues; // 激活队列数组 (按优先级划分)
int nactivequeues; // 优先级数量
// 3. 注册事件管理
struct event_io_map io; // IO 事件映射表 (fd -> event)
struct event_signal_map sigmap; // 信号事件映射表
struct min_heap timeheap; // 定时器最小堆
// 4. 控制标志
int event_gotterm; // 是否收到 loopexit 指令
int event_break; // 是否收到 loopbreak 指令
// ... 其他字段 (线程锁、通知管道等)
};1.2. 创建与配置
通常我们使用 event_base_new()
创建默认实例。如果需要定制(例如禁用 epoll 强制使用
poll),则需要使用 event_config。
struct event_config *cfg = event_config_new();
// 避免使用 epoll
event_config_avoid_method(cfg, "epoll");
// 要求后端支持 Edge-Triggered 模式
event_config_require_features(cfg, EV_FEATURE_ET);
struct event_base *base = event_base_new_with_config(cfg);
event_config_free(cfg);2.
event_base_loop: 永不停歇的引擎
event_base_dispatch(base) 实际上只是
event_base_loop(base, 0) 的封装。真正的逻辑在
event_base_loop 中。
2.1. 主循环流程图
2.2. 源码精读 (简化版)
int event_base_loop(struct event_base *base, int flags) {
// ... 初始化与锁 ...
while (!done) {
// 1. 检查是否需要立即停止
if (base->event_gotterm) {
break;
}
// 2. 计算后端等待时间 (Timeout)
// 从最小堆中取出最近的一个定时器时间
tv_p = &tv;
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);
} else {
// 如果有激活事件,或者是非阻塞模式,则不等待
evutil_timerclear(&tv);
}
// 3. 调用后端 (Backend Dispatch)
// 例如调用 epoll_wait,将就绪的 fd 对应的 event 插入激活队列
res = base->evsel->dispatch(base, tv_p);
// 4. 处理超时事件
timeout_process(base);
// 5. 处理激活队列 (Process Active Events)
if (base->event_count_active) {
event_process_active(base);
} else if (flags & EVLOOP_ONCE) {
// 如果是 EVLOOP_ONCE 模式且没有事件被激活,则退出
done = 1;
}
}
// ... 清理 ...
return 0;
}2.3. 关键步骤解析
- 计算 Timeout: Reactor
必须知道下一次定时器触发还要多久,以此作为
epoll_wait的超时时间。如果堆顶定时器是 50ms 后触发,那么epoll_wait最多等待 50ms。 - Backend Dispatch: 这是阻塞点。当
epoll_wait返回时,Libevent 会遍历就绪的 fd,找到对应的struct event,将其状态设置为EVLIST_ACTIVE,并插入到activequeues中。 - Process Active: 遍历
activequeues,依次调用用户的回调函数。注意,这里是同步调用。如果回调函数执行时间过长,会阻塞下一轮循环。
3. 退出循环:Exit vs Break
Libevent 提供了两种停止循环的方法,区别在于即时性:
event_base_loopexit(base, tv):- 设置
event_gotterm标志。 - 允许当前正在处理的激活事件全部执行完毕,然后再退出。
- 可以设置延迟退出时间
tv。
- 设置
event_base_loopbreak(base):- 设置
event_break标志。 - 立即停止。如果当前正在处理激活队列,执行完当前这个回调后,剩下的激活事件不再处理,直接退出。
- 设置
4. 总结
event_base 是 Libevent 的状态容器,而
event_base_loop
是驱动状态流转的动力源。理解了这个循环,你就理解了 Reactor
模式的本质:计算等待时间 -> 阻塞等待 ->
激活事件 -> 处理回调。
下一篇,我们将深入 Layer 1,看看 epoll
等后端是如何被封装成统一接口 eventop 的。
上一篇: 00-intro/build-systems.md - 多构建工具链集成 下一篇: 01-core/backend-epoll.md - IO 多路复用层详解