libevent源码详解(四)应用流程详解

libevent应用流程

  • 1.调用event_base_new()创建自己的event_base。
  • 2.调用event_new()创建自己的事件。
  • 3.调用event_add将自己创建的event添加到event_base中。
  • 4.调用event_base_dispatch()进行循环等待事件发生。

其它的一些接口evsignal_add(),evtimer_add,evbuffer_setcb都是在event_add的基础上进行封装。应用的流程原理本上面没什么区别。
我们随便看一个例子,然后按照应用流程来解析几个关键接口的实现。

int tcp_connect_server(const char* server_ip, int port);  
void cmd_msg_cb(int fd, short events, void* arg);  
void socket_read_cb(int fd, short events, void *arg);  

int main(int argc, char** argv)  
{  
    if( argc < 3 )  
    {  
        printf("please input 2 parameter\n");  
        return -1;  
    }  

    //两个参数依次是服务器端的IP地址、端口号  
    int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));  
    if( sockfd == -1)  
    {  
        perror("tcp_connect error ");  
        return -1;  
    }  

    printf("connect to server successful\n");  
    struct event_base* base = event_base_new();  
    struct event *ev_sockfd = event_new(base, sockfd,  EV_READ | EV_PERSIST, socket_read_cb, NULL);  
    event_add(ev_sockfd, NULL);  

    //监听终端输入事件  
    struct event* ev_cmd = event_new(base, STDIN_FILENO,  EV_READ | EV_PERSIST, cmd_msg_cb,(void*)&sockfd);  
    event_add(ev_cmd, NULL);  
    event_base_dispatch(base);  
    printf("finished \n");  
    return 0;  
} 

event_base_new解析

event_base_new实现如下

struct event_base *
event_base_new(void)
{
    struct event_base *base = NULL;
    struct event_config *cfg = event_config_new();
    if (cfg) {
        base = event_base_new_with_config(cfg);
        event_config_free(cfg);
    }
    return base;
}

我们调用event_base_new创建的event_base是利用event_config设置的。我们看看event_config的结构,然后分析event_config_new()返回的event_config都有些什么性质。

event_config解析

event_config实现如下

struct event_config {
    TAILQ_HEAD(event_configq, event_config_entry) entries;//尾队列

    int n_cpus_hint;
    enum event_method_feature require_features;
    enum event_base_config_flag flags;
};

event_config_new()实现如下

struct event_config *
event_config_new(void)
{
    struct event_config *cfg = mm_calloc(1, sizeof(*cfg));

    if (cfg == NULL)
        return (NULL);

    TAILQ_INIT(&cfg->entries);

    return (cfg);
}

可以看到,event_config_new返回来的event_config,尾队列只有队列头没有entry,其它三个成员的值也都是0。基本没什么卵用。

event_config的一些操作

  • event_config_set_flag(struct event_config *cfg, int flag) //设置flags
  • event_config_avoid_method(struct event_config *cfg, const char *method)//设置entries
  • event_config_require_features(struct event_config *cfg,int features)//设置features
  • event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)//设置n_cpus_hint
    这四个函数分别设置了event_config的四个数据成员。如果我们在调用event_config_new后没有调用这四个函数设置返回的event_config,那么返回来的event_config就是空的,没什么卵用。

event_base_new_with_config解析

event_base_new_with_config实现如下.基本上就是初始化event_base的数据成员。

struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
    int i;
    struct event_base *base;
    int should_check_environment;

#ifndef _EVENT_DISABLE_DEBUG_MODE
    event_debug_mode_too_late = 1;
