Libevent源码分析—event_base_dispatch()

我们知道libevent是一个Reactor模式的事件驱动的网络库。

到目前为止,我们已经看了核心的event和event_base结构体的源码,看了初始化这两个结构体的源码,看了注册event的源码,也将event注册到I/O多路复用监听的事件上了。现在准备工作都做好了,下面就是看运行时的主循环了,在这个主循环中,是如何检测事件、分发事件、调用事件的回调函数的。这一步就是libevent的核心框架流程了。

Reactor模式中的Event、Event Handler、Reactor目前都完成了,下面就剩Event Demultiplexer了。

这一步通过event_base_dispatch()完成

int
event_base_dispatch(struct event_base *event_base)
{
  return (event_base_loop(event_base, 0));  //调用event_base_loop()
}

可以看到,该函数只是做了调用event_base_loop()这一个动作,所以工作实际是在函数event_base_loop()内完成的。

event_base_loop()

该函数完成以下工作:

1.信号标记被设置,则调用信号的回调函数

2.根据定时器最小时间,设置I/O多路复用的最大等待时间,这样即使没有I/O事件发生,也能在最小定时器超时时返回。

3.调用I/O多路复用,监听事件,将活跃事件添加到活跃事件链表中

4.检查定时事件,将就绪的定时事件从小根堆中删除,插入到活跃事件链表中

5.对event_base的活跃事件链表中的事件,调用event_process_active()函数,在该函数内调用event的回调函数,优先级高的event先处理。

该函数内部调用了eventop.dispatch()监听事件,event_sigcb函数指针处理信号事件,timeout_process()将超时的定时事件加入到活跃事件链表中,event_process_active()处理活跃事件链表中的事件,调用相应的回调函数。

int
event_base_loop(struct event_base *base, int flags)
{
    const struct eventop *evsel = base->evsel;
    void *evbase = base->evbase;  //event_base的I/O多路复用
    struct timeval tv;
    struct timeval *tv_p;
    int res, done;
    /* clear time cache */
    //清空时间缓存
    base->tv_cache.tv_sec = 0;
    //处理Signal事件时,指定信号所属的event_base
    if (base->sig.ev_signal_added)
        evsignal_base = base;
    done = 0;
    while (!done) {  //进入事件主循环
        /* Terminate the loop if we have been asked to */
        //设置event_base的标记,以表明是否需要跳出循环
        if (base->event_gotterm) {  //event_loopexit_cb()可设置
            base->event_gotterm = 0;
            break;
        }
        if (base->event_break) {  //event_base_loopbreak()可设置
            base->event_break = 0;
            break;
        }
        /* You cannot use this interface for multi-threaded apps */
        //当event_gotsig被设置时,则event_sigcb就是信号处理的回调函数
        while (event_gotsig) {
            event_gotsig = 0;
            if (event_sigcb) {
                res = (*event_sigcb)();  //调用信号处理的回调函数
                if (res == -1) {
                    errno = EINTR;
                    return (-1);
                }
            }
        }
        timeout_correct(base, &tv);  //校准时间
        tv_p = &tv;
        //根据定时器堆中最小超时时间计算I/O多路复用的最大等待时间tv_p
        if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
            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;
        //调用I/O多路复用,监听事件
        res = evsel->dispatch(base, evbase, tv_p);
        if (res == -1)
            return (-1);
        //将time cache赋值为当前系统时间
        gettime(base, &base->tv_cache);

        //检查定时事件,将就绪的定时事件从小根堆中删除,插入到活跃事件链表中
        timeout_process(base);
        if (base->event_count_active) {
            //处理event_base的活跃链表中的事件
            //调用event的回调函数,优先级高的event先处理
            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);
}

epoll_dispatch()

在上面我们看到,event_base_loop()中通过I/O多路复用的dispatch()函数完成监听事件功能。在之前的event_init()中我们看到,通过遍历eventops数组,从中选择一个I/O多路复用机制,所以不同的I/O多路复用机制有不同的eventop结构体,相应的也就有不同的dispatch()函数。下面,再次看下eventop结构体(event-internal.h)

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 *);  //事件分发
        void (*dealloc)(struct event_base *, void *);  //注销,释放资源
        /* set if we need to reinitialize the event base */
        int need_reinit;
};

