libevent解读一

一、接触libevent

libevent是用C语言编写的开源高性能网络库,是学习网络编程、后台开发必备的源码学习资料。其源代码相当精炼、易读,设计技巧和技术框架也很精彩,所以在此我也将我的理解与思考与大家分享,后期我自己也模仿实现了C++版本的网络库,欢迎一起来讨论。


二、libevent特点

-高性能,体现在使用了事件驱动的设计模式,即Reactor模型。

-跨平台,支持目前几乎所有操作系统。

-支持多种I/O多路复用技术epoll、poll、select、kqueue等,并在此基础上进行封装,提供统一接口。

-将I/O、定时器、信号三者事件集成统一。


三、Reactor模式

        提到网络编程,不得不提主流的也是使用最广泛的Reactor设计模式(另一个是Preactor模式),那么什么是Reactor,翻译过来是“反应堆”,它不同于传统的顺序执行逻辑,而是运用了回调函数的技术,应用程序将相应的接口注册到Reactor上,如果有相应的事件发生,Reactor将主动调用应用程序注册的接口来实现功能。举个例子:用手机订外卖,订完可以干其他事,外卖好了自然有骑手送上门,也就意味着可以开吃了,而不用去一遍遍去咨询商家外卖是否做好。如下图是Reactor模型的架构图。

libevent解读一_第1张图片

其中,Reactor作为所有事件的管理组件,支持事件的注册与注销,内部通过调用操作系统的I/O多路复用技术进行事件循环,当有事件到达时,调用相应的Event Handler。


那么libevent的事件处理流程是怎样的?

libevent解读一_第2张图片

1)由应用程序初始化event,设置句柄、事件类型和回调函数;

2)向libevent注册该事件event,libevent会将I/O事件和Signal事件放入到等待链表(双向链表)中进行管理,对于定时事件,libevent使用小根堆来管理。

3)程序调用event_base_dispatch()函数进入无限循环,等待事件就绪。libevent会将所有就绪事件放入到激活链表中,最终调用事件的回调函数执行事件处理。


四、核心代码

1.event

struct event {
	/*TAILQ_ENTRY是宏定义的双向链表节点结构
	ev_next和ev_signal_next分别I/O事件和信号事件在链表中的位置
	ev_active_next指向该事件在激活事件链表中的位子*/
	TAILQ_ENTRY (event) ev_next;
	TAILQ_ENTRY (event) ev_active_next;
	TAILQ_ENTRY (event) ev_signal_next;
	//min_heap_idx表示该事件在小根堆中的索引
	unsigned int min_heap_idx;	/* for managing timeouts */

	struct event_base *ev_base;

	int ev_fd;  //表示文件描述符或信号
	
	/*事件类型,EV_WRITE\EV_READ\EV_TIMEOUT\EV_SIGNAL\EV_PERSIST
	可通过|或运算组合*/
	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;  //标记event当前状态
};

2、event_base

struct event_base {
	const struct eventop *evsel;  //指向全局变量static const struct eventop *eventops[]中的一个
	void *evbase;  //eventop实例对象
	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;  //二级指针——activequeues[priority]
	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;
};
static const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
	&evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
	&kqops,
#endif
#ifdef HAVE_EPOLL
	&epollops,
#endif
#ifdef HAVE_DEVPOLL
	&devpollops,
#endif
#ifdef HAVE_POLL
	&pollops,
#endif
#ifdef HAVE_SELECT
	&selectops,
#endif
#ifdef WIN32
	&win32ops,
#endif
	NULL
};

for (i = 0; eventops[i] && !base->evbase; i++) {
		base->evsel = eventops[i];

		base->evbase = base->evsel->init(base);
	}

可以看出,libevent将系统提供的I/O demultiplex机制统一封装成了eventop结构,它的成员是一系列的函数指针,负责完成自身初始化、销毁释放;对事件的注册、注销和分发

在编译阶段,libevent会根据操作系统按执行效率选择所支持的复用机制,如Linux下的epoll、poll、select等。

在event_base对象初始化时,会将evsel指向eventops中的一个并实例化一个该对象。

3.事件循环

libevent的事件主循环通过event_base_loop()函数完成,其流程如下图所示。

libevent解读一_第3张图片

对应得代码如下

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;

	/* 清空事件缓存 */
	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 */
		if (base->event_gotterm) {
			base->event_gotterm = 0;
			break;
		}

		if (base->event_break) {
			base->event_break = 0;
			break;
		}

		timeout_correct(base, &tv);  //校正系统时间

		/* 根据timer heap中事件的最小超时时间,设置I/O demultiplexer最大等待时间 */
		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);
		}
		
		/* If we have no events, we just exit */
		if (!event_haveevents(base)) {
			event_debug(("%s: no events registered.", __func__));
			return (1);
		}

		/* update last old time */
		gettime(base, &base->event_tv);

		/* clear time cache */
		base->tv_cache.tv_sec = 0;

		/*调用系统I/O demultiplexer等待就绪事件
		evsel->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);
}


五、总结

       这篇文章主要介绍总结了libevent的整体框架与Reactor模型,也从代码层面分析了libevent主要数据结构与执行逻辑。下一篇会介绍libevent事件的集成。




你可能感兴趣的:(libevent解读,网络编程,Reactor模型,C语言,多路复用,数据结构)