event_base是libevent的核心对象,它代表了libevent的事件处理框架。
在libevent的官方网站上,它号称支持/dev/poll, kqueue(2), event ports, POSIX select(2), Windows select(), poll(2), 跟 epoll(4) 。之所以有这么大的支持力度,因为libevent的事件处理框架并不直接调用系统API,而是为支持的操作系统封装了一系列的回调函数。
从它生成的Makefile可以看出来:
build_triplet = armv7l-unknown-linux-gnueabihf host_triplet = armv7l-unknown-linux-gnueabihf am__append_1 = libevent_pthreads.la am__append_2 = libevent_pthreads.pc #am__append_3 = libevent_openssl.la #am__append_4 = libevent_openssl.pc am__append_5 = select.c am__append_6 = poll.c #am__append_7 = devpoll.c #am__append_8 = kqueue.c am__append_9 = epoll.c #am__append_10 = evport.c am__append_11 = signal.c #am__append_12 = $(EVENT1_HDRS) ..... SYS_SRC = $(am__append_5) $(am__append_6) \ $(am__append_7) $(am__append_8) \ $(am__append_9) $(am__append_10) \ $(am__append_11)
由此可见它根据操作系统的支持程度不同,编入select.c, poll.c等。
而在event.c 中又有以下定义
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调用是在编译期指定的。
现在开始进入代码。为了使用libevent, 创建一个libevent对象是必须的。我们一般调用的是:
struct event_base *base; base = event_base_new();
往里跟event_base_new函数,可得:
struct event_base * event_base_new(void) { struct event_base *base = NULL; struct event_config *cfg = event_config_new(); if (cfg) { base = event_base_new_with_config(cfg); event_config_free(cfg); } return base; }
由此可见,我们可以在分配event_base_new之前完成一些配置信息,从而改变事件处理框架的默认行为。
具体的函数有
event_config_avoid_method(),不使用某些事件处理框架,如select等
event_config_require_features(),//指定使用某些feature,如EV_FEATURE_ET
event_config_set_flag(),//指定flag,如EVENT_BASE_FLAG_NOLOCK
event_config_set_num_cpus_hint()在windows下IOCP时,试图使用的CPU数量
继续往里跟,可得以下代码:
event_base_new_with_config(const struct event_config *cfg) { ... mm_calloc(1, sizeof(struct event_base)))//分配内存 detect_monotonic();//检测是否支持monotonic gettime(base, base->event_tv); //一系列的初始化操作。清空time_heap,event_queue等等,不一一列举。 min_heap_ctor(base->timeheap); TAILQ_INIT(base->eventqueue); base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; base->th_notify_fd[0] = -1; base->th_notify_fd[1] = -1; event_deferred_cb_queue_init(base->defer_queue);// base->defer_queue.notify_fn = notify_base_cbq_callback; base->defer_queue.notify_arg = base; if (cfg) base->flags = cfg->flags; evmap_io_initmap(base->io); evmap_signal_initmap(base->sigmap); event_changelist_init(base->changelist); base->evbase = NULL; should_check_environment = !(cfg (cfg->flags EVENT_BASE_FLAG_IGNORE_ENV)); //eventops是一个静态数组,存放着系统支持的异步框架(select,poll,epoll...) 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; //选中一个事件处理框架,在这行代码可以看到虽然系统可能支持多种异步处理框架,但libevent只会选用一种。 //默认选中的是eventops数组里的最后一条,但是可以通过event_config_avoid_method,event_config_require_features //来修改,选中我们中意的事件处理框架 base->evsel = eventops[i]; //调用该框架下的处理函数,例如epoll框架需要调用epoll_create来创建队列。 //返回一个框架操作对象,里面存放着相关的句柄信息。如epoll需要存放epoll_create返回的fd等等。 base->evbase = base->evsel->init(base); } //经过event_config_avoid_method,event_config_require_features的过滤后,有可能找不到中意的事件处理框架。 //这时返回NULL if (base->evbase == NULL) { event_warnx("%s: no event mechanism available", __func__); base->evsel = NULL; event_base_free(base); return NULL; } if (evutil_getenv("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s", base->evsel->name); /* allocate a single active event queue */ if (event_base_priority_init(base, 1) < 0) { event_base_free(base); return NULL; } /* prepare for threading */ #ifndef _EVENT_DISABLE_THREAD_SUPPORT if (EVTHREAD_LOCKING_ENABLED() && (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) { int r; EVTHREAD_ALLOC_LOCK(base->th_base_lock, EVTHREAD_LOCKTYPE_RECURSIVE); base->defer_queue.lock = base->th_base_lock; EVTHREAD_ALLOC_COND(base->current_event_cond); r = evthread_make_base_notifiable(base); if (r<0 event_warnx="" s:="" unable="" to="" make="" base="" notifiable="" __func__="" event_base_free="" base="" return="" null="" endif="" ifdef="" win32="" if="" cfg="" cfg-="">flags & EVENT_BASE_FLAG_STARTUP_IOCP)) event_base_start_iocp(base, cfg->n_cpus_hint); #endif return (base); }0>
至此,libevent初始化完毕。看完了初始化,再来理解event_base就会相对容易一些。 它首先会选中一种事件处理框架,将之存放在evsel中。然后需要存放与该事件处理框架相关的对象,存入evbase中。
evbase在eventinternal.h 中定义。我就不贴代码了,注释挺详细的。
另外在这里可以看到libevent用到了tail queue(使用TAILQ_XXX来操作),哈希表(使用HT_XXX来操作 ),最小堆等数据结构。没见过tail queue,之前的算法书也没提过哈希表的实现。所以应该在下篇分析一下。