在event_add()中通过add()成员函数注册event到监听事件中,现在在event_base_loop()中通过dispatch()成员函数监听事件。

libevent支持多种I/O多路复用机制,下面先看下epoll的eventop结构体(epoll.c)

const struct eventop epollops = {
    "epoll",
    epoll_init,
    epoll_add,
    epoll_del,
    epoll_dispatch,
    epoll_dealloc,
    1 /* need reinit */
};

然后看下epoll的dispatch()函数(epoll.c)

从下面源码可见,epoll_dispatch()的工作主要有:

1.调用epoll_wait()监听事件

2.如果有信号发生,调用evsignal_process()处理信号

3.将活跃的event根据其活跃的类型注册到活跃事件链表上

4.如果events数组大小不够,则重新分配为原来2倍大小

static int
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
    struct epollop *epollop = arg;
    struct epoll_event *events = epollop->events;
    struct evepoll *evep;
    int i, res, timeout = -1;
    if (tv != NULL)
        timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;  //转换为微米
    if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {  //设置最大超时时间
        /* Linux kernels can wait forever if the timeout is too big;
         * see comment on MAX_EPOLL_TIMEOUT_MSEC. */
        timeout = MAX_EPOLL_TIMEOUT_MSEC;
    }
    res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);  //监听事件发生
    if (res == -1) {
        if (errno != EINTR) {
            event_warn("epoll_wait");
            return (-1);
        }
        evsignal_process(base);  //由于Signal事件发生中断,处理Signal事件
        return (0);
    } else if (base->sig.evsignal_caught) {
        evsignal_process(base);  //有Signal事件发生,处理Signal事件
    }
    event_debug(("%s: epoll_wait reports %d", __func__, res));
    for (i = 0; i < res; i++) {  //处理活跃事件
        int what = events[i].events;  //活跃类型
        struct event *evread = NULL, *evwrite = NULL;
        int fd = events[i].data.fd;  //event的文件描述符
        if (fd < 0 || fd >= epollop->nfds)
            continue;
        evep = &epollop->fds[fd];
        if (what & (EPOLLHUP|EPOLLERR)) {  //判断epoll的events类型,并找到注册的event
            evread = evep->evread;
            evwrite = evep->evwrite;
        } else {
            if (what & EPOLLIN) {
                evread = evep->evread;
            }
            if (what & EPOLLOUT) {
                evwrite = evep->evwrite;
            }
        }
        if (!(evread||evwrite))
            continue;

        //添加event到活跃事件链表中
        if (evread != NULL)
            event_active(evread, EV_READ, 1);
        if (evwrite != NULL)
            event_active(evwrite, EV_WRITE, 1);
    }
    //如果注册的事件全部变为活跃,则增大events数组为原来两倍
    if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
        /* We used all of the event space this time.  We should
           be ready for more events next time. */
        int new_nevents = epollop->nevents * 2;
        struct epoll_event *new_events;
        new_events = realloc(epollop->events,
            new_nevents * sizeof(struct epoll_event));
        if (new_events) {
            epollop->events = new_events;
            epollop->nevents = new_nevents;
        }
    }
    return (0);
}

event_process_active()

好了,现在活跃的I/O事件、定时器事件已经全部添加到活跃事件链表中了。下面就开始调用这些event的回调函数进行处理了,这步是在event_base_loop()中调用event_process_active()来完成的。

该函数从event_base的activequeueus链表数组上取出一个链表;对该链表上的event调用回调函数;优先调用优先级值最小的event

/*
 * Active events are stored in priority queues.  Lower priorities are always
 * process before higher priorities.  Low priority events can starve high
 * priority ones.
 */
