libevent源码学习-----event_base事件循环
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 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);}}return c; } /** 處理激活/超時event的主要函數* 首先根據event是否是永久event判斷是否需要將event從base的所有隊列中刪除* 如果是永久隊列,則只需要從base的激活隊列中刪除* 然后才開始調用event的回調函數** 注意:此處需要處理具有超時時間的event,因為最小堆中記錄的是絕對時間,* 所以如果這個event是具有超時時間的event,那么它是在從base中所有隊列中刪除后才加入到* 激活隊列中的,見timeout_process()* 所以需要再將其添加到base中,重新調用event_add_internal即可,見event_persist_closure* * 返回處理了多少個非內部event*/ static int event_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的所有隊列中刪除,包括激活隊列, 因為只處理一次 */elseevent_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就僅僅是調用回調函數而已。
整體思路很容易理清,重點是細節方面,需要仔細琢磨
總結
以上是生活随笔為你收集整理的libevent源码学习-----event_base事件循环的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: libevent源码学习-----eve
- 下一篇: 每天一道LeetCode-----给定序