libevent源码学习-----event_base事件循环
event_base是libevent的事件驅(qū)動,也是Reactor模式的直接體現(xiàn)。任何使用libevent的代碼最開始都需要創(chuàng)建一個base,之后的任何接口函數(shù)都和這個base關(guān)聯(lián)著,下面是struct event_base的定義
struct event_base {/* io多路復(fù)用函數(shù)的統(tǒng)一接口 */const struct eventop *evsel;/* io多路復(fù)用函數(shù)的數(shù)據(jù) */void *evbase;/* 信號處理的統(tǒng)一接口 */const struct eventop *evsigsel;/* 信號處理的數(shù)據(jù) */struct evsig_info sig;/* 注冊到base中的event,不包括內(nèi)部event */int event_count;/* 激活的event個數(shù) */int event_count_active;/* 存儲套接字/描述符以及對應(yīng)的事件的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多路復(fù)用操作,信號操作,各種map,隊列,最小堆等等
其他的部分就是在程序設(shè)計過程中進行的各種判斷,各種標(biāo)志多一些
event_base的初始化是通過內(nèi)部調(diào)用event_base_with_new_config實現(xiàn)的,函數(shù)意思是帶有一些配置構(gòu)造一個event_base,配置主要包括的就是用戶不想要libevent使用的io復(fù)用函數(shù)名字。
函數(shù)中主要還是各種初始化,然后選擇合適的io復(fù)用函數(shù)等
用戶通過event_base_dispatch函數(shù)開啟事件驅(qū)動的主循環(huán),內(nèi)部調(diào)用event_base_loop執(zhí)行無線循環(huán),也沒有什么特別的
- 選擇io復(fù)用函數(shù)的阻塞時長
- 調(diào)用io函數(shù)
- 判斷最小堆中的event
- 處理所有激活的event
下面主要看一下如何處理激活event
/** 由base主循環(huán)調(diào)用,用來處理在base的激活隊列中的event,* 根據(jù)優(yōu)先級遍歷base的激活隊列數(shù)組,對于每一個隊列,調(diào)用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的主要函數(shù)* 首先根據(jù)event是否是永久event判斷是否需要將event從base的所有隊列中刪除* 如果是永久隊列,則只需要從base的激活隊列中刪除* 然后才開始調(diào)用event的回調(diào)函數(shù)** 注意:此處需要處理具有超時時間的event,因為最小堆中記錄的是絕對時間,* 所以如果這個event是具有超時時間的event,那么它是在從base中所有隊列中刪除后才加入到* 激活隊列中的,見timeout_process()* 所以需要再將其添加到base中,重新調(diào)用event_add_internal即可,見event_persist_closure* * 返回處理了多少個非內(nèi)部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在刪除完之后直接調(diào)用回調(diào)函數(shù)就可以了* 以后就不再管這個event了,因為只處理一次* ev_closure用于判斷是否是永久/信號event,在event_new中賦值*/switch (ev->ev_closure) {case EV_CLOSURE_SIGNAL:/* 信號的特殊處理,調(diào)用用戶的信號處理函數(shù) */event_signal_closure(base, ev);break;case EV_CLOSURE_PERSIST://io event/* 處理io事件,主要是特別處理有超時時間的event,需要重新計算絕對時間,然后調(diào)用回調(diào)函數(shù) */event_persist_closure(base, ev);break;}}return count; }函數(shù)處理當(dāng)前隊列中的每一個event,首先將其從激活隊列中刪除,如果是一次性事件,則從base中刪除
然后就根據(jù)信號/其他永久io事件調(diào)用相應(yīng)的處理函數(shù)
總結(jié)
event_base是整個事件驅(qū)動的載體,但是使用時僅僅需要event_base_new,和event_base_dispatch即可,內(nèi)部實現(xiàn)了對事件的監(jiān)控,對超時event的統(tǒng)一處理,對所有激活event按優(yōu)先級處理,處理時再細分是永久/一次性event,io/signal event等。這種先統(tǒng)一再分散的好處是可以在一個函數(shù)中統(tǒng)一處理激活event,實現(xiàn)更好的封裝接口,也更容易理清思路。
對信號的處理需要特別注意,因為不是一有信號發(fā)生馬上調(diào)用用戶的回調(diào)函數(shù),中間其實進行了兩次base循環(huán),這是為了將信號統(tǒng)一到event的結(jié)果,不然就直接sigaction綁定不是很好?
具有超時時間的event在處理時也是需要注意的部分,需要重新計算超時時間,然后使用event_add_internal重新添加(event在timeout_process已經(jīng)被刪除了),這就回到了event_add調(diào)用的event_add_internal上,如果不是超時event就僅僅是調(diào)用回調(diào)函數(shù)而已。
整體思路很容易理清,重點是細節(jié)方面,需要仔細琢磨
總結(jié)
以上是生活随笔為你收集整理的libevent源码学习-----event_base事件循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: libevent源码学习-----eve
- 下一篇: 每天一道LeetCode-----给定序