libevent是怎麼選擇底層實現的

1. libevent到底使用哪一種io模式來做爲底層實現

libevent實際封裝了不少IO複用模式,好比evport,select,poll,epoll,devpoll等等,這些都是不一樣操做系統下的I/O多路複用模式,那麼咱們怎麼知道當前使用的是哪一種模式呢?linux

說到底層實現,那就不得不說說event-config.h文件的生成。數組

1.1 event-config.h文件的生成

在上一篇《libevent目錄結構分析》中,咱們提到event-config.h,它存放了不少宏定義配置。服務器

event-config.h這個文件並非一直不變的,這裏有一個過程:socket

  • 首先,configure在檢查環境依賴的時候會生成config.h和Makefile;
  • 而後,Makefile會根據config.h生成event-config.h;

event-config.h基本是存放宏定義的,以下:ide

/* Define to 1 if you have the <dlfcn.h> header file. */
#define EVENT__HAVE_DLFCN_H 1
/* Define if your system supports the epoll system calls */
#define EVENT__HAVE_EPOLL 1
/* Define to 1 if you have the `epoll_create1' function. */
/* #undef EVENT__HAVE_EPOLL_CREATE1 */
/* Define to 1 if you have the `epoll_ctl' function. */
#define EVENT__HAVE_EPOLL_CTL 1
/* Define to 1 if you have the <errno.h> header file. */
#define EVENT__HAVE_ERRNO_H 1

這個是在個人redhat環境下編譯之後生成的部分宏定義,EVENT__HAVE_EPOLL這個宏定義爲1則說明個人redhat環境是支持epoll的,但這並不能說明我當前就是使用的epoll,由於redhat一樣支持select和poll。函數

1.2 I/O模式選擇

那麼底層實現到底用哪一個呢,看下面代碼:ui

struct event_config *config;
struct event_base *base;
/* Create a new configuration object. */
config = event_config_new();
/* We don't want to use the "select" method. */ 
event_config_avoid_method(config, "select");
/* We want a method that can work with non-socket file descriptors */ 
event_config_require_features(config, EV_FEATURE_FDS);
base = event_base_new_with_config(config);
if (!base) { /* There is no backend method that does what we want. */
 exit(1); }
event_config_free(config);

這是源碼包裏面whatsnew-2.0.txt裏面的例子,event_config_require_features這個函數其實就指定了底層實現,那麼具體過程是怎樣的呢。this

  • 首先,event_config_new生成了一個event_config,struct event_config定義以下:
struct event_config {
    TAILQ_HEAD(event_configq, event_config_entry) entries;

    int n_cpus_hint;
    struct timeval max_dispatch_interval;
    int max_dispatch_callbacks;
    int limit_callbacks_after_prio;
    enum event_method_feature require_features;
    enum event_base_config_flag flags;
};

裏面的枚舉require_features就是決定最終用哪一個,而event_config_require_features指定了require_features的值。spa

  • 而後在event_base_new_with_config函數中,有以下代碼:
for (i = 0; eventops[i] && !base->evbase; i++) {
        if (cfg != NULL) {
            /* determine if this backend should be avoided */
            if (event_config_is_avoided_method(cfg,
                eventops[i]->name))
                continue;
            /*
            這裏不符合咱們cfg->require_features指定的I/O都不會往下走,只有知足條件的才寫到event_base裏面去
            */
            if ((eventops[i]->features & cfg->require_features)
                != cfg->require_features)
                continue;
        }

        /* also obey the environment variables */
        if (should_check_environment &&
            event_is_method_disabled(eventops[i]->name))
            continue;

        base->evsel = eventops[i];

        base->evbase = base->evsel->init(base);
    }

這段代碼中,cfg就是上面的config,EV_FEATURE_FDS是0x04, 而eventops定義以下:操作系統

/* Array of backends in order of preference. */
static const struct eventop *eventops[] = {
#ifdef EVENT__HAVE_EVENT_PORTS
    &evportops,
#endif
#ifdef EVENT__HAVE_WORKING_KQUEUE
    &kqops,
#endif
#ifdef EVENT__HAVE_EPOLL
    &epollops,
#endif
#ifdef EVENT__HAVE_DEVPOLL
    &devpollops,
#endif
#ifdef EVENT__HAVE_POLL
    &pollops,
#endif
#ifdef EVENT__HAVE_SELECT
    &selectops,
#endif
#ifdef _WIN32
    &win32ops,
#endif
    NULL
};

eventops是一個結構體指針數組,它裏面具體有哪些選項就是根據event-config.h裏面宏定義來的,裏面元素evportops等這些都是定義在各個I/O模式的封裝文件裏,例如epollops就是在epoll.c裏面的, selectops則是在select.c中定義的。

查看eventops各個元素裏面features字段的值,發現devpollops,pollops,selectops這三種與0x04是能對上的,而devpoll並非redhat的IO,那麼在redhat環境中符合條件的就是poll和select,而event_config_avoid_method(config, "select")這個函數調用指定不使用select,因此最終使用的就是poll。

那若是沒有顯示指定底層實現呢,好比這樣的:

struct event_base *base = event_base_new();

仍是裏面的這段代碼:

for (i = 0; eventops[i] && !base->evbase; i++) {
        if (cfg != NULL) {
            /* determine if this backend should be avoided */
            if (event_config_is_avoided_method(cfg,
                eventops[i]->name))
                continue;
            /*
            這裏不符合咱們cfg->require_features指定的I/O都不會往下走,只有知足條件的才寫到event_base裏面去
            */
            if ((eventops[i]->features & cfg->require_features)
                != cfg->require_features)
                continue;
        }

        /* also obey the environment variables */
        if (should_check_environment &&
            event_is_method_disabled(eventops[i]->name))
            continue;

        base->evsel = eventops[i];

        base->evbase = base->evsel->init(base);
    }

符合條件的是epoll,poll,select,根據for循環條件!base->evbase 可知,第一個epoll賦給base->evbase後,循環就會結束,因此默認就是epoll。

cpp加油站(ID:xy13640954449), 做者linux服務器開發老司機,歡迎來撩!
相關文章
相關標籤/搜索