上一节过后,大家应该对于事件状态转换有了一定的了解,具体的代码部分也已经详细分析到了事件状态转换为EVLIST_INSERTED状态.下面接着上面的部分接着说明.
1.事件链表定义
上节提到过很多次链表这么个东西,具体libevent怎么实现链表,大家可以参考compat/sys/queue.h文件.这里把常用的链表定义操作有详细的代码,基本上全部使用宏的形式去实现函数,应该是为了更高效的运行,有兴趣的学习一下.
这里就不再介绍链表的实现了,而且关注一下libevent中用的链表到底在哪里?还记得说过有一个结构被称为事件链表管理器,详细说明的就是这么一个结构event_base.具体见event_internal.h文件.
struct event_base {
const struct eventop *evsel;
void *evbase;
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
int event_break; /* Set to terminate loop immediately */
/* 等待执行的活动事件链表 */
struct event_list **activequeues;
int nactivequeues;
/* signal handling info */
struct evsignal_info sig;
struct event_list eventqueue; /* 插入事件链表,等待事件的触发 */
struct timeval event_tv;
struct min_heap timeheap;
struct timeval tv_cache;
};
从这个数据结构中,很清除的看见一共有两种事件链表,和上节说明的是一样的.假设现在已经有大量事件放置于插入事件链表中了,那么它是怎么转换到EVLIST_ACTIVE状态的?下面来解释一下.
2.EVLIST_INSERTED转换为EVLIST_ACTIVE
在第一节的时候给大家说明了libevent是怎么支持多种网络模型的,懂一点网络编程知识的人必定也明白,这一小节的内容是整个网络模型的核心.所以必定会涉及到网络模型内部.这里还是利用select模型说明,具体代码见select.c文件.第一节的最后说明了event_base的事件分发过程核心涉及到的是不同网络模型的dispath函数,这里看一下select模型怎么实现的吧.
static int
select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
int res, i, j;
struct selectop *sop = arg;
check_selectop(sop);/* */
memcpy(sop->event_readset_out, sop->event_readset_in,
sop->event_fdsz);
memcpy(sop->event_writeset_out, sop->event_writeset_in,
sop->event_fdsz);
res = select(sop->event_fds + 1, sop->event_readset_out, /* 调用select模型,获取查询的fd状态,sop使用的fd是在事件插入链表时添加的,可见上一节,同时可仔细阅读一下本文件中的select_add函数就明白了 */
sop->event_writeset_out, NULL, tv);
check_selectop(sop);
if (res == -1) {
if (errno != EINTR) {
event_warn("select");
return (-1);
}
evsignal_process(base);
return (0);
} else if (base->sig.evsignal_caught) {
evsignal_process(base);
}
event_debug(("%s: select reports %d", __func__, res));
check_selectop(sop);
i = random() % (sop->event_fds+1);
for (j = 0; j <= sop->event_fds; ++j) {
struct event *r_ev = NULL, *w_ev = NULL;
if (++i >= sop->event_fds+1)
i = 0;
res = 0;
if (FD_ISSET(i, sop->event_readset_out)) { //判断fd是否可读
r_ev = sop->event_r_by_fd[i];
res |= EV_READ;
}
if (FD_ISSET(i, sop->event_writeset_out)) { //判断fd是否可写
w_ev = sop->event_w_by_fd[i];
res |= EV_WRITE;
}
if (r_ev && (res & r_ev->ev_events)) { //可读的话,激活读事件处理程序
event_active(r_ev, res & r_ev->ev_events, 1); //重点就是这句!!!
}
if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { //可写的话,激活写事件处理程序
event_active(w_ev, res & w_ev->ev_events, 1); //重点就是这句!!!
}
}
check_selectop(sop);
return (0);
}
上面的代码中,事件状态转换的就是event_active这句,咱们现在来看看这个函数是怎么实现的!这个函数用过libevent的话应该看过,这是个可供外部访问的接口.其实现位于event.c文件中.
void
event_active(struct event *ev, int res, short ncalls)
{
/* We get different kinds of events, add them together */
if (ev->ev_flags & EVLIST_ACTIVE) {
ev->ev_res |= res;
return;
}
ev->ev_res = res;
ev->ev_ncalls = ncalls;
ev->ev_pncalls = NULL;
event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE); //看到没有,插入激活事件队列就是这里!!!
}
event_active函数还是比较清楚的,这样事件就迎来了最为重要的一步,事件过程的处理.
3.事件函数的执行
来看看咱们第一节的那个函数, event_base的核心函数event_base_loop,这是整个libevent事件的分发核心了.位于event.c文件中,已删除部分无关部分.现在来看,你一定可以很清晰的感觉到libevent的整体流程了.int
event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
struct timeval tv;
struct timeval *tv_p;
int res, done;
/* clear time cache */
base->tv_cache.tv_sec = 0;
if (base->sig.ev_signal_added)
evsignal_base = base;
done = 0;
while (!done) {
/* Terminate the loop if we have been asked to */
/* 删除无关代码 */
res = evsel->dispatch(base, evbase, tv_p); //如果当前选用是select模型,那这里用的就是select->select_dispatch接口,见第一节
if (res == -1)
return (-1);
gettime(base, &base->tv_cache);
timeout_process(base);
if (base->event_count_active) { //如果当前有激活事件,那么event_count_active数量一定会大于0,很明显走到这里
event_process_active(base); //那重点必定就是这个函数了,激活事件的处理.
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
/* clear time cache */
base->tv_cache.tv_sec = 0;
event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
现在来看看上面提到的那个重点函数event_process_active,位于event.c文件中.
static void
event_process_active(struct event_base *base)
{
struct event *ev;
struct event_list *activeq = NULL;
int i;
short ncalls;
for (i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
activeq = base->activequeues[i];
break;
}
}
assert(activeq != NULL);
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) { //依次遍历激活事件队列
if (ev->ev_events & EV_PERSIST) //这个地方值得稍微留意一些,还记得第一次说事件类型的时候没有解释,现在应该看的出来的.
event_queue_remove(base, ev, EVLIST_ACTIVE); //若事件为EV_PERSIST类型,那么仅仅从激活队列中删除,那事件还可以重复触发的,这其实也是为啥当事件转换为激活状态时,只是将其添加到激活队列,并没有从插入队列中删除
else
event_del(ev); //若事件没有EV_PERSIST,那就从插入队列删除吧,这样事件只激活处理一次,不可能重复触发
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
while (ncalls) {
ncalls--;
ev->ev_ncalls = ncalls;
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); //重点是这里呀,看到没有,预先定义的事件处理函数被执行了!!!执行了!!!!
if (base->event_break)
return;
}
}
}
OK,现在以及差不多把整个流程说通了!大家可以自己再琢磨琢磨,还是比较容易理解的!