土法炼钢兴趣小组的算法知识备份

Event Base 与 Event Loop

目录

在 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. 主循环流程图

Event Loop Flow

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. 关键步骤解析

  1. 计算 Timeout: Reactor 必须知道下一次定时器触发还要多久,以此作为 epoll_wait 的超时时间。如果堆顶定时器是 50ms 后触发,那么 epoll_wait 最多等待 50ms。
  2. Backend Dispatch: 这是阻塞点。当 epoll_wait 返回时,Libevent 会遍历就绪的 fd,找到对应的 struct event,将其状态设置为 EVLIST_ACTIVE,并插入到 activequeues 中。
  3. Process Active: 遍历 activequeues,依次调用用户的回调函数。注意,这里是同步调用。如果回调函数执行时间过长,会阻塞下一轮循环。

3. 退出循环:Exit vs Break

Libevent 提供了两种停止循环的方法,区别在于即时性

4. 总结

event_base 是 Libevent 的状态容器,而 event_base_loop 是驱动状态流转的动力源。理解了这个循环,你就理解了 Reactor 模式的本质:计算等待时间 -> 阻塞等待 -> 激活事件 -> 处理回调

下一篇,我们将深入 Layer 1,看看 epoll 等后端是如何被封装成统一接口 eventop 的。


上一篇: 00-intro/build-systems.md - 多构建工具链集成 下一篇: 01-core/backend-epoll.md - IO 多路复用层详解

返回 Libevent 专题索引


By .