Libevent源碼分析-----配置event_base

 前面的博文都是講一些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時怎麼進行選擇的能夠參考這一個連接

智能調整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>  
#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

相關文章
相關標籤/搜索