#endif

    if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
        event_warn("%s: calloc", __func__);
        return NULL;
    }
    detect_monotonic();
    gettime(base, &base->event_tv);

    //创建最小堆
    min_heap_ctor(&base->timeheap);
    //普通事件队列创建
    TAILQ_INIT(&base->eventqueue);
    base->sig.ev_signal_pair[0] = -1;
    base->sig.ev_signal_pair[1] = -1;
    base->th_notify_fd[0] = -1;
    base->th_notify_fd[1] = -1;

    event_deferred_cb_queue_init(&base->defer_queue);
    base->defer_queue.notify_fn = notify_base_cbq_callback;
    base->defer_queue.notify_arg = base;
    //调用event_config_new返回来的默认cfg的flags为0
    if (cfg)
        base->flags = cfg->flags;

    evmap_io_initmap(&base->io);//io映射
    evmap_signal_initmap(&base->sigmap);//signal映射。signal和io的映射看之前event_base的分析文章
    event_changelist_init(&base->changelist);

    base->evbase = NULL;

    should_check_environment =
        !(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
    /*
    eventops是一个全局数组,里面保存了后端各种multiplexer的封装。
    各个平台支持的multiplexer方法libevent会预先封装好,然后在编译时检测平台支持的方法,然后生成对应的宏来设置eventops的值。
    初始化的时候只需要从里面读出对应的handle然后赋值给event base中的成员函数base,
    base就可以调用到multiplexer中的方法。
    libevent强制每个multiplexer都要实现init,add,del,dispatch,dealloc五个接口。
    */
    for (i = 0; eventops[i] && !base->evbase; i++) {
        if (cfg != NULL) {
            /* 
            event_config_is_avoided_method会遍历cfg的尾队列nentries。
            默认情况下,调用event_config_new返回来的默认cfg的nentries为空,
            所以这个函数总是返回false
            */
            if (event_config_is_avoided_method(cfg,
                eventops[i]->name))
                continue;
            if ((eventops[i]->features & cfg->require_features)
                != cfg->require_features)
                continue;
        }

        /* also obey the environment variables */
        if (should_check_environment &&
            event_is_method_disabled(eventops[i]->name))
            continue;

        base->evsel = eventops[i];
        //初始化后端
        base->evbase = base->evsel->init(base);
    }

    if (base->evbase == NULL) {
        event_warnx("%s: no event mechanism available",
            __func__);
        base->evsel = NULL;
        event_base_free(base);
        return NULL;
    }

    if (evutil_getenv("EVENT_SHOW_METHOD"))
        event_msgx("libevent using: %s", base->evsel->name);

    /* 
    设置event_base的优先级,并初始化event_base中的就绪队列。
    就绪队列的个数跟初始化时的优先级有关系。
    默认情况下,event_base只有一个就绪活动队列。 
    */
    if (event_base_priority_init(base, 1) < 0) {
        event_base_free(base);
        return NULL;
    }

    /* 多线程设置 */

#ifndef _EVENT_DISABLE_THREAD_SUPPORT
    if (EVTHREAD_LOCKING_ENABLED() &&
        (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {
        int r;
        EVTHREAD_ALLOC_LOCK(base->th_base_lock,
            EVTHREAD_LOCKTYPE_RECURSIVE);
        base->defer_queue.lock = base->th_base_lock;
        EVTHREAD_ALLOC_COND(base->current_event_cond);
        r = evthread_make_base_notifiable(base);
        if (r<0) {
            event_warnx("%s: Unable to make base notifiable.", __func__);
            event_base_free(base);
            return NULL;
        }
    }
#endif

    return (base);
}

event_new解析

event_new()的作用就是设置事件的类型、回调函数、回调函数参数、绑定的IO事件fd,并设置事件的初始状态为EVLIST_INIT,然后返回一个新创建的事件。

具体的实现代码如下

struct event *
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
{
    struct event *ev;
    ev = mm_malloc(sizeof(struct event));
    if (ev == NULL)
        return (NULL);
    if (event_assign(ev, base, fd, events, cb, arg) < 0) {
        mm_free(ev);
        return (NULL);
    }

    return (ev);
}

int
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
    if (!base)
        base = current_base;

    _event_debug_assert_not_added(ev);

    ev->ev_base = base;

    ev->ev_callback = callback;//设置回调函数
    ev->ev_arg = arg;//设置回调函数参数
    ev->ev_fd = fd;
    ev->ev_events = events;//设置事件类型
    ev->ev_res = 0;
    ev->ev_flags = EVLIST_INIT;//设置事件初始状态
    ev->ev_ncalls = 0;
    ev->ev_pncalls = NULL;

    if (events & EV_SIGNAL) {
        if ((events & (EV_READ|EV_WRITE)) != 0) {
            event_warnx("%s: EV_SIGNAL is not compatible with "
                "EV_READ or EV_WRITE", __func__);
            return -1;
        }
        ev->ev_closure = EV_CLOSURE_SIGNAL;
    } else {
        if (events & EV_PERSIST) {
            evutil_timerclear(&ev->ev_io_timeout);
            ev->ev_closure = EV_CLOSURE_PERSIST;
        } else {
            ev->ev_closure = EV_CLOSURE_NONE;
        }
    }

    min_heap_elem_init(ev);

    if (base != NULL) {
        /* by default, we put new events into the middle priority */
        ev->ev_pri = base->nactivequeues / 2;
    }

    _event_debug_note_setup(ev);

    return 0;
}

event_add解析

先上源码。event_add主要是封装了event_add_internal。
event_add_internal的主要实现如下。略掉了一些检查代码。

static inline int
event_add_internal(struct event *ev, const struct timeval *tv,
    int tv_is_absolute)
{
    struct event_base *base = ev->ev_base;
    int res = 0;
    int notify = 0;
    event_queue_insert
    ...
    /*
    IO事件和signal事件会执行这个if里面的代码。timeout事件不执行。
    主要就是将信号值和IO的fd值映射到一个数组中。
    需要映射的原因是libevent支持将一个fd或者信号值同时响应多个回调函数。具体请看以前章节的分析。
    映射完毕后,调用event_queue_insert将事件插入到event_base的普通事件队列eventqueue中。并将其状态设置为EVLIST_INSERTED
    */
    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
        if (ev->ev_events & (EV_READ|EV_WRITE))
            res = evmap_io_add(base, ev->ev_fd, ev);
        else if (ev->ev_events & EV_SIGNAL)
            res = evmap_signal_add(base, (int)ev->ev_fd, ev);
        if (res != -1)
            event_queue_insert(base, ev, EVLIST_INSERTED);
        if (res == 1) {
            /* evmap says we need to notify the main thread. */
            notify = 1;
            res = 0;
        }
    }
    //timeout事件执行这个if里面的代码。IO事件和signal事件不执行。
    if (res != -1 && tv != NULL) {
        struct timeval now;
        int common_timeout;
        if (ev->ev_closure == EV_CLOSURE_PERSIST && !tv_is_absolute)
            ev->ev_io_timeout = *tv;
        //如果事件已经在最小堆中,则将其从堆中删除
        if (ev->ev_flags & EVLIST_TIMEOUT) {
            /* XXX I believe this is needless. */
            if (min_heap_elt_is_top(ev))
                notify = 1;
            event_queue_remove(base, ev, EVLIST_TIMEOUT);
        }

        /*如果这个timeout事件之前已经发生,然后又在自己的回调函数中调用event_add,那么将这个事件从就绪队列中删除*/
        if ((ev->ev_flags & EVLIST_ACTIVE) &&
            (ev->ev_res & EV_TIMEOUT)) {
            if (ev->ev_events & EV_SIGNAL) {
                if (ev->ev_ncalls && ev->ev_pncalls) {
                    /* Abort loop */
                    *ev->ev_pncalls = 0;
                }
            }

            event_queue_remove(base, ev, EVLIST_ACTIVE);
        }

        gettime(base, &now);

        common_timeout = is_common_timeout(tv, base);
        if (tv_is_absolute) {
            ev->ev_timeout = *tv;
        } else if (common_timeout) {
            struct timeval tmp = *tv;
            tmp.tv_usec &= MICROSECONDS_MASK;
            evutil_timeradd(&now, &tmp, &ev->ev_timeout);
            ev->ev_timeout.tv_usec |=
                (tv->tv_usec & ~MICROSECONDS_MASK);
        } else {
            evutil_timeradd(&now, tv, &ev->ev_timeout);
        }

        event_debug((
             "event_add: timeout in %d seconds, call %p",
             (int)tv->tv_sec, ev->ev_callback));
        //将事件加入最小堆
        event_queue_insert(base, ev, EVLIST_TIMEOUT);
        if (common_timeout) {
            struct common_timeout_list *ctl =
                get_common_timeout_list(base, &ev->ev_timeout);
            if (ev == TAILQ_FIRST(&ctl->events)) {
                common_timeout_schedule(ctl, &now, ev);
            }
        } else {
            //如果我们加入最小堆的事件处在堆的头部,则提早唤醒主线程
            if (min_heap_elt_is_top(ev))
                notify = 1;
        }
    }

    //唤醒主线程
    if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
        evthread_notify_base(base);
    }

