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

事件结构体 (struct event)

目录

在 Libevent 的世界里,一切皆事件。无论是网络 I/O、定时器还是信号,最终都被封装成一个 struct event 对象。本篇我们将深入这个结构体的内部,看看它是如何工作的。

1. 结构体定义

虽然在 event.hstruct event 被声明为不透明的,但在 event_struct.h (或源码中的 event-internal.h) 中我们可以看到它的真面目。

struct event {
    // 1. 回调相关
    struct event_callback ev_evcallback;

    // 2. 链表节点 (用于插入各种队列)
    union {
        TAILQ_ENTRY(event) ev_next_with_common_timeout;
        int min_heap_idx;
    } ev_timeout_pos;
    evutil_socket_t ev_fd;

    // 3. 所属的 Reactor
    struct event_base *ev_base;

    // 4. 事件相关
    union {
        // 用于 I/O 事件链表
        struct {
            TAILQ_ENTRY(event) ev_io_next;
            struct timeval ev_timeout;
        } ev_io;
        // 用于信号事件链表
        struct {
            TAILQ_ENTRY(event) ev_signal_next;
            short ev_ncalls;
            short *ev_pncalls;
        } ev_signal;
    } ev_;

    // 5. 标志位
    short ev_events;      // 用户关注的事件 (EV_READ, EV_WRITE, etc.)
    short ev_res;         // 当前激活的原因 (EV_READ, EV_TIMEOUT)
    short ev_flags;       // 内部状态 (EVLIST_INSERTED, EVLIST_ACTIVE)
    unsigned char ev_pri; // 优先级
    unsigned char ev_closure; // 闭包类型 (EV_CLOSURE_EVENT_SIGNAL, etc.)
    
    // 6. 超时时间
    struct timeval ev_timeout;
};

: 为了节省内存,Libevent 使用了大量的 union。一个事件不可能同时是 I/O 事件又是信号事件(虽然可以复用结构体,但逻辑上是互斥的)。

2. 核心字段解析

2.1. ev_events (用户关注)

这是我们在 event_new 时传入的标志: * EV_READ / EV_WRITE: 关注读/写。 * EV_PERSIST: 持久化事件。默认情况下,事件触发一次回调后会自动从 Reactor 中移除(Non-persistent)。加上此标志后,事件会一直保持 Pending 状态,直到手动 event_del。 * EV_ET: 边缘触发 (Edge Trigger)。

2.2. ev_flags (内部状态)

这是 Libevent 内部维护事件生命周期的关键: * EVLIST_INIT: 事件已初始化 (event_assign / event_new)。 * EVLIST_INSERTED: 事件已注册到 Reactor (event_add),处于 Pending 状态。 * EVLIST_ACTIVE: 事件已触发,正在等待处理或正在处理,处于 Active 状态。 * EVLIST_TIMEOUT: 事件已注册到定时器堆中。 * EVLIST_SIGNAL: 事件已注册到信号队列中。

2.3. ev_callback

封装了用户定义的回调函数和参数:

struct event_callback {
    TAILQ_ENTRY(event_callback) evcb_active_next;
    short evcb_flags;
    unsigned char evcb_pri; // 优先级
    unsigned char evcb_closure;
    // 真正的回调函数指针
    union {
        void (*evcb_callback)(evutil_socket_t, short, void *);
        void (*evcb_selfcb)(struct event_callback *, void *);
        void (*evcb_cbfinalize)(struct event_callback *, void *);
    } evcb_cb_union;
    void *evcb_arg; // 用户参数
};

3. 事件生命周期 (Lifecycle)

一个事件的一生通常经历以下阶段:

stateDiagram-v2
    [*] --> Initialized: event_new()
    Initialized --> Pending: event_add()
    Pending --> Active: Triggered (IO/Timeout)
    Active --> Pending: Callback done (if EV_PERSIST)
    Active --> Initialized: Callback done (if !EV_PERSIST)
    Pending --> Initialized: event_del()
    Initialized --> [*]: event_free()
  1. Initialized (已初始化): 内存已分配,基本信息已填入,但 Reactor 还不知道它的存在。
  2. Pending (未决/注册): 调用 event_add 后,事件被加入到 event_base 的关注列表(如 epoll 或最小堆)。此时它在等待事件发生。
  3. Active (激活):
    • 硬件中断/系统调用返回,告知事件发生。
    • Libevent 将其从 Pending 列表(如果是 IO 事件且非 PERSIST)移出。
    • 将其加入到 activequeues 中。
  4. Running (运行): event_base_loop 处理激活队列,调用用户回调。

4. 优先级 (Priority)

Libevent 支持事件优先级。默认情况下,所有事件优先级相同。 通过 event_priority_set(ev, priority) 可以设置。 * event_base 维护了一个 activequeues 数组,索引越小优先级越高。 * 高优先级的事件会被优先处理。 * 注意: 如果高优先级事件源源不断地产生,低优先级事件可能永远得不到执行(饥饿问题)。

5. 总结

struct event 是连接用户逻辑与底层内核的桥梁。它精细地管理着状态流转,确保每一个 I/O、每一个定时器都能被准确、高效地调度。

理解了 ev_flags 的状态变化,你在调试 Libevent 程序时(特别是面对 Core Dump)就能一眼看出这个事件当前处于什么状态(是正在等数据,还是已经死掉了)。


上一篇: 01-core/cross-platform.md - 跨平台后端对比 下一篇: 02-data/evbuffer.md - Evbuffer 内存管理

返回 Libevent 专题索引


By .