Libevent源码分析—event_add()

本文详细介绍了libevent库中关键函数event_add的工作原理,包括事件注册、超时设置及事件状态管理等过程。同时,也解释了event_del如何从不同事件集合中移除事件。
接下来就是将已经初始化的event注册到libevent的事件链表上,通过event_add()来实现,源码位于event.c中。

event_add()

这个函数主要完成了下面几件事:
1.将event注册到event_base的I/O多路复用要监听的事件中
2.将event注册到event_base的已注册事件链表中
3.如果传入了超时时间,则删除旧的超时时间,重新设置,并将event添加到event_base的小根堆中;
   如果没有传入超时时间,则不会添加到小根堆中。
只有步骤1成功,才会执行步骤2和3;否则什么都没做,直接返回,保证不会改变event的状态。
 
从中还可以看到,将event添加到已注册事件链表、添加到小根堆、从活跃事件链表移除、从小根堆中移除,都是通过两个函数完成的:event_queue_insert()、event_queue_remove()
int
event_add(struct event *ev, const struct timeval *tv)
{
    struct event_base *base = ev->ev_base;    //event所属的event_base
    const struct eventop *evsel = base->evsel;    //event_base的I/O多路复用机制
    void *evbase = base->evbase;    //event_base的I/O多路复用机制
    int res = 0;
    //DEBUG log.h
    event_debug((
         "event_add: event: %p, %s%s%scall %p",
         ev,
         ev->ev_events & EV_READ ? "EV_READ " : " ",
         ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
         tv ? "EV_TIMEOUT " : " ",
         ev->ev_callback));
    assert(!(ev->ev_flags & ~EVLIST_ALL));
    /*
     * prepare for timeout insertion further below, if we get a
     * failure on any step, we should not change any state.
     */
    //如果传入了超时时间并且event不再time小根堆上,则在小根堆上预留一个位置
    //以保证如果后面有步骤失败,不会改变初始状态,保证是个原子操作
    if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
        if (min_heap_reserve(&base->timeheap,    //min_heap.h
            1 + min_heap_size(&base->timeheap)) == -1)
            return (-1);  /* ENOMEM == errno */
    }
    //如果event不在已注册链表或活跃链表中,
    //则调用evsel->add()注册event事件到I/O多路复用监听的事件上
    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
        res = evsel->add(evbase, ev);    //将event注册到监听事件上
        //注册监听事件成功,则将event注册到已注册事件链表上
        if (res != -1)
            event_queue_insert(base, ev, EVLIST_INSERTED);  //插入  
    }
    /* 
     * we should change the timout state only if the previous event
     * addition succeeded.
     */
    //前面操作都成功情况下,才能执行下面步骤
    //改变超时状态
    if (res != -1 && tv != NULL) {
        struct timeval now;
        /* 
         * we already reserved memory above for the case where we
         * are not replacing an exisiting timeout.
         */
        //EVLIST_TIMEOUT表明event已在定时器堆中
        //则删除旧的定时器
        if (ev->ev_flags & EVLIST_TIMEOUT)
            event_queue_remove(base, ev, EVLIST_TIMEOUT);  //移除
        /* Check if it is active due to a timeout.  Rescheduling
         * this timeout before the callback can be executed
         * removes it from the active list. */
        //如果事件是由于超时而变成活跃事件
        //则从活跃事件链表中删除
        if ((ev->ev_flags & EVLIST_ACTIVE) &&
            (ev->ev_res & EV_TIMEOUT)) {
            /* See if we are just active executing this
             * event in a loop
             */
            if (ev->ev_ncalls && ev->ev_pncalls) {
                /* Abort loop */
                *ev->ev_pncalls = 0;  //调用次数清0
            }
            //从活跃事件链表移除
            event_queue_remove(base, ev, EVLIST_ACTIVE);  //移除
        }
        gettime(base, &now);
        evutil_timeradd(&now, tv, &ev->ev_timeout);    //为event添加超时时间
        event_debug((
             "event_add: timeout in %ld seconds, call %p",
             tv->tv_sec, ev->ev_callback));
        //将event插入到小根堆中
        event_queue_insert(base, ev, EVLIST_TIMEOUT);  //插入
    }
    return (res);
}

event_queue_insert()

该函数根据不同的输入队列,即不同的事件,在不同的队列中插入,并增加相应的事件计数,更新event状态;
EVLIST_INSERTED:在已注册事件链表event_base.eventqueue插入
EVLIST_ACTIVE:根据event优先级,在活跃事件链表event_base.activequeues[event.ev_pri]插入
EVLIST_TIMEOUT:在小根堆event_base.timeheap中插入
void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
    //如果event已经在活跃链表中,则返回;否则,出错
    if (ev->ev_flags & queue) {
        /* Double insertion is possible for active events */
        if (queue & EVLIST_ACTIVE)
            return;
        event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
               ev, ev->ev_fd, queue);
    }
    if (~ev->ev_flags & EVLIST_INTERNAL)
        base->event_count++;  //增加注册事件数
    ev->ev_flags |= queue;  //改变event状态
    switch (queue) {  //根据不同的输入参数队列,选择在不同的事件集合中插入
    case EVLIST_INSERTED:  //I/O或Signal事件
        TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);  //在已注册事件链表插入
        break;
    case EVLIST_ACTIVE:  //活跃事件
        base->event_count_active++;  //增加活跃事件数
        TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],  //在活跃事件链表插入
            ev,ev_active_next);
        break;
    case EVLIST_TIMEOUT: {  //定时器事件
        min_heap_push(&base->timeheap, ev);  //在小根堆插入
        break;
    }
    default:
        event_errx(1, "%s: unknown queue %x", __func__, queue);
    }
}

event_queue_remove()

和event_queue_insert()相对应,这个函数主要根据不同的输入参数,从不同的事件集合中删除事件。
void
event_queue_remove(struct event_base *base, struct event *ev, int queue)
{
    if (!(ev->ev_flags & queue))
        event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
               ev, ev->ev_fd, queue);
    if (~ev->ev_flags & EVLIST_INTERNAL)
        base->event_count--;
    ev->ev_flags &= ~queue;
    switch (queue) {
    case EVLIST_INSERTED:  //I/O、Signal事件
        TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
        break;
    case EVLIST_ACTIVE:  //活跃事件
        base->event_count_active--;
        TAILQ_REMOVE(base->activequeues[ev->ev_pri],
            ev, ev_active_next);
        break;
    case EVLIST_TIMEOUT:  //定时器事件
        min_heap_erase(&base->timeheap, ev);
        break;
    default:
        event_errx(1, "%s: unknown queue %x", __func__, queue);
    }
}

event_del()

libevent还提供了event_del()这个函数,该函数从直接删除event事件,该函数就是主要通过调用event_queue_remove()函数完成删除的功能。
另外,该函数还将event从I/O多路复用监听的事件中删除。
int
event_del(struct event *ev)
{
    struct event_base *base;
    const struct eventop *evsel;
    void *evbase;
    event_debug(("event_del: %p, callback %p",
         ev, ev->ev_callback));
    /* An event without a base has not been added */
    if (ev->ev_base == NULL)
        return (-1);
    base = ev->ev_base;
    evsel = base->evsel;
    evbase = base->evbase;
    assert(!(ev->ev_flags & ~EVLIST_ALL));
    /* See if we are just active executing this event in a loop */
    //计数清0
    if (ev->ev_ncalls && ev->ev_pncalls) {
        /* Abort loop */
        *ev->ev_pncalls = 0;
    }
    //根据event不同的状态,从相应的event集合中删除
    if (ev->ev_flags & EVLIST_TIMEOUT)
        event_queue_remove(base, ev, EVLIST_TIMEOUT);
    if (ev->ev_flags & EVLIST_ACTIVE)
        event_queue_remove(base, ev, EVLIST_ACTIVE);
    if (ev->ev_flags & EVLIST_INSERTED) {
        event_queue_remove(base, ev, EVLIST_INSERTED);
        return (evsel->del(evbase, ev));  //从I/O多路复用监听的事件中删除
    }
    return (0);
}

 

 

转载于:https://www.cnblogs.com/zxiner/p/6929108.html