Q1: timeout事件可以用min-head最小堆来存储,也可以用event_base中的类型为struct common_timeout_list 的common_timeout_queues数组来存储。那么什么时候该使用最小堆,什么时候使用common_timeout_queues队列呢?
A: 当一个timeout同时要响应上千个事件,同时调用上千个回调函数时,使用common_timeout_queues数组比最小堆高效得多。

Q2: 如何使用?
A: 创建timeout事件后,要调用event_base_init_common_timeout接口,然后再调用event_add

event_base_dispatch解析

event_base_dispatch主要是包装了event_base_loop函数。几个关键的函数做了注释。

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;
    struct timeval tv;
    struct timeval *tv_p;
    int res, done, retval = 0;

    /* Grab the lock.  We will release it inside evsel.dispatch, and again
     * as we invoke user callbacks. */
    EVBASE_ACQUIRE_LOCK(base, th_base_lock);

    if (base->running_loop) {
        event_warnx("%s: reentrant invocation.  Only one event_base_loop"
            " can run on each event_base at once.", __func__);
        EVBASE_RELEASE_LOCK(base, th_base_lock);
        return -1;
    }

    base->running_loop = 1;

    clear_time_cache(base);

    if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
        evsig_set_base(base);

    done = 0;

#ifndef _EVENT_DISABLE_THREAD_SUPPORT
    base->th_owner_id = EVTHREAD_GET_ID();
