libevent源码浅析(四)
副标题#e#
最近方才一个项目本身用libevent,因此这几天又把libevent的代码拿出来翻了下,当初看的时候有些似是而非的对象,这次是根基没有了。这篇也算是前面几篇libevent的blog的增补了。
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 */
/* active event management */
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;
};
我们这里用select来讲,其他的事件驱动器都相似。
我们来看,个中activequeues我们知道是暗示激活的事件行列.这里libevent的处理惩罚是,select被叫醒后,挪用 event_active要领,将此事件插入到activequeues行列中,这里这个行列的实现是用tail queue。然后libevent会执行event_process_active要领,从而从激活行列中,依次执行所激活的事件。这里这个行列之所以是一个指针的指针,是因为,libevent中事件还分为优先级,这样每个优先级都有一个activequeues行列。
记下来我们再来看按时器的实现,libevent会在每次执行轮回时,从优先级行列中取出来最小的谁人时间,然后将它插手到select中,从而实现按时器。而在每次select超时退出后,libevent会从小到大取出超时时间(直到大于当前时间),每次和当前时间较量,假如已超时,则会从优先级行列中删除此节点,然后将此超时事件插手到激活行列中。
接下来我们来看相关代码。
int
event_base_loop(struct event_base *base, int flags)
{
..................................................
timeout_correct(base, &tv);
tv_p = &tv;
///判定是否有激活事件,假如没有的话我们则会从优先级行列中取出最小的谁人时间。也就是离此刻最近的谁人超时时间。
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
///下面会先容这个函数
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
evutil_timerclear(&tv);
}
.........................................................
/* clear time cache */
base->tv_cache.tv_sec = 0;
///挪用相关事件驱动引擎的dispatch要领,这个要领中会将已激活的事件插手到激活行列,这里看到tv_p也就是上面取到的超时时间被传入到dispatch。
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
gettime(base, &base->tv_cache);
///处理惩罚超时事件,将所有已超时的事件插手到激活行列。下面我们会先容这个函数
timeout_process(base);
if (base->event_count_active) {
///执行激活事件行列
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);
}
#p#副标题#e#
来看timeout_next要领
static int
timeout_next(struct event_base *base, struct timeval **tv_p)
{
struct timeval now;
struct event *ev;
struct timeval *tv = *tv_p;
///取出最小的谁人时间。
if ((ev = min_heap_top(&base->timeheap)) == NULL) {
/* if no time-based events are active wait for I/O */
*tv_p = NULL;
return (0);
}
///获得当前的时间。
if (gettime(base, &now) == -1)
return (-1);
///已超时则直接退出
if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {
evutil_timerclear(tv);
return (0);
}
///将按时器事件减去当前时间,也就是超时时间付给tv。
evutil_timersub(&ev->ev_timeout, &now, tv);
assert(tv->tv_sec >= 0);
assert(tv->tv_usec >= 0);
event_debug(("timeout_next: in %ld seconds", tv->tv_sec));
return (0);
}
#p#分页标题#e#
void
timeout_process(struct event_base *base)
{
..............................................
gettime(base, &now);
///开始遍历此优先级行列
while ((ev = min_heap_top(&base->timeheap))) {
///假如比当前时间大,则说明还没到超时时间因此直接退出。
if (evutil_timercmp(&ev->ev_timeout, &now, >))
break;
///删除此超时事件,因此我们在利用按时器时,需要我们每次进入按时器后,再次add此事件。
/* delete this event from the I/O queues */
event_del(ev);
event_debug(("timeout_process: call %p",
ev->ev_callback));
///插手激活行列。
event_active(ev, EV_TIMEOUT, 1);
}
}
个中event_active是通过event_queue_insert来插入到激活行列的,因此我们来看这个函数:
void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
..............................................
ev->ev_flags |= queue;
switch (queue) {
///这个主要用来生存所有的激活以及非激活行列,也就是eventqueue.
case EVLIST_INSERTED:
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
break;
case EVLIST_ACTIVE:
///激活行列数加一,并将此事件插入到相应的优先级的激活行列中。
base->event_count_active++;
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
ev,ev_active_next);
break;
case EVLIST_TIMEOUT: {
///处理惩罚超时事件。
min_heap_push(&base->timeheap, ev);
break;
}
default:
event_errx(1, "%s: unknown queue %x", __func__, queue);
}
}
最厥后看执行激活行列
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)
///假如有persist符号,则只从激活行列中移除此事件,不然则从全局事件列表中删除此事件。
event_queue_remove(base, ev, EVLIST_ACTIVE);
else
event_del(ev);
/* 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 (event_gotsig || base->event_break)
return;
}
}
}