<think>好的,用户想要了解libevent的源码剖析,我需要先回忆一下libevent的基本信息。Libevent是一个用C语言编写的高性能网络库,主要支持事件驱动,处理像网络IO、定时器、信号这些事件。它常用于构建高并发的服务器应用,比如Memcached就是用它来处理的。 首先,我应该介绍一下libevent的核心组件,这样用户能有个整体认识。事件循环(event loop)、事件(event)、缓冲区管理(evbuffer)这些是基本的部分。然后可能需要详细讲讲事件循环的工作机制,比如Reactor模式,它是如何通过系统调用如select、poll、epoll或者kqueue来监听事件的。 接下来要分析源码结构。源码目录通常包括event.c、buffer.c、http等部分,每个部分的功能需要解释清楚。比如event.c负责核心事件的处理,buffer.c管理网络数据的缓冲区。 然后,事件处理流程是关键。用户可能想知道事件是如何被注册、添加到事件循环,然后被分发处理的。这里可以结合具体的函数,比如event_new、event_add,以及事件回调的执行过程。 多线程支持也是用户可能关心的点,需要说明libevent在多线程环境下的处理方式,比如加锁机制,如何保证线程安全。 另外,性能优化方面,比如使用epoll的ET模式,或者时间缓存来减少系统调用次数,这些细节能体现libevent的高效设计。 用户可能还希望了解实际应用中如何利用libevent,比如结合Memcached的例子,说明其在实际项目中的应用场景。 最后,推荐一些学习资源,比如官方文档、书籍或者社区,帮助用户进一步深入学习。还要注意使用中文,保持讲解清晰,结构合理,避免过于技术化的术语,让不同层次的读者都能理解。</think>LibEvent 是一个基于事件驱动的高性能网络库,使用 C 语言编写,支持多种 I/O 多路复用技术(如 `select`、`poll`、`epoll`、`kqueue` 等),广泛应用于高并发服务器开发(如 Memcached)。其核心思想是通过**事件循环**(Event Loop)高效管理网络事件、定时器和信号。 以下是对 LibEvent 源码的核心剖析,帮助你理解其设计原理和工作机制: --- ### 1. **核心组件** #### **事件(Event)** - **基本单元**:所有操作围绕 `struct event` 展开,代表一个需要监听的事件(如 socket 可读、定时器到期、信号触发)。 - **关键字段**: - `ev_fd`:关联的文件描述符(如 socket)。 - `ev_events`:事件类型(`EV_READ`、`EV_WRITE`、`EV_SIGNAL` 等)。 - `ev_callback`:事件触发时的回调函数。 - `ev_flags`:标志位(如持久化事件 `EV_PERSIST`)。 #### **事件循环(Event Loop)** - **核心引擎**:由 `event_base` 结构体管理,负责监听和分发事件。 - **多路复用封装**:通过 `eventop` 结构体抽象不同系统的 I/O 多路复用接口(如 `epoll` 在 Linux 下的实现)。 #### **缓冲区(evbuffer)** - 提供高效的数据缓冲区管理,用于处理网络数据的读写,支持自动扩容和链式存储。 --- ### 2. **事件处理流程** #### **初始化** - 创建 `event_base`,根据系统选择最佳的多路复用后端(如 `epoll`)。 ```c struct event_base *base = event_base_new(); ``` #### **注册事件** - 创建事件对象并绑定回调函数: ```c struct event *ev = event_new(base, fd, EV_READ | EV_PERSIST, callback, arg); ``` - 将事件添加到事件循环: ```c event_add(ev, timeout); // 可设置超时时间 ``` #### **事件循环** - 启动事件循环,阻塞等待事件触发: ```c event_base_dispatch(base); ``` - **内部流程**: 1. 通过 `epoll_wait`(或其他多路复用接口)监听事件。 2. 将就绪事件加入激活队列。 3. 遍历激活队列,执行对应的回调函数。 --- ### 3. **源码设计亮点** #### **Reactor 模式** - LibEvent 基于 **Reactor 模式**,核心是事件循环和事件分发器。所有 I/O 操作异步化,避免阻塞线程。 #### **多路复用抽象层** - 通过 `eventop` 结构体统一不同系统的 I/O 多路复用接口: ```c struct eventop { const char *name; void *(*init)(struct event_base *); // 初始化函数 int (*add)(void *, struct event *); // 添加事件 int (*del)(void *, struct event *); // 删除事件 int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件分发 // ... }; ``` - 具体实现如 `epoll.c`、`kqueue.c`,在运行时自动选择最优后端。 #### **时间缓存** - 避免频繁获取系统时间,通过缓存当前时间(`event_base` 中的 `tv_cache`)提升性能。 #### **高效定时器管理** - 使用**最小堆**(Min-Heap)管理定时器事件,确保快速获取最近的超时时间。 #### **信号处理** - 通过 socket pair 将信号事件转换为 I/O 事件,统一到事件循环中处理。 --- ### 4. **关键源码文件** - **event.c**:事件核心逻辑(注册、删除、回调执行)。 - **evmap.c**:管理文件描述符到事件的映射。 - **epoll.c** / **kqueue.c**:多路复用的具体实现。 - **evbuffer.c**:缓冲区管理。 - **http.c**:基于 LibEvent 的 HTTP 服务器实现(非核心)。 --- ### 5. **性能优化技巧** - **边缘触发(ET)模式**:使用 `epoll` 的 ET 模式减少事件触发次数。 - **内存池**:通过预分配内存减少动态内存分配开销。 - **零拷贝**:`evbuffer` 支持分散-聚集 I/O(`readv`/`writev`),减少数据拷贝。 --- ### 6. **学习建议** 1. **从官方示例入手**:LibEvent 提供 `sample` 目录,包含简单服务器示例。 2. **调试跟踪事件流**:通过 GDB 跟踪 `event_add`、`event_dispatch` 流程。 3. **阅读核心数据结构**:深入理解 `event`、`event_base`、`evbuffer` 的设计。 4. **对比其他库**:与 libuv、Boost.Asio 等库对比,理解事件驱动的共性与差异。 --- ### 7. **资源推荐** - **官方文档**: [libevent.org](https://libevent.org/) - **书籍**:《Libevent 深入浅出》(国内开发者编写的开源电子书) - **源码分析文章**:张亮《Libevent 源码分析》系列博客 通过剖析源码,你不仅能掌握 LibEvent 的设计精髓,还能深入理解事件驱动编程的底层原理,为开发高性能服务器打下基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值