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