zoukankan      html  css  js  c++  java
  • libevent2源码分析之四:libevent2的消息泵

    Dispatch类似于一个消息泵,在一个死循环中,不停地检查IO的状态(可以想像成不断从消息队列中读取消息),将状态的改变变成事件,再进行事件的响应。

     

    主要代码如下:

    [event.c]

    int

    event_base_loop(struct event_base *base, int flags)

    {

        const struct eventop *evsel = base->evsel;

    ...

        done = 0;

    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;

            }

    ...

    /// 在这里调用底层的 dispatch

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

            ...

            if (N_ACTIVE_CALLBACKS(base)) {

    /// 处理和响应事件

                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;

        }

     

    done:

    ...

        return (retval);

    }

     

    如果不是一次性事件,由while (!done)是一个死循环。这样可以反复地调用底层的dispatch去获取fd的状态. dispatch之后,又调用了event_process_active这个重要的函数。后面会讲到,它的作用是调用event绑定的回调函数。

     

    select_dispatch

     

    其主工作是调用了select函数,然后调用 evmap_io_active 触发事件。这个函数的实现有一个小技巧值得学习就是如何对随时变化的集合进行操作。

     

    static int select_dispatch(struct event_base *base, struct timeval *tv)

    {

        int res=0, i, j, nfds;

        struct selectop *sop = base->evbase;

    ///...

    /// 主要工作是调整 event_XXXset_out 与 event_XXXset_in, select()基于前者,而对set的修改基于后者,在select之前有必要做一次同步

        memcpy(sop->event_readset_out, sop->event_readset_in,

               sop->event_fdsz);

        memcpy(sop->event_writeset_out, sop->event_writeset_in,

               sop->event_fdsz);

        nfds = sop->event_fds+1;

        EVBASE_RELEASE_LOCK(base, th_base_lock);

        res = select(nfds, sop->event_readset_out,

            sop->event_writeset_out, NULL, tv);

        EVBASE_ACQUIRE_LOCK(base, th_base_lock);

    ...

        i = random() % nfds;

        for (j = 0; j < nfds; ++j) {

            if (++i >= nfds)

                i = 0;

            res = 0;

            if (FD_ISSET(i, sop->event_readset_out))

                res |= EV_READ;

            if (FD_ISSET(i, sop->event_writeset_out))

                res |= EV_WRITE;

            if (res == 0)

                continue;

            evmap_io_active(base, i, res);

        }

        check_selectop(sop);

        return (0);

    }

    上面的代码中,先调用select获取fd的状态。注意一次并非返回一个fd,而操作两个fd的列表:读的fd列表,写的fd列表。接下来对select之后的队列进行操作,如果有读、写事件,则调用evmap_io_active在一个io map中登记,实际是将事件放到active队列中。此时只是登记,还没有调起事件对应的回调函数。

    event_process_active

    接下来分析如何调用事件对应的回调函数。回到event_base_dispatch(event_base_loop),从调用底层dispatch之后继续分析。到了最后调用了event_process_active。这个函数就是处理就绪队列的,就是它的内部实现调用了event关联的回调函数。event_process_active的处理流程是这样的:

    event_process_active -> event_process_active_single_queue->(*ev->callback)(...)

    [event.c]

    static int event_process_active(struct event_base *base)

    {

        /* Caller must hold th_base_lock */

        struct event_list *activeq = NULL;

        int i, c = 0;

        for (i = 0; i < base->nactivequeues; ++i) {

            if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {

                base->event_running_priority = i;

                activeq = &base->activequeues[i];

                c = event_process_active_single_queue(base, activeq);

                if (c < 0) {

                    base->event_running_priority = -1;

                    return -1;

                } else if (c > 0)

                    break; /* Processed a real event; do not

                        * consider lower-priority events */

                /* If we get here, all of the events we processed

                 * were internal.  Continue. */

            }

        }

        event_process_deferred_callbacks(&base->defer_queue,&base->event_break);

        base->event_running_priority = -1;

        return c;

    }

     遍历activequeues,对每个项(一个队列),处理此项。

    static int

    event_process_active_single_queue(struct event_base *base,

        struct event_list *activeq)

    {

        struct event *ev;

        int count = 0;

        EVUTIL_ASSERT(activeq != NULL);

        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_internal(ev);

            switch (ev->ev_closure) {

            case EV_CLOSURE_SIGNAL:

                event_signal_closure(base, ev);

                break;

            case EV_CLOSURE_PERSIST:

                event_persist_closure(base, ev);

                break;

            default:

            case EV_CLOSURE_NONE:

                EVBASE_RELEASE_LOCK(base, th_base_lock);

                (*ev->ev_callback)(

                    ev->ev_fd, ev->ev_res, ev->ev_arg);

                 break;

            }

    ...

            if (base->event_break)

                return -1;

            if (base->event_continue)

                break;

        }

        return count;

    }

  • 相关阅读:
    10-JavaScript 条件语句
    9-JavaScript 比较
    8-JavaScript 运算符
    6-JavaScript 事件
    Sum Problem 重定向文件的使用
    Calculate A + B.
    Vue中computed的本质及与methods的区别
    外部文件使用django的models
    DjangoURL反向解析
    字符串格式化的方式
  • 原文地址:https://www.cnblogs.com/qkhh/p/3679468.html
Copyright © 2011-2022 走看看