Libevent源码学习(三) 事件内部流程解析上部

      上一节简单的介绍了事件的概念,这节就实际利用libevent的代码与上节最后说到的几个问题进行详细说明.为了简单考虑,这里先不考虑超时事件与信号事件,只进行读写事件的解释,遇到和超时事件信号事件有关的代码也就略过了.

1.事件的定义

      事件是libevent一个非常重要的概念,先来看看它的数据结构定义.代码位于event.h文件中
struct event {
	TAILQ_ENTRY (event) ev_next;
	TAILQ_ENTRY (event) ev_active_next;
	TAILQ_ENTRY (event) ev_signal_next;
	unsigned int min_heap_idx;	/* for managing timeouts */

	struct event_base *ev_base;

	int ev_fd;
	short ev_events;
	short ev_ncalls;
	short *ev_pncalls;	/* Allows deletes in callback */

	struct timeval ev_timeout;

	int ev_pri;		/* smaller numbers are higher priority */

	void (*ev_callback)(int, short, void *arg);
	void *ev_arg;

	int ev_res;		/* result passed to event callback */
	int ev_flags;
};
      上面的代码中,有几个是比较重要的地方.
      1.还记得上节最后说过如果事件很多怎么办?就需要用一个链表将事件进行管理组合,这个链表咱们后续会说到.前三个TAILQ_ENTRY结构变量的作用就是用于标记当前事件位于链表中的位置.
      2.event_base结构的变量ev_base暂时理解为事件链表的管理者就可以了.
      3.ev_events是事件的类型,是读事件还是写事件等.
      4.ev_callback回调函数是需要关注的重点,这个就是事件的处理方法,也是需要编程实现的地方.
      5.ev_flags用于标记事件的状态,这个下一段解释.
      6.还有一个重要的参数ev_fd,这个不用我说大家也看出来了:网络描述符.没有这个参数我们也不知道这个网络数据从那个端口来的.

2.事件的状态

      在介绍如何初始化事件开始,介绍一下libevent内部对于事件的状态管理.先来看看其定义,位于event.h文件中
#define EVLIST_TIMEOUT	0x01
#define EVLIST_INSERTED	0x02
#define EVLIST_SIGNAL	0x04
#define EVLIST_ACTIVE	0x08
#define EVLIST_INTERNAL	0x10
#define EVLIST_INIT	0x80
      这里对于读写事件来说,需要注意的是第二个,第四个和第六个.
      首先事件应该有几种状态,初始化状态,等待发生状态,发生状态,完成状态.因此一个比较好的思路可以构建两个事件链表,一个是等待发生事件链表,另一个是发生事件链表.这个时候整个底层平台需要做的就很明确了.
      1.不断的遍历等待发生事件链表,若触发条件达到之后将其放到发生事件链表中.
      2.不断的遍历发生事件链表,调用事件响应函数处理.
      3.事件完成之后就不需要保存了,没有必要,具体怎么处理后续再说.
      根据上述想法,事件的状态转化路径如下:
      1.当事件刚完成初始化时,状态为EVLIST_INIT.
      2.当插入已注册链表等待事件发生时,状态为EVLIST_INSERTED.
      3.当事件发生时,状态为EVLIST_ACTIVE.
      4.当事件完成之后,就看程序员怎么处理了.

3.事件的初始化

      现在介绍事件的初始化过程,重点是函数event_set,位于event.c文件中,具体见下.
void
event_set(struct event *ev, int fd, short events,
	  void (*callback)(int, short, void *), void *arg)
{
	/* Take the current base - caller needs to set the real base later */
	ev->ev_base = current_base;

	ev->ev_callback = callback;
	ev->ev_arg = arg;
	ev->ev_fd = fd;
	ev->ev_events = events;
	ev->ev_res = 0;
	ev->ev_flags = EVLIST_INIT;
	ev->ev_ncalls = 0;
	ev->ev_pncalls = NULL;

	min_heap_elem_init(ev);

	/* by default, we put new events into the middle priority */
	if(current_base)
		ev->ev_pri = current_base->nactivequeues/2;
}
      这个函数是对事件进行初始化,主要是赋值事件处理函数,事件标志,状态等.可以看到初始事件的状态是EVLIST_INIT.这里current_base是一个全局的变量,理解为事件链表管理者就可以了.

4.注册添加事件

      初始化完成事件后,需要将事件告知平台,这样平台才知道有这么个事件.重点看event_add函数,位于event.c文件,已删除部分无关代码
int
event_add(struct event *ev, const struct timeval *tv)
{
	struct event_base *base = ev->ev_base;
	const struct eventop *evsel = base->evsel;
	void *evbase = base->evbase;
	int res = 0;

	/* 删除部分代码 */

	/*当事件是读写事件或信号事件,并且事件并没有插入注册事件链表或激活事件链表中*/
	if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
	    !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
 		/* evsel是第一节介绍过的使用的网络模型,这里是将整个事件的fd插入到网络模型中去. */
		res = evsel->add(evbase, ev);
		if (res != -1) /* 网络模型fd插入成功 */
			event_queue_insert(base, ev, EVLIST_INSERTED);/*将当前事件插入链表中,并且改变事件的状态为EVLIST_INSERTED*/
	}

	/* 删除部分代码 */

	return (res);
}
      上面的注释很清除的解释了工作流程.后续的下一节再详细介绍!

发布了27 篇原创文章 · 获赞 6 · 访问量 6万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览