在上一篇关于event_base使用的post里,我们略过了各类backend和event_base的组织原理,这篇post会更深入地讨论这块内容。
我们已经了解,event_base中的evsel、evbase和evsigsel等成员和backend强相关。
struct event_base {
/** Function pointers and other data to describe this event_base's
* backend. */
const struct eventop *evsel;
/** Pointer to backend-specific data. */
void *evbase;
/** List of changes to tell backend about at next dispatch. Only used
* by the O(1) backends. */
struct event_changelist changelist;
/** Function pointers used to describe the backend that this event_base
* uses for signals */
const struct eventop *evsigsel;
...
};在此基础上,我们首先观察eventop的结构,它定义了backend的具体行为:
struct eventop {
const char *name;
void *(*init)(struct event_base *);
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*dispatch)(struct event_base *, struct timeval *);
void (*dealloc)(struct event_base *);
int need_reinit;
enum event_method_feature features;
size_t fdinfo_len;
};可见,eventop用函数指针的形式规定了具体backend实现的行为接口:
init:初始化,创建backend运行所需的数据结构并返回,event_base用evbase存放该函数的返回值。add:注册感兴趣的事件,如读写、信号。del:同上,删除某些事件。dispatch:用于实现事件循环的某些逻辑,如果出现某些注册事件,该函数应该调用event_active。dealloc:清理释放该backend的资源。
具体的backend逻辑分别存放在对应的.c文件中,如select、devpoll、epoll、evport、kqueue、poll和signal等。这里我们以select为例细看:
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_dispatch,
select_dealloc,
1, /* need_reinit. */
EV_FEATURE_FDS,
0,
};
struct selectop {
int event_fds; /* Highest fd in fd set */
int event_fdsz;
int resize_out_sets;
fd_set *event_readset_in;
fd_set *event_writeset_in;
fd_set *event_readset_out;
fd_set *event_writeset_out;
};区分selectops和selectop两个数据结构,前者实现了上文eventop的五种接口,提供了逻辑实现,后者存储了select这个backend特定的数据,如监听对象的fd、读写事件的集合等。
更具体一点,看看select_add的源代码:
static int
select_add(struct event_base *base, int fd, short old, short events, void *p)
{
struct selectop *sop = base->evbase;
(void) p;
EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
check_selectop(sop);
/*
* Keep track of the highest fd, so that we can calculate the size
* of the fd_sets for select(2)
*/
if (sop->event_fds < fd) {
int fdsz = sop->event_fdsz;
// typedef unsigned long fd_mask;
if (fdsz < (int)sizeof(fd_mask))
fdsz = (int)sizeof(fd_mask);
/* In theory we should worry about overflow here. In
* reality, though, the highest fd on a unixy system will
* not overflow here. XXXX */
while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
fdsz *= 2;
if (fdsz != sop->event_fdsz) {
if (select_resize(sop, fdsz)) {
check_selectop(sop);
return (-1);
}
}
sop->event_fds = fd;
}
if (events & EV_READ)
FD_SET(fd, sop->event_readset_in);
if (events & EV_WRITE)
FD_SET(fd, sop->event_writeset_in);
check_selectop(sop);
return (0);
}代码的逻辑不难,首先进行合法性检查,然后根据backend中最高的fd计算Unix中fd_set的大小,检查是否需要扩容,而后调用FD_SET将参数fd添加到selectop中对应的fd_set中。
剩下的问题是,如何判断哪些backend可用,如何选择合适的backend?
答案藏在event.c中:
/* 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
#ifdef EVENT__HAVE_WEPOLL
&wepollops,
#endif
NULL
};在特定系统编译时根据宏确定某backend是否支持,然后决定是否要将对应的eventop实现加入到全局eventops数组中。数组中eventop的排列顺序就是backend选择的优先级顺序。
而在event_base_new_with_config方法中对eventops数组遍历,并检查当前backend是否被手动禁用,然后完成eventop的确定,如果没有找到合适的方法,则发出警告,释放资源并退出。
...
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;
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);
}
if (base->evbase == NULL) {
event_warnx("%s: no event mechanism available",
__func__);
base->evsel = NULL;
event_base_free(base);
return NULL;
}
...