Libevent源码分析-----配置event_base
? ? ? ??前面的博文都是講一些Libevent的一些輔助結(jié)構(gòu),現(xiàn)在來講一下關(guān)鍵結(jié)構(gòu)體:event_base。
?
? ? ? ??這里作一個(gè)提醒,在閱讀Libevent源碼時(shí),會(huì)經(jīng)常看到backend這個(gè)單詞。其直譯是“后端”。實(shí)際上其指的是Libevent內(nèi)部使用的多路IO復(fù)用函數(shù),多路IO復(fù)用函數(shù)就是select、poll、epoll這類函數(shù)。本系列博文中,為了敘述方便,“多路IO復(fù)用函數(shù)”與“后端”這兩種說法都會(huì)采用。
配置結(jié)構(gòu)體:
? ? ? ??通常我們獲取event_base都是通過event_base_new()這個(gè)無參函數(shù)。使用這個(gè)無參函數(shù),只能得到一個(gè)默認(rèn)配置的event_base結(jié)構(gòu)體。本文主要是講一些怎么獲取一個(gè)非默認(rèn)配置的event_base以及可以對(duì)event_base進(jìn)行哪些配置。
? ? ? ??還是先看一下event_base_new函數(shù)吧。//event.c文件 struct event_base * event_base_new(void) {struct event_base *base = NULL;struct event_config *cfg = event_config_new();if (cfg) {base = event_base_new_with_config(cfg);event_config_free(cfg);}return base; }
? ? ? ??可以看到,其先創(chuàng)建了一個(gè)event_config結(jié)構(gòu)體,并用cfg指針指向之,然后再用這個(gè)變量作為參數(shù)調(diào)用event_base_new_with_config。因?yàn)椴]有對(duì)cfg進(jìn)行任何的設(shè)置,所以得到的是默認(rèn)配置的event_base。
? ? ? ??從這里也可以知道,如果要對(duì)event_base進(jìn)行配置,那么對(duì)cfg變量進(jìn)行配置即可。現(xiàn)在我們的目光從event_base結(jié)構(gòu)體轉(zhuǎn)到event_config結(jié)構(gòu)體。
? ? ? ??先來看看event_config結(jié)構(gòu)體的定義。
struct event_config {TAILQ_HEAD(event_configq, event_config_entry) entries;int n_cpus_hint;enum event_method_feature require_features;enum event_base_config_flag flags; };struct event_config_entry {TAILQ_ENTRY(event_config_entry) next;const char *avoid_method; };? ? ? ??我們要做的就是對(duì)event_config結(jié)構(gòu)體的那四個(gè)成員變量進(jìn)行配置。
具體的配置內(nèi)容:
拒絕使用某個(gè)后端:
? ? ? ??第一個(gè)成員entries,其結(jié)構(gòu)就不展開了,關(guān)于TAILQ_HEAD,可以參考《TAILQ_QUEUE隊(duì)列》。這里知道它是表示一個(gè)隊(duì)列即可,隊(duì)列元素的類型就是event_config_entry,可以用來存儲(chǔ)一個(gè)字符串指針。它對(duì)應(yīng)的設(shè)置函數(shù)為event_config_avoid_method。
? ? ? ??Libevent是跨平臺(tái)的Reactor,對(duì)于事件監(jiān)聽,其內(nèi)部是使用多路IO復(fù)用函數(shù)。比較通用的多路IO復(fù)用函數(shù)是select和poll。而很多平臺(tái)都提出了自己的高效多路IO復(fù)用函數(shù),比如:epoll、devpoll、kqueue。Libevent對(duì)于這些多路IO復(fù)用函數(shù)都進(jìn)行包裝,供自己使用。event_config_avoid_method函數(shù)就是指出,避免使用指定的多路IO復(fù)用函數(shù)。其是通過字符串的方式指定的,即參數(shù)method。這個(gè)字符串將由隊(duì)列元素event_config_entry的avoid_method成員變量存儲(chǔ)(由于是指針,所以更準(zhǔn)確來說是指向)。
? ? ? ??查看Libevent源碼包里的文件,可以發(fā)現(xiàn)有諸如epoll.c、select.c、poll.c、devpoll.c、kqueue.c這些文件。打開這些文件就可以發(fā)現(xiàn)在文件內(nèi)容的前面都會(huì)定義一個(gè)struct eventop類型變量。該結(jié)構(gòu)體的第一個(gè)成員必然是一個(gè)字符串。這個(gè)字符串就描述了對(duì)應(yīng)的多路IO復(fù)用函數(shù)的名稱。所以是可以通過名稱來禁用某種多路IO復(fù)用函數(shù)的。
? ? ? ??下面是event_config_avoid_method函數(shù)的實(shí)現(xiàn)。其作用是把method指明的各個(gè)名稱記錄到entries成員變量中。int event_config_avoid_method(struct event_config *cfg, const char *method) {struct event_config_entry *entry = mm_malloc(sizeof(*entry));if (entry == NULL)return (-1);//復(fù)制字符串if ((entry->avoid_method = mm_strdup(method)) == NULL) {mm_free(entry);return (-1);}//插入到隊(duì)列中TAILQ_INSERT_TAIL(&cfg->entries, entry, next);return (0); }
上面的代碼是設(shè)置拒絕使用某一個(gè)多路IO復(fù)用函數(shù),在創(chuàng)建一個(gè)event_base時(shí)怎么進(jìn)行選擇的可以參考這一個(gè)鏈接。
智能調(diào)整CPU個(gè)數(shù):
? ? ? ??第二個(gè)成員變量n_cpus_hint。從名字來看是指明CPU的數(shù)量。是通過函數(shù)event_config_set_num_cpus_hint來設(shè)置的。其作用是告訴event_config,系統(tǒng)中有多少個(gè)CPU,以便作一些對(duì)線程池作一些調(diào)整來獲取更高的效率。目前,僅僅Window系統(tǒng)的IOCP(Windows的IOCP能夠根據(jù)CPU的個(gè)數(shù)智能調(diào)整),該函數(shù)的設(shè)置才有用。在以后,Libevent可能會(huì)將之應(yīng)用于其他系統(tǒng)。
? ? ? ??正如其名字中的hint,這僅僅是一個(gè)提示。就如同C++中的inline。event_base實(shí)際使用的CPU個(gè)數(shù)不一定等于提示的個(gè)數(shù)。
?
規(guī)定所選后端需滿足的特征:
? ? ? ??第三個(gè)成員變量require_features。從其名稱來看是要求的特征。不錯(cuò),這個(gè)變量指定 多路IO復(fù)用函數(shù)應(yīng)該滿足哪些特征。所有的特征定義在一個(gè)枚舉類型中。
//event.h文件 enum event_method_feature {//支持邊沿觸發(fā)EV_FEATURE_ET = 0x01,//添加、刪除、或者確定哪個(gè)事件激活這些動(dòng)作的時(shí)間復(fù)雜度都為O(1)//select、poll是不能滿足這個(gè)特征的.epoll則滿足EV_FEATURE_O1 = 0x02,//支持任意的文件描述符,而不能僅僅支持套接字EV_FEATURE_FDS = 0x04 };? ? ? ??這個(gè)成員變量是通過event_config_require_features函數(shù)設(shè)置的。該函數(shù)的內(nèi)部還是挺簡單的。
int event_config_require_features(struct event_config *cfg,int features) {if (!cfg)return (-1);cfg->require_features = features;return (0); }? ? ? ??從函數(shù)的實(shí)現(xiàn)可以看到,如果要設(shè)置多個(gè)特征,不能調(diào)用該函數(shù)多次,而應(yīng)該使用位操作。比如: EV_FEATURE_O1 | EV_FEATURE_FDS作為參數(shù)。
? ? ? ? 值得注意的是,對(duì)于某些系統(tǒng),可能其提供的多路IO復(fù)用函數(shù)不能滿足event_config_require_features函數(shù)要求的特征,此時(shí)event_base_new_with_config函數(shù)將返回NULL,即得不到一個(gè)滿足條件的event_base。所以在設(shè)置這個(gè)特征時(shí),那么就要檢查event_base_new_with_config的返回值是否為NULL,像下面代碼那樣。
#include<event.h> #include<stdio.h>int main() {event_config *cfg = event_config_new();event_config_require_features(cfg, EV_FEATURE_O1 | EV_FEATURE_FDS);event_base *base = event_base_new_with_config(cfg);if( base == NULL ){printf("don't support this features\n");base = event_base_new(); //使用默認(rèn)的。}…..return 0; }? ? ? ??上面代碼中,如果是在Linux運(yùn)行,也是返回NULL。即epoll都不能同時(shí)滿足那個(gè)兩個(gè)特征。
? ? ? ??那么怎么知道多路IO復(fù)用函數(shù)支持哪些特征呢?前面說到的一個(gè)機(jī)構(gòu)體struct eventop中有一個(gè)成員正是enum event_method_feature features。在Libevent-2.0.21-stable中是倒數(shù)第二個(gè)成員。打開epoll.c、select.c、poll.c、devpoll.c、kqueue.c這些文件,查看里面定義的struct eventop類型變量,就可以看到各個(gè)多路IO復(fù)用函數(shù)都支持哪些特征。在epoll.c文件可以看到,epoll支持EV_FEATURE_ET|EV_FEATURE_O1。所以前面的代碼中,返回NULL。
其他一些設(shè)置:
? ? ? ??第四個(gè)變量flags是通過函數(shù)event_config_set_flag設(shè)置的。函數(shù)的實(shí)現(xiàn)很簡單。注意,函數(shù)的內(nèi)部是進(jìn)行 |= 運(yùn)算的。
//event.c文件 int event_config_set_flag(struct event_config *cfg, int flag) {if (!cfg)return -1;cfg->flags |= flag;return 0; }? ? ? ??現(xiàn)在來看一下參數(shù)flag可以取哪些值。
- EVENT_BASE_FLAG_NOLOCK:不要為event_base分配鎖。設(shè)置這個(gè)選項(xiàng)可以為event_base節(jié)省一點(diǎn)加鎖和解鎖的時(shí)間,但是當(dāng)多個(gè)線程訪問event_base會(huì)變得不安全
- EVENT_BASE_FLAG_IGNORE_ENV:選擇多路IO復(fù)用函數(shù)時(shí),不檢測EVENT_*環(huán)境變量。使用這個(gè)標(biāo)志要考慮清楚:因?yàn)檫@會(huì)使得用戶更難調(diào)試程序與Libevent之間的交互
- EVENT_BASE_FLAG_STARTUP_IOCP:僅用于Windows。這使得Libevent在啟動(dòng)時(shí)就啟用任何必需的IOCP分發(fā)邏輯,而不是按需啟用。如果設(shè)置了這個(gè)宏,那么evconn_listener_new和bufferevent_socket_new函數(shù)的內(nèi)部將使用IOCP
- EVENT_BASE_FLAG_NO_CACHE_TIME:在執(zhí)行event_base_loop的時(shí)候沒有cache時(shí)間。該函數(shù)的while循環(huán)會(huì)經(jīng)常取系統(tǒng)時(shí)間,如果cache時(shí)間,那么就取cache的。如果沒有的話,就只能通過系統(tǒng)提供的函數(shù)來獲取系統(tǒng)時(shí)間。這將更耗時(shí)
- EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告知Libevent,如果決定使用epoll這個(gè)多路IO復(fù)用函數(shù),可以安全地使用更快的基于changelist 的多路IO復(fù)用函數(shù):epoll-changelist多路IO復(fù)用可以在多路IO復(fù)用函數(shù)調(diào)用之間,同樣的fd 多次修改其狀態(tài)的情況下,避免不必要的系統(tǒng)調(diào)用。但是如果傳遞任何使用dup()或者其變體克隆的fd給Libevent,epoll-changelist多路IO復(fù)用函數(shù)會(huì)觸發(fā)一個(gè)內(nèi)核bug,導(dǎo)致不正確的結(jié)果。在不使用epoll這個(gè)多路IO復(fù)用函數(shù)的情況下,這個(gè)標(biāo)志是沒有效果的。也可以通過設(shè)置EVENT_EPOLL_USE_CHANGELIST 環(huán)境變量來打開epoll-changelist選項(xiàng)
? ? ? ??綜觀上面4個(gè)變量的設(shè)置,特征設(shè)置event_config_require_features和CPU數(shù)目設(shè)置event_config_set_num_cpus_hint兩者的函數(shù)調(diào)用會(huì)覆蓋之前的設(shè)置。如果要同時(shí)設(shè)置多個(gè),那么需要在參數(shù)中使用位運(yùn)算中的 | 。而另外兩個(gè)變量的設(shè)置可以通過多次調(diào)用函數(shù)的方式同時(shí)設(shè)置多個(gè)值。
獲取當(dāng)前配置:
? ? ? ??前面的介紹的都是設(shè)置,現(xiàn)在來講一下獲取。主要有下面幾個(gè)。
const char **event_get_supported_methods(void); const char *event_base_get_method(const struct event_base *); int event_base_get_features(const struct event_base *base); static int event_config_is_avoided_method(const struct event_config *cfg, const char *method)
? ? ? ??第一個(gè)函數(shù)是獲取當(dāng)前系統(tǒng)所支持的多路IO復(fù)用函數(shù)有哪些。第二個(gè)函數(shù)需要一個(gè)event_base結(jié)構(gòu)體作為參數(shù),說明是在new到一個(gè)event_base之后才能調(diào)用的。該函數(shù)返回值是對(duì)應(yīng)event_base* 當(dāng)前所采用的多路IO復(fù)用函數(shù)是哪個(gè)。第三個(gè)函數(shù)則是獲取參數(shù)event_base當(dāng)前所采用的特征是什么。第四個(gè)函數(shù)則說明參數(shù)method指明的多路IO復(fù)用函數(shù)是不是被參數(shù)cfg所禁用了。如果是禁用了,返回非0值。不禁用就返回0。
#include<event2/event.h> #include<stdio.h>#ifdef WIN32 #include<WinSock2.h> #endifint main() { #ifdef WIN32WSADATA wsa_data;WSAStartup(0x0201, &wsa_data); #endifconst char** all_methods = event_get_supported_methods();while( all_methods && *all_methods ){printf("%s\t", *all_methods++);}printf("\n");event_base *base = event_base_new();if( base )printf("current method:\t %s\n", event_base_get_method(base) );elseprintf("base == NULL\n");#ifdef WIN32WSACleanup(); #endifreturn 0; }? ? ? ??上面代碼在我的Ubuntu10.04上運(yùn)行,其結(jié)果為:
? ? ? ??epoll? ? ? ??poll? ? ? ??select
? ? ? ??currentmethod:? ? ? ??epoll
? ? ? ??在Win7 + VS2010的運(yùn)行結(jié)果為
? ? ? ??win32
? ? ? ??currentmethod:? ? ? ??win32
? ? ? ??http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html
總結(jié)
以上是生活随笔為你收集整理的Libevent源码分析-----配置event_base的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: noip2016 组合数问题
- 下一篇: 如何建立软件测试管理体系?