(九)事件的激活與調度

前言

我們在上一個小節中分析了事件主循環的整個過程以及event_base_once函數。可能信息量有點大,這一小節,我們把event.c剩下的一部分重要的函數分析分析。

event_dispatch

我們再回到討論主循環這個話題來,在第1小節給的例子裏面最後調用了event_dispatch,它其實幹的就是事件主循環的事,只是做了幾層封裝而已。
下面我們來看一看。

event_dispatch

int
event_dispatch(void)
{
                return (event_loop(0));
}

裏面調用了event_loop函數,已經和event_base_loop很接近了。我們再往下看。

event_loop

int
event_loop(int flags)
{
                return event_base_loop(current_base, flags);
}

對,event_dispatch的實質就是調用event_base_loop(current_base, 0)
那麼這個flags到底可以取什麼值呢?我們在event_base_loop中已經發現它可以取EVLOOP_ONCEEVLOOP_NONBLOCK
事實上,它也就只能取這兩個值,用於設置事件循環的標識。在event.h中定義。

#define EVLOOP_ONCE     0x01    /**< Block at most once. */
#define EVLOOP_NONBLOCK 0x02    /**< Do not block. */

event_active

這個函數的作用是手動激活事件

void
event_active(struct event *ev, int res, short ncalls)
{
        /* We get different kinds of events, add them together */
                //當前事件已經激活了則將激活類型與之前的做或操作並返回
                if (ev->ev_flags & EVLIST_ACTIVE) {
                ev->ev_res |= res;
                return;
        }
        //設置激活事件的類型以及調用次數
        ev->ev_res = res;
        ev->ev_ncalls = ncalls;
        ev->ev_pncalls = NULL;
        //插入到激活鏈表中去
        event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
}

你可以在使用libevent的時候調用該函數來手動激活一個事件,並且可以自己指定激活事件的原因。
不過其實在監聽事件中,如果該事件滿足條件被觸發了,那麼將其添加到激活鏈表中去的函數還是event_active

event_process_active

前面說了這麼多,真正來調度激活事件的函數就是它了。

/*
 * Active events are stored in priority queues.  Lower priorities are always
 * process before higher priorities.  Low priority events can starve high
 * priority ones.
 */
//根據libevent自帶的英文註釋來看,激活事件是按優先級來調度的,低優先級先被調度並且低優先級可能會使高優先級一直得不到調度
static void
event_process_active(struct event_base *base)
{
        struct event *ev;
        struct event_list *activeq = NULL;
        int i;
        short ncalls;
        //遍歷優先級鏈表,從0開始,找到優先級最低的不爲空的激活鏈表來調度
        for (i = 0; i < base->nactivequeues; ++i) {
                //如果該優先級所在的鏈表不爲空,那就證明有激活事件可以調度,則跳出循環,進行處理
                if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
                        activeq = base->activequeues[i];
                        break;
                }
        }

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

                /* Allows deletes to work */
                //由於之前記錄了調用次數,這裏就開始正式調用了
                ncalls = ev->ev_ncalls;
                ev->ev_pncalls = &ncalls;
                while (ncalls) {
                        ncalls--;
                        ev->ev_ncalls = ncalls;
                        (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
                        if (event_gotsig || base->event_break)
                                return;
                }
        }
}

該函數的邏輯還算清晰,也不是特別長。
主要需要關注的就是activequeues是個二級指針,不要搞忘了並且低優先級優先被調度。
最後處理的時候,如果事件類型中設置了爲EV_PERSIST永久性事件,則激活之後只將其從激活鏈表中移出;如果沒有設置,則註銷,還記得註銷的時候幹了什麼嗎?如果記不得了,翻到第7小節再看看。

小結

在本小節我們瞭解了event.c剩下的一些主要的函數,相信你對事件主循環更加了解了。接下來,我們就可以開始進入之前一直省略的信號以及定時部分了,看看它們是如何集成到事件主循環中的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章