讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:主機(jī)域名、網(wǎng)絡(luò)空間、營(yíng)銷(xiāo)軟件、網(wǎng)站建設(shè)、吳起網(wǎng)站維護(hù)、網(wǎng)站推廣。
libevent是一個(gè)輕量級(jí)的開(kāi)源高性能事件驅(qū)動(dòng)網(wǎng)絡(luò)庫(kù),是一個(gè)典型的Reactor模型。其主要特點(diǎn)有事件驅(qū)動(dòng),高性能,跨平臺(tái),統(tǒng)一事件源等等。
網(wǎng)上關(guān)于libevent的源碼分析有很多相關(guān)博客,本人在學(xué)習(xí)過(guò)程中也是借助了網(wǎng)絡(luò)。所以,在此,關(guān)于libevent中的許多具體實(shí)現(xiàn)部分就不做介紹,主要是從相關(guān)數(shù)據(jù)結(jié)構(gòu)層面上去分析。僅供參考。
libevent中的事件處理類(lèi)型是event結(jié)構(gòu)類(lèi)型,event結(jié)構(gòu)體封裝了句柄,事件類(lèi)型,回調(diào)函數(shù),以及其他必要的標(biāo)志和數(shù)據(jù),是整個(gè)libevent庫(kù)的核心。
該結(jié)構(gòu)的定義如下:
struct event{ /* * ev_next, ev_active_next都是雙向鏈表節(jié)點(diǎn)指針 * 它們是libevent對(duì)不同事件類(lèi)型和在不同時(shí)期,對(duì)事件的管理時(shí)使用到的字段 * * libevent使用雙向鏈表保存所有注冊(cè)的IO和signal事件 * ev_next 就是該IO事件在鏈表中的位置,稱(chēng)此鏈表為已注冊(cè)事件鏈表 * ev_active_next: libevent將所有激活事件放入鏈表active list中,然后遍歷active list * 執(zhí)行調(diào)度,ev_active_next就指明了event在active list中的位置 */ TAILQ_ENTRY(event) ev_next; TAILQ_ENTRY(event) ev_active_next; /* * _ev 是一個(gè)聯(lián)合體,所有具有相同描述符的IO事件通過(guò)ev.ev_io.ev_io_next成員串聯(lián)成一個(gè) * 尾隊(duì)列,稱(chēng)之為IO事件隊(duì)列,所有具有相同信號(hào)值的信號(hào)事件通過(guò)ev.ev_signal.ev_signal_next * 串聯(lián)成一個(gè)尾隊(duì)列,稱(chēng)之為信號(hào)事件隊(duì)列。ev.ev_signal.ev_ncalls成員指定時(shí)間發(fā)生時(shí),Reactor * 需要執(zhí)行多少次該事件對(duì)應(yīng)的回調(diào)函數(shù),ev.ev_signal.ev_pcalls要么是NULL,要么執(zhí)行ev.ev_signal.ev_ncalls */ union{ struct { TAILQ_ENTRY(event) ev_io_next; struct timeval ev_timeout; }ev_io; struct { TAILQ_ENTRY(event) ev_signal_next; short ev_ncalls; short *ev_pcalls; }ev_signal; } _ev; /* * ev_timeout_pos是一個(gè)聯(lián)合體,它僅用于定時(shí)事件處理器,老版本libevnet中使用最小堆管理 * 定時(shí)器,但是開(kāi)發(fā)者認(rèn)為有時(shí)簡(jiǎn)單鏈表的管理更加高效。所以新版本中引入了“通用定時(shí)器”的 * 概念。這些定時(shí)器不是存儲(chǔ)在時(shí)間堆中,而是存儲(chǔ)在尾隊(duì)列中,我們稱(chēng)之為通用定時(shí)器隊(duì)列。 * 對(duì)于通用定時(shí)器而言,ev_timeout_pos中的ev_next_with_common_timeout成員指出了該定時(shí)器 * 在隊(duì)列中的位置;對(duì)于其他定時(shí)器,min_heap_idx成員指出了該定時(shí)器在時(shí)間堆中的位置。一個(gè) * 定時(shí)器是否是通用定時(shí)器,取決于其超時(shí)值的大小。具體參考event.c中的is_common_timeout函數(shù)。 */ union{ TAILQ_ENTRY(event) ev_next_with_common_timeout; int min_heap_idx; }ev_timeout_pos; //如果是超時(shí)事件ev_timeout超時(shí)值 struct timeval ev_timeout; //ev_base :該事件所屬的反應(yīng)堆實(shí)例,這是一個(gè)event_base結(jié)構(gòu)體 struct event_base *ev_base; //對(duì)于IO事件,是綁定的文件描述符,對(duì)于signal事件,是綁定的信號(hào) int ev_fd; /* * ev_events : event關(guān)注的事件類(lèi)型,它可以是以下三種類(lèi)型: * IO事件:EV_WRITE / EV_READ * 定時(shí)事件: EV_TIMEOUT * 信號(hào): EV_SIGANL *輔助選項(xiàng): EV_PERSIST, 表明是一個(gè)永久事件 */ short ev_events; //各個(gè)事件可以使用 "|"運(yùn)算符進(jìn)行組合,信號(hào)和IO事件不能同時(shí)設(shè)置 //事件就緒執(zhí)行時(shí),調(diào)用ev_callback的次數(shù),通常為1 short ev_ncalls; //指針,指向ev_ncalls或NULL short *ev_pncalls; //allows deletes in callback int ev_pri; //smaller numbers are higher priority //ev_callback:event回調(diào)函數(shù),被ev_base調(diào)用,執(zhí)行事件處理程序,這是一個(gè)函數(shù)指針 //其中fd對(duì)應(yīng)ev_fd, events對(duì)應(yīng)ev_events, arg對(duì)應(yīng)ev_arg void (*ev_callback)(int , short, void *arg); //void* 表明可以是任意類(lèi)型,在設(shè)置event時(shí)指定 void *ev_arg; //記錄了當(dāng)前激活事件的類(lèi)型 int ev_res; //result passed to event callback /* * libevent用于標(biāo)記event信息的字段,表明當(dāng)前的狀態(tài) */ int ev_flags; };
從event結(jié)構(gòu)的定義可以看出,event中封裝了句柄,回調(diào)函數(shù),和事件類(lèi)型。包括該事件在相應(yīng)鏈表或時(shí)間堆中的索引位置。宏TAILQ_ENTRY是尾隊(duì)列的節(jié)點(diǎn)類(lèi)型,其定義為:
#define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; \ /*下一個(gè)元素*/ struct type **tqe_prev; \ /*前一個(gè)元素的地址*/ }
每當(dāng)有事件event轉(zhuǎn)變?yōu)榫途w狀態(tài)時(shí),libevent就會(huì)把它移入到active event list[priority]中,其中priority是event的優(yōu)先級(jí);接著libevent會(huì)根據(jù)自己的調(diào)度策略選擇就緒事件,調(diào)用其cb_callback()函數(shù)執(zhí)行事件處理。
結(jié)構(gòu)體event_base是libevent的Reactor,其聲明如下:
struct event_base { /* 初始化Reactor的時(shí)候選擇一種后端IO復(fù)用機(jī)制,并記錄在如下字段中*/ const struct eventop *evsel; /*指向IO復(fù)用機(jī)制真正存儲(chǔ)的數(shù)據(jù),它通過(guò)evsel成員的init函數(shù)來(lái)初始化*/ void *evbase; /* 指向信號(hào)的后端處理機(jī)制,目前僅在signal.h文件中定義了一種處理方法*/ const struct eventop *evsigsel; void *evsigbase; /*信號(hào)處理器使用到的數(shù)據(jù)結(jié)構(gòu),其中封裝了一個(gè)socketpair創(chuàng)建的管道,它用于信號(hào)處理函數(shù)和事件多路分發(fā)器之間的通信。*/ struct evsig_info sig; /* 添加到該event_base的所有事件和激活事件的數(shù)量*/ int event_count; /**< counts number of total events */ int event_count_active; /**< counts number of active events */ /* 是否執(zhí)行完活動(dòng)事件隊(duì)列上的剩余任務(wù)之后就退出事件循環(huán) */ int event_gotterm; /**< Set to terminate loop once done * processing events. */ /* 是否立即退出事件循環(huán),而不管是否還有任務(wù)需要處理 */ int event_break; /**< Set to exit loop immediately */ /* 活動(dòng)事件隊(duì)列數(shù)組。索引越小的隊(duì)列,優(yōu)先級(jí)越高。高優(yōu)先級(jí)的活動(dòng)事件隊(duì)列中的事件處理器將被優(yōu)先處理*/ struct event_list **activequeues; /* 活動(dòng)事件隊(duì)列數(shù)組的大小,即該event_base一共有nactivequeues個(gè)不同優(yōu)先級(jí)的活動(dòng)事件隊(duì)列*/ int nactivequeues; /*是否應(yīng)該啟動(dòng)一個(gè)新的事件循環(huán)*/ int event_continue; //目前正在處理的活動(dòng)事件隊(duì)列的優(yōu)先級(jí) int event_running_priority; //事件循環(huán)是否已經(jīng)啟動(dòng) int running_loop; /** Deferred callback management: a list of deferred callbacks to * run active the active events. */ TAILQ_HEAD (deferred_cb_list, deferred_cb) deferred_cb_list; //文件描述符和IO事件之間的映射關(guān)系表 struct event_io_map io; /*信號(hào)值和信號(hào)事件之間的映射關(guān)系表*/ struct event_signal_map sigmap; /*注冊(cè)時(shí)間隊(duì)列,存放IO事件處理器和信號(hào)事件處理器*/ struct event_list eventqueue; /*時(shí)間堆*/ struct min_heap timeheap; //系統(tǒng)管理時(shí)間的一些成員 struct timeval event_tv; struct timeval tv_cache; };
其中:
evsel和evbase這兩個(gè)字段的額設(shè)置可能會(huì)讓人迷惑,可以將其看作是類(lèi)和靜態(tài)函數(shù)的關(guān)系,比如添加事件時(shí)的調(diào)用行為:evsel->add(evbase, ev),實(shí)際上執(zhí)行操作的是evbase,這相當(dāng)于class::add(instance, ev),instance就是class的一個(gè)實(shí)例。evsel指向全局變量static const struct eventop *eventops[]中的一個(gè)。eventops[]包含了select,poll,kequeue和epoll等等其中的若干個(gè)全局實(shí)例對(duì)象。evbase實(shí)際上是一個(gè)eventop實(shí)例對(duì)象。
eventop結(jié)構(gòu)體,是一系列的函數(shù)指針,定義如下:
struct eventop{ const char* name; void *(*init)(struct event_base *); //初始化 int (*add)(void *, struct event *); //注冊(cè)事件 int (*del)(void *, struct event *); //刪除事件 int (*dispatch)(struct event_base*, void *, struct timeval *); //事件分發(fā) void (*dealloc)(struct event_base*, void *); //注銷(xiāo),釋放資源 //set if we need to reinitialize the event_base int need_reinit; };
在libevent中,每個(gè)IO事件分發(fā)機(jī)制的實(shí)現(xiàn)都必須提供這五個(gè)函數(shù)接口。
事件主循環(huán)主要是通過(guò)event_base_loop()函數(shù)來(lái)完成的。其代碼如下:
int event_base_loop(struct event_base *base, int flags){ const struct eventop *evsel = base->evsel; void *evbase = base->evbase; struct timeval tv; struct timeval *tv_p; int res, done; //清空時(shí)間緩存 base->tv_cache.tv_sec = 0; //evsignal_base是全局變量,在處理signal時(shí),用于指明signal所屬的event_base實(shí)例 if(base->sig.ev_signal_added) evsignal_base = base; done = 0; //事件主循環(huán) while(!done){ //查看是否需要跳出循環(huán),程序可以調(diào)用event_loopexit_cb()設(shè)置event_gotterm標(biāo)記 //調(diào)用event_base_loopbreak設(shè)置event_break標(biāo)志 if(base->event_gotterm){ base->event_gotterm = 0; break; } if(base->event_break){ base->event_break = 0; break; } //you cannot use this interface for multi-threaded apps while(event_gotsig){ event_gotsig = 0; if(event_sigcb){ res = (*event_sigcb)(); if(res == -1){ errno = EINTR; return -1; } } } //校正系統(tǒng)時(shí)間,如果系統(tǒng)使用的是非MONOTONIC時(shí)間,用戶可能會(huì)向后調(diào)整了系統(tǒng)時(shí)間 //在timeout_correct函數(shù)中,比較last wait time和當(dāng)前事件,如果 //當(dāng)前時(shí)間 < last wait time //表明時(shí)間有問(wèn)題,這需要更新timer_heap中所有定時(shí)事件的超時(shí)時(shí)間 timeout_correct(base, &tv); //根據(jù)time heap中事件的最小超時(shí)時(shí)間,計(jì)算系統(tǒng)IO demultiplexer的最大等待時(shí)間 tp_p = &tv; if(!base->event_count_active && !(flags & EVLOOP_NONBLOCK)){ timeout_next(base, &tv_p); } else{ //依然有未處理的就緒時(shí)間,就讓IO demultiplexer立即返回,不必等待 //下面會(huì)提到,在libevent中,低優(yōu)先級(jí)的就緒事件可能不能立即被處理 evutil_timerclear(&tv); } //如果當(dāng)前沒(méi)有注冊(cè)事件,就退出 if(!event_haveevents(base)){ event_debug("%s: no events registered.", __func__); return 1; } //更新last wait time,并清空time cache gettime(base, &base->event_tv); base->tv_cache.tv_sec = 0; //調(diào)用系統(tǒng)IO demultiplexer等待就緒IO events,可能是epoll_wait,或者select等 //在evsel->dispatch()中,會(huì)把就緒signal event / IO event插入到激活鏈表中 res = evsel->dispatch(base, evbase, tv_p); if(res == -1) return -1; //將time cache 賦值為當(dāng)前系統(tǒng)時(shí)間 gettime(base, &base->tv_cache); //檢查heap中的timer events,將就緒的timer event從heap上刪除,并插入到激活鏈表中 timeout_process(base); //調(diào)用event_process_active()處理激活鏈表中的就緒event,調(diào)用其回調(diào)函數(shù)執(zhí)行事件處理 //該函數(shù)會(huì)尋找最高優(yōu)先級(jí)(priority值越小優(yōu)先級(jí)越高)的激活事件鏈表 //然后處理鏈表中的所有就緒事件 //因此低優(yōu)先級(jí)的就緒事件可能得不得及時(shí)處理 if(base->event_count_active){ event_process_active(base); if(!base->event_count_active && (flags & EVLOOP_ONCE)) done = 1; } else if(flags & EVLOOP_NONBLOCK) done = 1; } //循環(huán)結(jié)束,清空時(shí)間緩存 base->tv_cache.tv_sec = 0; event_debug("%s: asked to terminate loop.", __func__); return 0; }
統(tǒng)一事件源,libevent將timer和signal事件都統(tǒng)一到了系統(tǒng)的IO demultiplex機(jī)制中
通過(guò)socketpair來(lái)實(shí)現(xiàn)的。即一個(gè)socket對(duì),其中有兩個(gè)socket,一個(gè)讀,一個(gè)寫(xiě)。
將讀socket在事件主循環(huán)實(shí)例中注冊(cè)一個(gè)讀事件,當(dāng)信號(hào)發(fā)生時(shí),往寫(xiě)socket中寫(xiě)入一個(gè)字符,通常為信號(hào)值,此時(shí)讀socket上有讀事件,觸發(fā)IO demultiplex讀事件,然后同普通的IO事件一起被處理即可。
timer和IO事件的統(tǒng)一。因?yàn)橄到y(tǒng)的IO機(jī)制,例如select()和epoll_wait()都允許程序制定一個(gè)最大的等待時(shí)間,根據(jù)所有timer事件中的最小超時(shí)時(shí)間來(lái)設(shè)置IO demultiplex的最大等待時(shí)間,當(dāng)IO返回時(shí),再激活所有就緒的timer事件就可以了,這樣就將timer事件完美融合到了系統(tǒng)的IO機(jī)制中了。
IO和signal的統(tǒng)一。因?yàn)閟ignal的出現(xiàn)對(duì)進(jìn)程來(lái)說(shuō)是完全隨機(jī)的。所以當(dāng)signal發(fā)生時(shí),并不立即調(diào)用event的callback函數(shù)處理信號(hào),而是設(shè)法通知系統(tǒng)的IO機(jī)制,讓其返回,然后再統(tǒng)一和IO事件,以及timer一起處理。
事件主循環(huán)的流程如下
1) 開(kāi)始 2) 調(diào)整系統(tǒng)時(shí)間與否 3) 根據(jù)timer heap中的event的最小超時(shí)時(shí)間計(jì)算系統(tǒng)IO demultiplexer的最大等待時(shí)間 4) 更新last wait time, 并清空time cache5) 調(diào)用系統(tǒng)I/O demultiplexer等待就緒I/O events6) 檢查signal的激活標(biāo)記,如果被設(shè)置,則檢查激活signal event,并將event插入到激活鏈表中7) 將就緒的I/O event插入到激活鏈表中8) 檢查heap中的timer events,將就緒的timer event從heap上刪除,并插入到激活鏈表中9) 根據(jù)優(yōu)先級(jí)處理激活鏈表中的就緒event,調(diào)用其回調(diào)函數(shù)執(zhí)行事件處理(優(yōu)先級(jí)越小越高)10) 結(jié)束
新聞名稱(chēng):Libevent淺析
網(wǎng)站路徑:http://aaarwkj.com/article32/igidpc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、虛擬主機(jī)、做網(wǎng)站、小程序開(kāi)發(fā)、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)