libev源码分析---整体设计
libev是Marc Lehmann用C寫(xiě)的高性能事件循環(huán)庫(kù)。通過(guò)libev,可以靈活地把各種事件組織管理起來(lái),如:時(shí)鐘、io、信號(hào)等。libev在業(yè)界內(nèi)也是廣受好評(píng),不少項(xiàng)目都采用它來(lái)做底層的事件循環(huán)。node.js也是其中之一。 學(xué)習(xí)和分析libev庫(kù),有助于理解node.js底層的工作原理,同時(shí)也可以學(xué)習(xí)和借鑒libev的設(shè)計(jì)思想。本文是最近在學(xué)習(xí)libev源碼的一些心得總結(jié)吧。
libev示例
先上一個(gè)例子,看看libev是怎么使用的吧。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | // a single header file is required #include <ev.h> #include <stdio.h> // for puts // every watcher type has its own typedef'd struct // with the name ev_TYPE ev_io stdin_watcher; ev_timer timeout_watcher; // all watcher callbacks have a similar signature // this callback is called when data is readable on stdin static void stdin_cb (EV_P_ ev_io *w, int revents) { puts ("stdin ready"); // for one-shot events, one must manually stop the watcher // with its corresponding stop function. ev_io_stop (EV_A_ w); // this causes all nested ev_run's to stop iterating ev_break (EV_A_ EVBREAK_ALL); } // another callback, this time for a time-out static void timeout_cb (EV_P_ ev_timer *w, int revents) { puts ("timeout"); // this causes the innermost ev_run to stop iterating ev_break (EV_A_ EVBREAK_ONE); } int main (void) { // use the default event loop unless you have special needs struct ev_loop *loop = EV_DEFAULT; // initialise an io watcher, then start it // this one will watch for stdin to become readable ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ); ev_io_start (loop, &stdin_watcher); // initialise a timer watcher, then start it // simple non-repeating 5.5 second timeout ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.); ev_timer_start (loop, &timeout_watcher); // now wait for events to arrive ev_run (loop, 0); // break was called, so exit return 0; } |
這是libev官網(wǎng)文檔的例子,其中l(wèi)ibev的使用步驟還是比較清晰的。從main開(kāi)始入手,可以發(fā)現(xiàn)代碼中主要做了這么幾件事情:
-
獲取ev_loop實(shí)例。ev_loop,從名字上可以看出,它代表了一個(gè)事件循環(huán),也是我們后面代碼的主要組織者。
-
創(chuàng)建和初始化watcher。libev中定義了一系列的watcher,每類(lèi)watcher負(fù)責(zé)一類(lèi)特定的事件。一般可以通過(guò)ev_TYPE_init函數(shù)來(lái)創(chuàng)建一個(gè)watcher實(shí)例(TYPE是某一種watcher類(lèi)型,如:io, timer等)。例子中分別創(chuàng)建了io和timer兩個(gè)watcher,并綁定了相應(yīng)的回調(diào)函數(shù)。當(dāng)感興趣的事件發(fā)生后,對(duì)應(yīng)的回調(diào)函數(shù)將會(huì)被調(diào)用。
-
將watcher注冊(cè)到ev_loop中。一般可以通過(guò)ev_TYPE_start函數(shù)來(lái)完成。注冊(cè)成功后,watcher便和loop關(guān)聯(lián)起來(lái)了,當(dāng)loop中檢測(cè)到感興趣的事件發(fā)生,便會(huì)通知相關(guān)的watcher。
-
啟動(dòng)事件循環(huán)。 即后面的ev_run函數(shù)。事件循環(huán)啟動(dòng)后,當(dāng)前線程/進(jìn)程將會(huì)被阻塞,直到循環(huán)被終止。
在上面的例子中,在兩個(gè)回調(diào)函數(shù)中的ev_break函數(shù)就是終止循環(huán)的地方。當(dāng)5.5秒超時(shí)或是標(biāo)準(zhǔn)輸入有輸入事件,則會(huì)進(jìn)入到相應(yīng)的回調(diào)函數(shù),然后會(huì)終止事件循環(huán),退出程序。
libev工作原理
總的來(lái)看,libev其實(shí)是實(shí)現(xiàn)了Reactor模式。當(dāng)中主要包含了這么幾個(gè)角色:watcher, ev_loop和ev_run。
watcher
watcher是Reactor中的Event Handler。一方面,它向事件循環(huán)提供了統(tǒng)一的調(diào)用接口(按類(lèi)型區(qū)分);另一方面,它是外部代碼的注入口,維護(hù)著具體的watcher信息,如:綁定的回調(diào)函數(shù),watcher的優(yōu)先級(jí),是否激活等。
在ev.h中我們可以看到各種watcher的定義,如:ev_io, ev_timer等。其中,watcher的公共屬性定義如下:
| 1 2 3 4 5 6 7 | /* shared by all watchers */ #define EV_WATCHER(type) \ int active; /* private */ \ int pending; /* private */ \ EV_DECL_PRIORITY /* private int priority; */ \ EV_COMMON /* rw void *data; */ \ EV_CB_DECLARE (type) /* private */ |
其中的宏定義如下:
| 1 2 3 | # define EV_DECL_PRIORITY int priority; # define EV_COMMON void *data; # define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents); |
-
active: 表示當(dāng)前watcher是否被激活。ev_TYPE_start調(diào)用后置位,ev_TYPE_stop調(diào)用后復(fù)位;
-
pending: 表示當(dāng)前watcher有事件就緒,等待處理。pending的值其實(shí)就是當(dāng)前watcher在pendings隊(duì)列中的下標(biāo);
-
priority: 是當(dāng)前watcher的優(yōu)先級(jí);
-
data: 附加數(shù)據(jù)指針,用來(lái)在watcher中攜帶額外所需的數(shù)據(jù);
-
cb:是事件觸發(fā)后的回調(diào)函數(shù)定義。
具體的watcher再在此基礎(chǔ)上添加額外的屬性。 開(kāi)發(fā)者可以根據(jù)需要,選擇特定類(lèi)型的watcher,創(chuàng)建實(shí)例并進(jìn)行初始化,然后將實(shí)例注冊(cè)到loop中即可。libev中定義了若干種類(lèi)型的watcher,每類(lèi)watcher負(fù)責(zé)解決某一特定領(lǐng)域的問(wèn)題(如:io, timer, signal等),可以在ev.h中看到這些watcher的定義。
ev_loop
ev_loop則是一個(gè)Reactor的角色,是事件循環(huán)的上下文環(huán)境,就像一根竹簽,把前面的watcher實(shí)例像糖葫蘆一樣串起來(lái)。
ev_loop的定義
ev_loop的定義在ev.c中,具體如下:
| 1 2 3 4 5 6 7 8 | struct ev_loop { ev_tstamp ev_rt_now; #define ev_rt_now ((loop)->ev_rt_now) #define VAR(name,decl) decl; #include "ev_vars.h" #undef VAR }; |
ev_vars.h中定義了ev_loop的各種屬性。在ev_wrap.h中則定義了與loop相關(guān)的各種宏,代碼中大多都是以宏的形式進(jìn)行操作。
watcher的管理
在ev_loop中,watcher按各自的類(lèi)型進(jìn)行分類(lèi)存儲(chǔ)。如:io的loop->anfds,timer的loop->timers。ev_TYPE_start在激活watcher后,便將它加入到相應(yīng)的存儲(chǔ)結(jié)構(gòu)中(具體的實(shí)現(xiàn)在后面介紹watcher的文章再分析)。
在事件循環(huán)中,有事件就緒的watcher會(huì)被挑揀出來(lái),保存到ev_loop中。這些就緒的watcher主要由loop->pendings和loop->pendingcnt來(lái)維護(hù)(如下圖所示)。這兩個(gè)東西都是二維數(shù)組,第一維都是優(yōu)先級(jí)。pendings中存的是ANPENDING實(shí)例,后者的做要作用就是維護(hù)就緒的watcher指針; 而pendingcnt中存的則是對(duì)應(yīng)優(yōu)先級(jí)上的pendings元素的數(shù)量。在每個(gè)就緒的watcher上也會(huì)有一個(gè)pending字段記錄它在pendings列表中的下標(biāo),這樣就可以通過(guò)watcher很方便的找到它在pendings列表中的位置了,這對(duì)刪除操作很有幫助。
在一輪事件循環(huán)結(jié)束后,則會(huì)根據(jù)優(yōu)先級(jí),依次觸發(fā)就緒的watcher。
bool ev_run(loop, flag)
ev_run函數(shù)是執(zhí)行事件循環(huán)的引擎,即Reactor模式中的select方法。通過(guò)向ev_run函數(shù)傳遞一個(gè)ev_loop實(shí)例,便可以開(kāi)啟一個(gè)事件循環(huán)。ev_run實(shí)際上是一個(gè)巨大的do-while循環(huán),期間會(huì)檢查loop中注冊(cè)的各種watcher的事件。如果有事件就緒,則觸發(fā)相應(yīng)的watcher。這個(gè)循環(huán)會(huì)一直持續(xù)到ev_break被調(diào)用或者無(wú)active的watcher為止。當(dāng)然,也可以通過(guò)傳遞EVRUN_NOWAIT或EVRUN_ONCE等f(wàn)lag來(lái)控制循環(huán)的阻塞行為。
ev_run的工作內(nèi)容,在官方文檔的API中有詳細(xì)說(shuō)明,通過(guò)文檔有助于對(duì)ev_run的理解。具體的代碼有點(diǎn)長(zhǎng),在這里就不貼了,感興趣的同學(xué)可以在ev.c中查看ev_run的實(shí)現(xiàn)代碼。剔除掉條件檢查和一些無(wú)關(guān)緊要的代碼,主要的流程如下圖所示。
可以看到,ev_run的主要工作就是按watcher的分類(lèi),先后檢查各種類(lèi)型的watcher上的事件,通過(guò)ev_feed_event函數(shù)將就緒的watcher加入到pending的數(shù)據(jù)結(jié)構(gòu)中。最后調(diào)用ev_invoke_pending,觸發(fā)pending中的watcher。完了以后會(huì)檢查,是否還有active的watcher以及是否有ev_break調(diào)用過(guò),然后決定是否要繼續(xù)下一輪循環(huán)。
總結(jié)
總的來(lái)看,libev的結(jié)構(gòu)設(shè)計(jì)還是非常清晰。如果說(shuō),主循環(huán)ev_run是libev這棵大樹(shù)的主干,那么功能強(qiáng)大,數(shù)量繁多的watcher就是這棵大樹(shù)的樹(shù)葉,而循環(huán)上下文ev_loop則是連接主干和樹(shù)葉的樹(shù)枝,它們的分工與職責(zé)是相當(dāng)明確的。作為大樹(shù)的主干,ev_run的代碼是非常穩(wěn)定和干凈的,基本上不會(huì)摻雜外部開(kāi)發(fā)者的邏輯代碼進(jìn)來(lái)。作為葉子的watcher,它的定位也非常明確:專(zhuān)注于自己的領(lǐng)域,只解決某一個(gè)類(lèi)型的問(wèn)題。不同的watcher以及watcher和主循環(huán)之間并沒(méi)有太多的干擾和耦合,這也是libev能如此靈活擴(kuò)展的原因。而中間的樹(shù)枝ev_loop的作用也是不言而喻的,正是因?yàn)樗谥虚g的調(diào)和,前面兩位哥們才能活得這么有個(gè)性。
看到這里,libev的主體架構(gòu)已經(jīng)比較清楚了,但是似乎還沒(méi)看到與性能相關(guān)的關(guān)鍵代碼。與主干代碼不一樣,這些代碼更多的是隱藏在實(shí)現(xiàn)具體邏輯的地方,也就是watcher之中的。雖然watcher的使用接口都比較相似,但是不同的watcher,底層的數(shù)據(jù)結(jié)構(gòu)和處理策略還是不一樣的。下面一篇文章我們就來(lái)探索一下libev中比較常用的幾種watcher的設(shè)計(jì)與實(shí)現(xiàn)。
轉(zhuǎn)載于:https://www.cnblogs.com/Huayuan/archive/2013/05/03/3058578.html
總結(jié)
以上是生活随笔為你收集整理的libev源码分析---整体设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在ARC工程中制定部分文件不适用ARC编
- 下一篇: oracle数据库----笔记1----