Libevent源码学习(四) 事件内部流程解析下部

上一节过后,大家应该对于事件状态转换有了一定的了解,具体的代码部分也已经详细分析到了事件状态转换为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,现在以及差不多把整个流程说通了!大家可以自己再琢磨琢磨,还是比较容易理解的!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值