static void
event_process_active(struct event_base *base)
{
    struct event *ev;
    struct event_list *activeq = NULL;
    int i;
    short ncalls;
    for (i = 0; i < base->nactivequeues; ++i) {  //取出第一个活跃链表
        if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
            activeq = base->activequeues[i];
            break;
        }
    }
    assert(activeq != NULL);

    //优先处理优先级值最小的event
    for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
        if (ev->ev_events & EV_PERSIST)
            event_queue_remove(base, ev, EVLIST_ACTIVE);  //是持久事件,则从活跃链表移除
        else
            event_del(ev);  //不是持久事件,则直接删除该事件

        /* Allows deletes to work */
        ncalls = ev->ev_ncalls;
        ev->ev_pncalls = &ncalls;
        while (ncalls) {
            ncalls--;
            ev->ev_ncalls = ncalls;
            //调用该event的回调函数,event.ev_res保存返回值
            (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
            if (event_gotsig || base->event_break) {
                  ev->ev_pncalls = NULL;
                return;
            }
        }
        ev->ev_pncalls = NULL;
    }
}
时间: 2024-10-14 18:15:38

Libevent源码分析—event_base_dispatch()的相关文章

[libevent源码分析] event_base_dispatch

分析下事件循环 event_base_dispatch int event_base_dispatch(struct event_base *event_base) { return (event_base_loop(event_base, 0)); } int event_base_loop(struct event_base *base, int flags) { const struct eventop *evsel = base->evsel; void *evbase = base->

Libevent源码分析 (1) hello-world

Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libevent的源码,凡是我觉得有必要的内容均一一记录,与君共勉. 首先要说说什么是libevent: libevent是一个事件通知库,libevent API提供一种机制使得我们可以在一个文件描述符(file descriptor)发生特定事件时或者timeout发生时执行指定的回调函数.libevent意

Libevent源码分析-timer和signal处理

timer处理 Signal处理 timerfd和signalfd timerfd signalfd timer处理 在Libevent源码分析-event处理流程中,使用了定时器,来看一下源码: evtimer_set(&ev, time_cb, NULL);//设置定时器事件 其中evtimer_set是个宏定义 #define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) //event_set原型 void ev

【转】libevent源码分析

libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络库.Libevent有几个显著的亮点: (1)事件驱动(event-driven),高性能:(2)轻量级,专注于网络,不如 ACE 那么臃肿庞

[libevent源码分析] event_init

libevent采用的是经典的reactor网络框架,集成了信号.定时.网络事件于一体 首先对event_init进行源码剖析 event_init 主要创建event_base对象, struct event_base { const struct eventop *evsel; //lievent支持select epoll kequeue..等网络api,包括init.add.del.dispatch的接口,每种网络框架都支持 void *evbase; //支持相应网络api的 结构对象

[libevent源码分析] event_add

event_add 把event往当前event中的ev_base追加,如果需要定时,那么tv不能为空 int event_add(struct event *ev, const struct timeval *tv) { struct event_base *base = ev->ev_base; //event_add 会把event加入到他的ev_base成员里 const struct eventop *evsel = base->evsel; //对应linux的epoll相关函数

libevent源码分析-event

event结构 event相关接口 Libevent对event的管理 event结构 event是Reactor模式中的最重要的组件.它包含了了一个句柄fd,并设置监听这个句柄上的哪些事件(读/写等),设置了对应的函数指针,在事件到来时,回调函数指针来处理事件. 先看一下event的结构.它位于include/event2/event_struct.h中 struct event { TAILQ_ENTRY(event) ev_active_next; TAILQ_ENTRY(event) e

[libevent源码分析] event_set

libevent使用event来封装网络事件回调,参数.fd...等一些信息,函数很简单 void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg) { /* Take the current base - caller needs to set the real base later */ ev->ev_base = current_base; /

Libevent源码分析系列【转】

转自:https://www.cnblogs.com/zxiner/p/6919021.html 1.使用libevent库 源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了,然后去看底层相应的源码,这样比较有条理,自上向下掌握.下面用libevent库写个程序,每隔1秒输出一行信息. test.c 2.event, event_base 经过第1步,下面开始看上面程序中的每一部分的源码.首先是两个核心结构体event和event_base event event_base源码