解析libevent 1.4.14 版本如何实现不同操作系统下配置使用I/O demultiplex机制:
一、定义
- libevent在编译阶段选择系统的I/O demultiplex机制,而不支持在运行阶段根据配置选择。
- Libevent支持多种I/O多路复用技术的关键就在于结构体eventop,它的成员是一系列的函数指针, 定义在event-internal.h文件中:
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;};在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,来完成自身的初始化、销毁释放;对事件的注册、注销和分发。
- 通过宏定义在编译的时候选择相应的I/O多路复用对象,同时用extern申明要取的对象是在别处定义的。 event.c 文件中:
#ifdef HAVE_EVENT_PORTSextern const struct eventop evportops;#endif#ifdef HAVE_SELECTextern const struct eventop selectops;#endif#ifdef HAVE_POLLextern const struct eventop pollops;#endif#ifdef HAVE_EPOLLextern const struct eventop epollops;#endif#ifdef HAVE_WORKING_KQUEUEextern const struct eventop kqops;#endif#ifdef HAVE_DEVPOLLextern const struct eventop devpollops;#endif#ifdef WIN32extern const struct eventop win32ops;#endif
通过宏定义选择系统的I/O多路复用机制存储在一个全局静态数组eventops中,数组内容根据优先级顺序声明如下:
/* In order of preference */static const struct eventop *eventops[] = {#ifdef HAVE_EVENT_PORTS&evportops,#endif#ifdef HAVE_WORKING_KQUEUE&kqops,#endif#ifdef HAVE_EPOLL&epollops,#endif#ifdef HAVE_DEVPOLL&devpollops,#endif#ifdef HAVE_POLL&pollops,#endif#ifdef HAVE_SELECT&selectops,#endif#ifdef WIN32&win32ops,#endifNULL};
二、实现
- 在每一个(epoll、poll、dev/poll、select和kqueue)I/O多路复用机制的实现文件里定义了上面申明的 eventop对象。举例epoll机制在epoll.c文件:
const struct eventop epollops = {"epoll",epoll_init,epoll_add,epoll_del,epoll_dispatch,epoll_dealloc,1 /* need reinit */};
变量epollops中的函数指针具体声明如下,注意到其返回值和参数都和eventop中的定义严格一致,这是函数指针的语法限制。static void *epoll_init (struct event_base *);static int epoll_add (void *, struct event *);static int epoll_del (void *, struct event *);static int epoll_dispatch(struct event_base *, void *, struct timeval *);static void epoll_dealloc (struct event_base *, void *);
三、调用
- 在用户使用编译好的libevent库时,会调用event_init()初始化event_base框架。这时就会去 eventops[i] 里取优先级最高的 I/O demultiplex机制赋给框架使用。这段代码在文件event.c函数event_base_new()中:
base->evbase = NULL;for (i = 0; eventops[i] && !base->evbase; i++) {base->evsel = eventops[i];base->evbase = base->evsel->init(base);}