论坛首页 编程语言技术论坛

libevent源码浅析(四)

浏览 3596 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-05-15   最后修改:2009-05-15
C
最近刚刚一个项目自己用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);
}


来看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;
		}
	}
}
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics