Libevent 是一个开源的事件通知库,主要用于编写高效、可扩展的网络服务器和事件驱动型应用程序。其核心 API 提供了一种机制:当文件描述符上发生特定事件(例如可读、可写)、超时到达或信号触发时,自动执行注册的回调函数。该库旨在取代开发者自行实现的自定义事件循环,从而简化非阻塞 I/O 编程,同时保持跨平台兼容性和高性能。   

核心功能与特性 

        libevent 的设计重点在于抽象底层操作系统事件机制,提供统一接口,支持以下关键能力:

        事件注册与处理:支持文件描述符读/写事件、定时器、信号以及周期性超时回调;事件可动态添加或删除,无需修改主循环。

        缓冲 I/O(bufferevent):提供高级缓冲抽象,支持套接字、过滤器、速率限制、SSL/TLS(集成 OpenSSL 或 MbedTLS)、零拷贝文件传输以及 Windows IOCP。

        协议支持模块:内置 evdns(异步 DNS 解析)、evhttp(HTTP 客户端与服务器)、以及简易 RPC 框架。

        多线程支持:允许每个线程使用独立的 event_base,或在共享 event_base 上启用锁机制。

        可移植性:无需修改应用代码即可切换后端,提升开发效率。

创建事件基础(event_base)

struct event_base *base = event_base_new();

作用: 创建一个事件循环上下文(event loop context),这是 libevent 中所有事件、定时器、信号的“容器”和调度中心。

每个 event_base 实例独立管理一组事件,并绑定到一个特定的底层多路复用机制(epoll / kqueue / select 等)。

返回值:成功 → 非 NULL 指针

                失败 → NULL(内存不足或不支持的后端)

重要注意

一个线程通常只使用一个 event_base(最常见模式)。

多线程场景下可为每个线程创建独立 event_base,或使用带锁的共享 event_base(需开启 event_enable_lock 或使用 event_config 设置)。

创建事件对象(event)

struct event *ev = event_new(base, fd, EV_READ|EV_PERSIST, callback_func, arg);

   参数详解

base:所属的事件基础,必须是已创建的 event_base。

fd:监视的文件描述符(socket、pipe、eventfd 等)。特殊值:-1 用于纯定时器或信号事件(不监视任何 fd)。

events(事件标志位,可组合):

EV_READ:可读事件(数据到达或对端关闭)

EV_WRITE:可写事件(发送缓冲区有空间)

EV_SIGNAL:信号事件(此时 fd 应为信号编号,如 SIGINT=2)

EV_TIMEOUT:超时事件(通常与 event_add 的 timeval 一起使用)

EV_PERSIST:持久化标志,非常重要! 加此标志后,事件触发一次不会自动删除,下次条件满足还会继续触发(最常用模式)。

其他:EV_ET(边缘触发,仅部分后端支持)

callback_func:回调函数,原型必须为:

void callback(evutil_socket_t fd, short what, void *arg);

fd:触发时的事件文件描述符

what:实际触发的事件标志(EV_READ / EV_WRITE / EV_TIMEOUT / EV_SIGNAL 的组合)

arg:用户传入的任意指针(常用于传递结构体、上下文等)

arg:传递给回调函数的私有数据指针

返回值: 成功 → struct event * 失败 → NULL

常见变体(推荐使用宏简化创建):

纯定时器:evtimer_new(base, cb, arg) → 等价于 event_new(base, -1, 0, cb, arg)

信号事件:evsignal_new(base, SIGINT, cb, arg)

激活事件(加入监听)

event_add(ev, NULL);          // 永久监听(无超时)

第二个参数(超时):

NULL → 永不超时(最常见,用于读写事件)

非 NULL → 相对超时(从 event_add 调用时刻开始计时)

超时到达时:回调函数的 what 会包含 EV_TIMEOUT,且若无 EV_PERSIST 则事件自动删除

关键行为

事件只有在 event_add 后才真正被加入 libevent 的内核监控队列。

可多次调用 event_add(重复添加会被忽略)。

已激活的事件可通过 event_del(ev) 临时移除监听(不释放对象)。

进入事件循环(调度)

event_base_dispatch(base);

作用: 启动事件循环,阻塞当前线程,直到以下任一条件发生:

所有事件都被删除(无活跃/pending 事件)

有人调用 event_base_loopbreak(base)

有人调用 event_base_loopexit(base, &tv)(延迟退出)

清理资源

event_base_free(base);

必须顺序(避免内存泄漏或未定义行为):

        先删除/释放所有 struct event *(event_del(ev) + event_free(ev))

        再释放 event_base_free(base)

完整推荐模板

#include <event2/event.h>
#include <stdio.h>
#include <stdlib.h>

void on_read(evutil_socket_t fd, short what, void *arg) {
    // 处理读事件...
    if (what & EV_TIMEOUT) {
        printf("读超时\n");
    }
}

int main(void) {
    struct event_base *base = event_base_new();
    if (!base) return 1;

    evutil_socket_t sock = ...;  // 假设已创建并设置为非阻塞

    struct event *ev = event_new(base, sock, EV_READ|EV_PERSIST, on_read, base);
    if (!ev) goto cleanup;

    struct timeval tv = {30, 0};
    if (event_add(ev, &tv) < 0) goto cleanup_ev;

    // 可添加更多事件...

    event_base_dispatch(base);

cleanup_ev:
    event_free(ev);
cleanup:
    event_base_free(base);
    return 0;
}

总结关键设计理念

单线程 reactor 模型(默认)

事件对象与 event_base 解耦:事件可动态增删

持久化(EV_PERSIST) 是最常用模式,避免频繁创建/销毁事件

回调中不要阻塞:保持回调快速返回是高并发关键

资源管理严格:谁创建谁释放,顺序不可颠倒

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