前面的博文都是講一些Libevent的一些輔助結構,如今來說一下關鍵結構體:event_base。html
這裏做一個提醒,在閱讀Libevent源碼時,會常常看到backend這個單詞。其直譯是「後端」。實際上其指的是Libevent內部使用的多路IO複用函數,多路IO複用函數就是select、poll、epoll這類函數。本系列博文中,爲了敘述方便,「多路IO複用函數」與「後端」這兩種說法都會採用。linux
一般咱們獲取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。socket
從這裏也能夠知道,若是要對event_base進行配置,那麼對cfg變量進行配置便可。如今咱們的目光從event_base結構體轉到event_config結構體。ide
先來看看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結構體的那四個成員變量進行配置。oop
第一個成員entries,其結構就不展開了,關於TAILQ_HEAD,能夠參考《TAILQ_QUEUE隊列》。這裏知道它是表示一個隊列便可,隊列元素的類型就是event_config_entry,能夠用來存儲一個字符串指針。它對應的設置函數爲event_config_avoid_method。ui
Libevent是跨平臺的Reactor,對於事件監聽,其內部是使用多路IO複用函數。比較通用的多路IO複用函數是select和poll。而不少平臺都提出了本身的高效多路IO複用函數,好比:epoll、devpoll、kqueue。Libevent對於這些多路IO複用函數都進行包裝,供本身使用。event_config_avoid_method函數就是指出,避免使用指定的多路IO複用函數。其是經過字符串的方式指定的,即參數method。這個字符串將由隊列元素event_config_entry的avoid_method成員變量存儲(因爲是指針,因此更準確來講是指向)。this
查看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時怎麼進行選擇的能夠參考這一個連接。
第二個成員變量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能夠取哪些值。
綜觀上面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> #endif int main() { #ifdef WIN32 WSADATA wsa_data; WSAStartup(0x0201, &wsa_data); #endif const 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) ); else printf("base == NULL\n"); #ifdef WIN32 WSACleanup(); #endif return 0; }
上面代碼在個人Ubuntu10.04上運行,其結果爲:
epoll poll select
currentmethod: epoll
在Win7 + VS2010的運行結果爲
win32
currentmethod: win32
參考:
http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html