对于一个网络框架而言,其中的事件循环无疑是重要的组成部分,下面,我们就来分析一下libevent的事件循环。
libevent中的事件循环信息主要是存在结构体event_base中
PS:这个结构体成员太多,我没有全部列出,只是列出了一部分比较重要的
struct event_base {
//指定某个eventop结构体,它决定了该event_base使用哪种I/O多路复用技术(注解1)
const struct eventop *evsel;
void *evbase;
//告知后端下次执行事件分发时需要注意的哪些事件
struct event_changelist changelist;
//一个eventop,专门用来处理信号事件的
const struct eventop *evsigsel;
//存储信号处理的信息
struct evsig_info sig;
//虚拟事件的个数
int virtual_event_count;
//总事件个数
int event_count;
//活跃事件个数
int event_count_active;
//处理完当前所有的事件之后退出
int event_gotterm;
//立即退出
int event_break;
//立即启动一个新的事件循环
int event_continue;
//当前运行事件的优先级
int event_running_priority;
//是否正在进行事件循环
int running_loop;
//活跃事件的链表
struct event_list *activequeues;
//活跃事件的个数
int nactivequeues;
//要延迟处理的事件队列
struct deferred_cb_queue defer_queue;
//IO事件队列(这个命名。。。)
struct event_io_map io;
//信号事件队列
struct event_signal_map sigmap;
//所有注册事件的队列
struct event_list eventqueue;
//管理定时事件的最小堆
struct min_heap timeheap;
//IO就绪的时候和缓存时间(这里我也不会怎么说明)
struct timeval event_tv;
struct timeval tv_cache;
......
};
struct event_base *event_base_new_with_config(const struct event_config *cfg)
{
int i;
struct event_base *base;
int should_check_environment;
#ifndef _EVENT_DISABLE_DEBUG_MODE
event_debug_mode_too_late = 1;
#endif
//为结构体分配内存
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
event_warn("%s: calloc", __func__);
return NULL;
}
//确定程序是否能使用monotonic时间
detect_monotonic();
//获取当前系统时间
gettime(base, &base->event_tv);
//初始化定时器小根堆
min_heap_ctor(&base->timeheap);
//初始化事件队列
TAILQ_INIT(&base->eventqueue);
//初始化用于信号事件的pair
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
//初始化用于线程通信的pair
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
//初始化deferred_queue
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数组,选择最优的IO复用机制(注解1)
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);
}
//选择IO机制失败或对其进行初始化失败,返回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);
//初始化优先级队列
if (event_base_priority_init(base, 1) < 0) {
event_base_free(base);
return NULL;
}
/* prepare for threading */
//以下都是线程或WINDOWS方面内容的初始化,略过
..............
return (base);
}
int event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
struct timeval tv;
struct timeval *tv_p;
int res, done, retval = 0;
/* Grab the lock. We will release it inside evsel.dispatch, and again
* as we invoke user callbacks. */
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
//如果running_loop非零,证明该事件循环已启动,直接返回-1
if (base->running_loop) {
event_warnx("%s: reentrant invocation. Only one event_base_loop"
" can run on each event_base at once.", __func__);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return -1;
}
base->running_loop = 1;
//清空时间缓存
clear_time_cache(base);
//如果加入了信号事件,就初始化与信号相关的全局变量
if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
evsig_set_base(base);
done = 0;
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
base->th_owner_id = EVTHREAD_GET_ID();
#endif
base->event_gotterm = base->event_break = 0;
while (!done) {
base->event_continue = 0;
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
break;
}
if (base->event_break) {
break;
}
//更新时间缓存
timeout_correct(base, &tv);
tv_p = &tv;
//如果有活动event或者指定标识EVLOOP_NONBLOCK,清空时间tv。
//否则计算一下要等待下一个定时事件发生的时间。
if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
evutil_timerclear(&tv);
}
/* If we have no events, we just exit */
if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = 1;
goto done;
}
/* update last old time */
gettime(base, &base->event_tv);
clear_time_cache(base);
//调用后端的处理函数(注1)
res = evsel->dispatch(base, tv_p);
if (res == -1) {
event_debug(("%s: dispatch returned unsuccessfully.",
__func__));
retval = -1;
goto done;
}
//更新时间缓存
update_time_cache(base);
//处理已过期的事件
//将这些事件从各个队列中取出,加入active队列,并在ev_res中设置EV_TIMEOUT标识
timeout_process(base);
//关键的事件处理函数event_process_active,以后的文章会详细介绍
//如果没有事件了,就结束循环
if (N_ACTIVE_CALLBACKS(base)) {
int n = event_process_active(base);
if ((flags & EVLOOP_ONCE)
&& N_ACTIVE_CALLBACKS(base) == 0
&& n != 0)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
event_debug(("%s: asked to terminate loop.", __func__));
done:
clear_time_cache(base);
base->running_loop = 0;
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (retval);
}
int event_base_loopexit(struct event_base *event_base, const struct timeval *tv)
{
return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb,
event_base, tv));
}
/** Callback: used to implement event_base_loopexit by telling the event_base
* that it's time to exit its loop. */
static void
event_loopexit_cb(evutil_socket_t fd, short what, void *arg)
{
struct event_base *base = arg;
base->event_gotterm = 1;
}
void event_base_free(struct event_base *base)
{
int i, n_deleted=0;
struct event *ev;
/* XXXX grab the lock? If there is contention when one thread frees
* the base, then the contending thread will be very sad soon. */
/* event_base_free(NULL) is how to free the current_base if we
* made it with event_init and forgot to hold a reference to it. */
if (base == NULL && current_base)
base = current_base;
/* If we're freeing current_base, there won't be a current_base. */
if (base == current_base)
current_base = NULL;
/* Don't actually free NULL. */
if (base == NULL) {
event_warnx("%s: no base to free", __func__);
return;
}
/* XXX(niels) - check for internal events first */
#ifdef WIN32
event_base_stop_iocp(base);
#endif
/* threading fds if we have them */
if (base->th_notify_fd[0] != -1) {
event_del(&base->th_notify);
EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);
if (base->th_notify_fd[1] != -1)
EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
event_debug_unassign(&base->th_notify);
}
/* Delete all non-internal events. */
for (ev = TAILQ_FIRST(&base->eventqueue); ev; ) {
struct event *next = TAILQ_NEXT(ev, ev_next);
if (!(ev->ev_flags & EVLIST_INTERNAL)) {
event_del(ev);
++n_deleted;
}
ev = next;
}
while ((ev = min_heap_top(&base->timeheap)) != NULL) {
event_del(ev);
++n_deleted;
}
for (i = 0; i < base->n_common_timeouts; ++i) {
struct common_timeout_list *ctl =
base->common_timeout_queues[i];
event_del(&ctl->timeout_event); /* Internal; doesn't count */
event_debug_unassign(&ctl->timeout_event);
for (ev = TAILQ_FIRST(&ctl->events); ev; ) {
struct event *next = TAILQ_NEXT(ev,
ev_timeout_pos.ev_next_with_common_timeout);
if (!(ev->ev_flags & EVLIST_INTERNAL)) {
event_del(ev);
++n_deleted;
}
ev = next;
}
mm_free(ctl);
}
if (base->common_timeout_queues)
mm_free(base->common_timeout_queues);
for (i = 0; i < base->nactivequeues; ++i) {
for (ev = TAILQ_FIRST(&base->activequeues[i]); ev; ) {
struct event *next = TAILQ_NEXT(ev, ev_active_next);
if (!(ev->ev_flags & EVLIST_INTERNAL)) {
event_del(ev);
++n_deleted;
}
ev = next;
}
}
if (n_deleted)
event_debug(("%s: %d events were still set in base",
__func__, n_deleted));
if (base->evsel != NULL && base->evsel->dealloc != NULL)
base->evsel->dealloc(base);
for (i = 0; i < base->nactivequeues; ++i)
EVUTIL_ASSERT(TAILQ_EMPTY(&base->activequeues[i]));
EVUTIL_ASSERT(min_heap_empty(&base->timeheap));
min_heap_dtor(&base->timeheap);
mm_free(base->activequeues);
EVUTIL_ASSERT(TAILQ_EMPTY(&base->eventqueue));
evmap_io_clear(&base->io);
evmap_signal_clear(&base->sigmap);
event_changelist_freemem(&base->changelist);
EVTHREAD_FREE_LOCK(base->th_base_lock, EVTHREAD_LOCKTYPE_RECURSIVE);
EVTHREAD_FREE_COND(base->current_event_cond);
mm_free(base);
}
注解1:请见:http://blog.csdn.net/small_qch/article/details/19487929