事件处理框架event_base

事件处理框架对应于Reactor模式中的Reactor管理器,提供了事件注册、注销等接口。

  1. 事件处理框架对应event_base结构体,这也是Libevent的核心。
struct event_base {
    // Libevent选择的io多路复用模型
    const struct eventop* evsel;
    //调用i/o模型evsel->init()返回的变量,相当于io多路复用模型上下文
    void* evbase;
    //当前注册的事件总数
    int event_count;        /* counts number of total events */
    //激活事件总数
    int event_count_active; /* counts number of active events */

    int event_gotterm;      /* Set to terminate loop 正常退出dispatch*/
    int event_break;        /* Set to terminate loop immediately 马上退出dispatch*/

    /* active event management */
    // 一个二级指针,等同于一个数组,数组中的每个元素都指向一个链表,链表的每个元素都指向优先级为priority的激活事件
    struct event_list** activequeues;
    // 优先级队列数
    int nactivequeues;

    /* signal handling info */
    struct evsignal_info sig; //信号相关
    // 保存所有的注册事件
    struct event_list eventqueue; 
    struct timeval event_tv;
    // 管理注册的定时事件的小根堆
    struct min_heap timeheap;
    // 用于时间管理的变量
    struct timeval tv_cache;
};
  1. event_base的初始化:使用event_base_new函数,主要功能是初始化小根堆、初始化保存注册事件的队列、初始化IO多路复用上下文、初始化一个优先级队列,这个队列保存相同优先级的事件。
  2. 事件的注册:事件处理框架提供事件注册接口event_add,用于注册EV_READ、EV_WRITE、EV_TIMEOUT事件,事件注册完以后,会放入相应的保存注册事件的队列中。如下所示:
/**
*   ev:使用event_set()接口进行初始化好的事件
*   tv:等待事件发生的时长
*/
int
event_add(struct event* ev, const struct timeval* tv)
{   
    // 事件所属的event_base
    struct event_base* base = ev->ev_base;
    // Libevent选择的io多路复用模型
    const struct eventop* evsel = base->evsel; 
    // io多路复用模型上下文
    void* evbase = base->evbase;
    int res = 0;

    /*
     * prepare for timeout insertion further below, if we get a
     * failure on any step, we should not change any state.
     */
    // tv不为空,则准备注册超时事件
    //预先在二叉堆中预留一个空位给新添加的超时事件
    if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
        if (min_heap_reserve(&base->timeheap,
                             1 + min_heap_size(&base->timeheap)) == -1)
            return (-1);  /* ENOMEM == errno */
    }

    
    // IO读写或者信号事件未插入已注册或者已激活队列中,则
    // 1. 首先对于IO读写事件,将调用底层的epoll_ctrl将套接字文件描述符挂载到epoll实例上;
    // 对于信号事件则设置信号处理器,并且向epoll注册监听EV_READ | EV_PERSIST的ev_signal事件
    // 2. 将IO读写事件和ev_signal事件插入到对应的注册队列中
    if ((ev->ev_events & (EV_READ | EV_WRITE | EV_SIGNAL)) && 
        !(ev->ev_flags & (EVLIST_INSERTED | EVLIST_ACTIVE))) { 
        res = evsel->add(evbase, ev);
        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.
         */
        // 如果定时器已经在定时器小根堆中,删除旧的
        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. */
        // 如果定时器在激活队列中,立即将其从active队列移除
        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;
            }

            event_queue_remove(base, ev, EVLIST_ACTIVE);
        }

        // 得到定时器触发时间点
        gettime(base, &now);
        evutil_timeradd(&now, tv, &ev->ev_timeout);

        event_debug((
                        "event_add: timeout in %ld seconds, call %p",
                        tv->tv_sec, ev->ev_callback));
        // 插入定时器到小根堆中
        event_queue_insert(base, ev, EVLIST_TIMEOUT);
    }

    return (res);
}
  1. 事件的注销:event_del接口

事件处理主循环

事件处理主循环将根据操作系统提供的IO多路复用机制执行事件主循环,对已经注册的就绪事件(有事件发生)调用注册事件时指定的事件处理程序来处理事件。

  1. Libevent的事件主循环主要通过event_base_loop函数完成。
int
event_base_loop(struct event_base* base, int flags)
{
    // Libevent选择的IO多路复用机制
    const struct eventop* evsel = base->evsel;
    // io多路复用模型上下文
    void* evbase = base->evbase;
    struct timeval tv;
    struct timeval* tv_p;
    int res, done;

    /* clear time cache */
    base->tv_cache.tv_sec = 0;

    if (base->sig.ev_signal_added)
        evsignal_base = base;
    done = 0;
    while (!done) {
        /* Terminate the loop if we have been asked to */
        if (base->event_gotterm) {
            base->event_gotterm = 0;
            break;
        }

        if (base->event_break) {
            base->event_break = 0;
            break;
        }

        // 校正系统时间
        timeout_correct(base, &tv);
        // epoll_wait的阻塞时间
        tv_p = &tv; 
        // 无激活事件并且未设置EVLOOP_NONBLOCK标志
        if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
            //获取到base->timeheap最小堆中最先超时的计时器,用于计算epoll_wait等待事件就绪的时长
            timeout_next(base, &tv_p);
        } else {
            /*
             * if we have active events, we just poll new events
             * without waiting.
             */
            evutil_timerclear(&tv); // 将阻塞超时时间清零
        }

        /* If we have no events, we just exit */
        //事件循环中没有要监听的事件退出
        if (!event_haveevents(base)) {
            event_debug(("%s: no events registered.", __func__));
            return (1);
        }

        /* update last old time */
        gettime(base, &base->event_tv);

        /* clear time cache */
        base->tv_cache.tv_sec = 0;
        // 调用epoll_wait等待事件就绪,
        res = evsel->dispatch(base, evbase, tv_p);

        if (res == -1)
            return (-1);
        // base->tv_cache为当前系统时间
        gettime(base, &base->tv_cache);
        //处理已经触发的计时器事件,通过获取小根堆堆顶与当前时间比较
        //如果堆顶时间小于当前时间说明计时器已经触发,将event插入到
        //base->activequeues激活队列,将从小根堆中删除
        timeout_process(base);

        // active队列(激活事件队列)有事件则统一进行处理回调
        if (base->event_count_active) {
            event_process_active(base);
            if (!base->event_count_active && (flags & EVLOOP_ONCE))
                done = 1;
        } else if (flags & EVLOOP_NONBLOCK)
            done = 1;
    }

    /* clear time cache */
    base->tv_cache.tv_sec = 0;

    event_debug(("%s: asked to terminate loop.", __func__));
    return (0);
}