Libevent源码学习(一) 支持多种网络模型原理

本文介绍了Libevent如何支持多种网络模型,包括Linux和Windows下的实现。作者详细阐述了Libevent的基类接口、不同网络模型的构建以及统一的使用方法,并通过源码分析了关键部分,如`event_base`和`event_base_loop`函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  在linux下学习了一段时间网络编程,主体的编程思路懂得差不多了(windows下做过,基本差不多),所以决定看些开源的库来实际学习一下.故选择了相对简练的libevent进行代码阅读.在读的过程中,参考了一位已经将libevent解析的很清楚的大神的博客.http://blog.youkuaiyun.com/sparkliang/article/details/4957667

这里算是自己学习过程中的一些结果写出来,供大家参考.源码使用的也是1.4.13稳定版.

一.支持多种网络模型原理

在libevent官网上,大家都看到libevent支持各种不同的网络模型,而且还支持跨平台.其实这部分如果从C++的编程思想来看,大家可以很容易想到,利用类的继承加上虚函数实现.

1.首先构建一个基类,定义接口函数

2.继承这个基类,实现多个不同网络模型方法(接口)

3.使用指向子类的基类指针实现统一访问

由于libevent使用纯C代码实现,所以上面的部分只是给大家回忆一下C++怎么实现的而已,但是基本思想差不多一致.下面结合libevent的实际代码说明.

1.基类与接口,本部分代码位于event-internal.h文件中

struct eventop {
	const char *name;
	void *(*init)(struct event_base *);
	int (*add)(void *, struct event *);
	int (*del)(void *, struct event *);
	int (*dispatch)(struct event_base *, void *, struct timeval *);
	void (*dealloc)(struct event_base *, void *);
	/* set if we need to reinitialize the event base */
	int need_reinit;
};
eventop就是这个基类,这个也算是整个libevent工作过程中最实际干活的部分.eventop使用函数指针的方法去构建统一的接口,实现的接口包括初始化,添加事件,删除事件,分发事件,注销五个.事件的概念后续再说.

2.不同网络模型构建,本部分代码位于WIN32-Code/win32.c,devpoll.c,epoll.c,evport.c,kqueue.c,poll.c,select.c

本版本libevent支持7种不同网络模型,从上述文件即可看出.这里选用select.c进行解释,下面的代码位于select.c文件

static void *select_init	(struct event_base *);
static int select_add		(void *, struct event *);
static int select_del		(void *, struct event *);
static int select_dispatch	(struct event_base *, void *, struct timeval *);
static void select_dealloc     (struct event_base *, void *);

const struct eventop selectops = {
	"select",
	select_init,
	select_add,
	select_del,
	select_dispatch,
	select_dealloc,
	0
};
可以看出,首先第一步定义了5个select接口函数,并且构建了eventop实例selectops,注意这是个全局变量.同样大家可以从其他六个文件中找到相同的代码与全局变量定义.

下面的代码位于event.c文件中

#ifdef HAVE_EVENT_PORTS
extern const struct eventop evportops;
#endif
#ifdef HAVE_SELECT
extern const struct eventop selectops;
#endif
#ifdef HAVE_POLL
extern const struct eventop pollops;
#endif
#ifdef HAVE_EPOLL
extern const struct eventop epollops;
#endif
#ifdef HAVE_WORKING_KQUEUE
extern const struct eventop kqops;
#endif
#ifdef HAVE_DEVPOLL
extern const struct eventop devpollops;
#endif
#ifdef WIN32
extern const struct eventop win32ops;
#endif

/* In order of preference */
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
};
上面的代码使用预编译的方式来声明七种全局网络方法,具体使用那个网络方法就看编译环境了.

3.统一使用方法

这里用的时候就涉及到libevent的核心成员event_base了,event_base就类似于线程池模型中的管理模块一样.所有的事件都是由它管理的.先来看看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 */

	/* 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;
};
可以看得出来,event_base有eventop这个一个成员变量evsel,很明显,这个变量就是后续实际干活的实例了.

现在来看event_base是怎么初始化的,常用的是这么一个函数event_base_new().下面代码位于event.c文件中,已经删除无关部分

struct event_base *
event_base_new(void)
{
	int i;
	struct event_base *base;

	if ((base = calloc(1, sizeof(struct event_base))) == NULL)
		event_err(1, "%s: calloc", __func__);
	/*删除部分代码*/	
	base->evbase = NULL;
	for (i = 0; eventops[i] && !base->evbase; i++) {
		base->evsel = eventops[i];

		base->evbase = base->evsel->init(base);
	}
/*删除部分代码*/

	return (base);
}

这个event_base的evsel是使用咱们在第二步说到的全局变量赋值的,这样整个地方就很明了了.

要是还有些问题的话,再来看看event_base的核心函数event_base_loop,这是整个libevent事件的分发核心了.位于event.c文件中,已删除部分无关部分

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 */
		
		/* 删除部分代码 */	
		/* clear time cache */
		base->tv_cache.tv_sec = 0;

		res = evsel->dispatch(base, evbase, tv_p);

		if (res == -1)
			return (-1);
		gettime(base, &base->tv_cache);

		/* 删除部分代码 */
	}

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

	event_debug(("%s: asked to terminate loop.", __func__));
	return (0);
}

从上面的整个过程中,已经很清晰的解释了libevent是怎么构建不同网络模型的.

剩下的部分咱们后续再说,重点还没说到....



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值