浏览 3596 次
锁定老帖子 主题:libevent源码浅析(四)
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-05-15
最后修改:2009-05-15
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); } 来看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); } 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; } } } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |