libevent源碼學習-----event_base事件迴圈

來源:互聯網
上載者:User

event_base是libevent的事件驅動,也是Reactor模式的直接體現。任何使用libevent的代碼最開始都需要建立一個base,之後的任何介面函數都和這個base關聯著,下面是struct event_base的定義

struct event_base {    /* io多工函數的統一介面 */    const struct eventop *evsel;    /* io多工函數的資料 */    void *evbase;    /* 訊號處理的統一介面 */    const struct eventop *evsigsel;    /* 訊號處理的資料 */    struct evsig_info sig;    /* 註冊到base中的event,不包括內部event */    int event_count;    /* 啟用的event個數 */    int event_count_active;    /* 儲存通訊端/描述符以及對應的事件的map */    struct event_io_map io;    /* 儲存訊號的map */    struct event_signal_map sigmap;    /* 註冊隊列 */    struct event_list eventqueue;    /* 啟用隊列 */    struct event_list *activequeues;    /* 最小堆 */    struct min_heap timeheap;    /* ... */};

struct event_base中主要包括上述變數,io多工操作,訊號操作,各種map,隊列,最小堆等等
其他的部分就是在程式設計過程中進行的各種判斷,各種標誌多一些
event_base的初始化是通過內部調用event_base_with_new_config實現的,函數意思是帶有一些配置構造一個event_base,配置主要包括的就是使用者不想要libevent使用的io複用函數名字。
函數中主要還是各種初始化,然後選擇合適的io複用函數等

使用者通過event_base_dispatch函數開啟事件驅動的主迴圈,內部調用event_base_loop執行無線迴圈,也沒有什麼特別的 選擇io複用函數的阻塞時間長度 調用io函數 判斷最小堆中的event 處理所有啟用的event

下面主要看一下如何處理啟用event

/* * 由base主迴圈調用,用來處理在base的啟用隊列中的event, * 根據優先順序遍曆base的啟用隊列數組,對於每一個隊列,調用event_process_active_single_queue處理 */static intevent_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);        }    }    return c;}
/* * 處理啟用/逾時event的主要函數 * 首先根據event是否是永久event判斷是否需要將event從base的所有隊列中刪除 * 如果是永久隊列,則只需要從base的啟用隊列中刪除 * 然後才開始調用event的回呼函數 * * 注意:此處需要處理具有逾時時間的event,因為最小堆中記錄的是絕對時間, * 所以如果這個event是具有逾時時間的event,那麼它是在從base中所有隊列中刪除後才加入到 * 啟用隊列中的,見timeout_process() * 所以需要再將其添加到base中,重新調用event_add_internal即可,見event_persist_closure *  * 返回處理了多少個非內部event */static intevent_process_active_single_queue(struct event_base *base,    struct event_list *activeq){    struct event *ev;    int count = 0;    for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {        /* 如果是永久event,只需要從啟用隊列中刪除 */        if (ev->ev_events & EV_PERSIST)            event_queue_remove(base, ev, EVLIST_ACTIVE);        /* 否則就要從base的所有隊列中刪除,包括啟用隊列, 因為只處理一次 */        else            event_del_internal(ev);        /*          * 如果是訊號或者永久event需要單獨處理,而其他event在刪除完之後直接調用回呼函數就可以了         * 以後就不再管這個event了,因為只處理一次         * ev_closure用於判斷是否是永久/訊號event,在event_new中賦值         */        switch (ev->ev_closure) {        case EV_CLOSURE_SIGNAL:        /* 訊號的特殊處理,調用使用者的訊號處理函數 */            event_signal_closure(base, ev);            break;        case EV_CLOSURE_PERSIST://io event        /* 處理io事件,主要是特別處理有逾時時間的event,需要重新計算絕對時間,然後調用回呼函數 */            event_persist_closure(base, ev);            break;        }    }    return count;}

函數處理當前隊列中的每一個event,首先將其從啟用隊列中刪除,如果是一次性事件,則從base中刪除
然後就根據訊號/其他永久io事件調用相應的處理函數

總結
event_base是整個事件驅動的載體,但是使用時僅僅需要event_base_new,和event_base_dispatch即可,內部實現了對事件的監控,對逾時event的統一處理,對所有啟用event按優先順序處理,處理時再細分是永久/一次性event,io/signal event等。這種先統一再分散的好處是可以在一個函數中統一處理啟用event,實現更好的封裝介面,也更容易理清思路。
對訊號的處理需要特別注意,因為不是一有訊號發生馬上調用使用者的回呼函數,中間其實進行了兩次base迴圈,這是為了將訊號統一到event的結果,不然就直接sigaction綁定不是很好。
具有逾時時間的event在處理時也是需要注意的部分,需要重新計算逾時時間,然後使用event_add_internal重新添加(event在timeout_process已經被刪除了),這就回到了event_add調用的event_add_internal上,如果不是逾時event就僅僅是調用回呼函數而已。
整體思路很容易理清,重點是細節方面,需要仔細琢磨

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.