一、支持多种I/O多路复用技术
Libevent的核心是事件驱动、同步非阻塞,为了达到这一目标,必须采用系统提供的 I/O多路复用技术,而这些在 Windows、Linux、Unix 等不同平台上却各有不同,如何能提供优雅而统一的支持方式,就是要思考的一个关键问题。
1,统一的接口
Libevent支持多种I/O多路复用技术的关键就在于结构体eventop,这个结构体提供了一系列的函数指针,下面是这个结构体的定义,如下:
struct eventop {
const char *name;
void *(*init)(struct event_base *); // 初始化
int (*add)(void *, struct event *); // 注册事件
int (*del)(void *, struct event *); // 删除事件
int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件分发
void (*dealloc)(struct event_base *, void *); // 注销,释放资源
/* set if we need to reinitialize the event base */
int need_reinit;
};
2、设置I/O demultiplex 机制
Libevent 把所有支持的 I/O demultiplex 机制存储在一个全局静态数组 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
};
Libevent 根据系统配置和编译选项决定使用哪一种 I/O demultiplex 机制,这段代码在函数 event_base_new()中
base->evbase = NULL;
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
libevent 在编译阶段根据编译选项配置选择不同系统的 I/O demultiplex 机制,而不支持在运行阶段根据配置再次选择。以 Linux 下面的 epoll 为例,实现在源文件 epoll.c 中,eventops 对象 epollops 定义如下
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_dispatch,
epoll_dealloc,
1 /* need reinit */
};
C++语言提供了虚函数来实现多态,在 C 语言中这是通过函数指针实现的。epollops 以及 epoll 的各种函数都直接定义在了 epoll.c 源文件中,对外都是不可见的。对于 libevent 的使用者而言,完全不会知道它们的存在,对 epoll 的使用也是通过 eventop 来完成的,达到了信息隐藏的目的。