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源创计划”,欢迎正在阅读的你也加入,一块儿分享。