libevent是怎麼選擇底層實現的

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

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

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

1.1 event-config.h文件的生成

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

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

  • 首先,configure在檢查環境依賴的時候會生成config.h和Makefile;socket

  • 而後,Makefile會根據config.h生成event-config.h;ide

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

 1/* Define to 1 if you have the <dlfcn.h> header file. */
2#define EVENT__HAVE_DLFCN_H 1
3/* Define if your system supports the epoll system calls */
4#define EVENT__HAVE_EPOLL 1
5/* Define to 1 if you have the `epoll_create1' function. */
6/* #undef EVENT__HAVE_EPOLL_CREATE1 */
7/* Define to 1 if you have the `epoll_ctl' function. */
8#define EVENT__HAVE_EPOLL_CTL 1
9/* Define to 1 if you have the <errno.h> header file. */
10#define EVENT__HAVE_ERRNO_H 1

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

1.2 I/O模式選擇

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

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

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

  • 首先,event_config_new生成了一個event_config,struct event_config定義以下:

 1struct event_config {
2    TAILQ_HEAD(event_configq, event_config_entry) entries;
3
4    int n_cpus_hint;
5    struct timeval max_dispatch_interval;
6    int max_dispatch_callbacks;
7    int limit_callbacks_after_prio;
8    enum event_method_feature require_features;
9    enum event_base_config_flag flags;
10};

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

  • 而後在event_base_new_with_config函數中,有以下代碼:

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

10            if ((eventops[i]->features & cfg->require_features)
11                != cfg->require_features)
12                continue;
13        }
14
15        /* also obey the environment variables */
16        if (should_check_environment &&
17            event_is_method_disabled(eventops[i]->name))
18            continue;
19
20        base->evsel = eventops[i];
21
22        base->evbase = base->evsel->init(base);
23    }

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

 1/* Array of backends in order of preference. */
2static const struct eventop *eventops[] = {
3#ifdef EVENT__HAVE_EVENT_PORTS
4    &evportops,
5#endif
6#ifdef EVENT__HAVE_WORKING_KQUEUE
7    &kqops,
8#endif
9#ifdef EVENT__HAVE_EPOLL
10    &epollops,
11#endif
12#ifdef EVENT__HAVE_DEVPOLL
13    &devpollops,
14#endif
15#ifdef EVENT__HAVE_POLL
16    &pollops,
17#endif
18#ifdef EVENT__HAVE_SELECT
19    &selectops,
20#endif
21#ifdef _WIN32
22    &win32ops,
23#endif
24    NULL
25};

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。

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

1struct event_base *base = event_base_new();

仍是裏面的這段代碼:

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

10            if ((eventops[i]->features & cfg->require_features)
11                != cfg->require_features)
12                continue;
13        }
14
15        /* also obey the environment variables */
16        if (should_check_environment &&
17            event_is_method_disabled(eventops[i]->name))
18            continue;
19
20        base->evsel = eventops[i];
21
22        base->evbase = base->evsel->init(base);
23    }

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


本文分享自微信公衆號 - cpp加油站(xy13640954449)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索