libevent實際封裝了不少IO複用模式,好比evport,select,poll,epoll,devpoll等等,這些都是不一樣操做系統下的I/O多路複用模式,那麼咱們怎麼知道當前使用的是哪一種模式呢?linux
說到底層實現,那就不得不說說event-config.h文件的生成。數組
在上一篇《libevent目錄結構分析》中,咱們提到event-config.h,它存放了不少宏定義配置。服務器
event-config.h這個文件並非一直不變的,這裏有一個過程:socket
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。函數
那麼底層實現到底用哪一個呢,看下面代碼: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
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
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服務器開發老司機,歡迎來撩!