#endif

    base->event_gotterm = base->event_break = 0;

    while (!done) {
        base->event_continue = 0;

        /* Terminate the loop if we have been asked to */
        if (base->event_gotterm) {
            break;
        }

        if (base->event_break) {
            break;
        }
        //检查系统事件是否被人为往前设置,如果是,则将最小堆和common_timeout_queues中的时间戳减去相应的值
        timeout_correct(base, &tv);

        tv_p = &tv;
        if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
            timeout_next(base, &tv_p);
        } else {
            /*
             * if we have active events, we just poll new events
             * without waiting.
             */
            evutil_timerclear(&tv);
        }

        //event_base中没有普通等待事件和就绪事件,则直接结束循环
        if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
            event_debug(("%s: no events registered.", __func__));
            retval = 1;
            goto done;
        }

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

        clear_time_cache(base);

        res = evsel->dispatch(base, tv_p);

        if (res == -1) {
            event_debug(("%s: dispatch returned unsuccessfully.",
                __func__));
            retval = -1;
            goto done;
        }

        update_time_cache(base);
        //处理要就绪的timeout事件
        timeout_process(base);

        if (N_ACTIVE_CALLBACKS(base)) {
            //处理就绪的IO事件和signal事件
            int n = event_process_active(base);
            if ((flags & EVLOOP_ONCE)
                && N_ACTIVE_CALLBACKS(base) == 0
                && n != 0)
                done = 1;
        } else if (flags & EVLOOP_NONBLOCK)
            done = 1;
    }
    event_debug(("%s: asked to terminate loop.", __func__));

done:
    clear_time_cache(base);
    base->running_loop = 0;

    EVBASE_RELEASE_LOCK(base, th_base_lock);

    return (retval);
}

你可能感兴趣的:(源码解析)