libevent源码分析之IO多路复用

本文深入探讨了IO多路复用的概念,并详细分析了libevent如何在不同平台下应用和封装IO多路复用,包括select和epoll。libevent通过eventop结构体抽象接口,实现跨平台兼容性和高性能。同时,文章还介绍了libevent中select和epoll的具体封装细节及源码分析。

一、IO多路复用概念

IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞,挂起线程,直到阻塞超时。

二、libevent的IO多路复用应用

libevent支持多种IO多路复用机制,保障了跨平台和性能。支持的IO多路复用包括:

机制 平台
select unix、linux
win32select win
poll unix、linux
epoll unix、linux
kqueue Mac OS
dev/poll Solaris
evport Solaris

注意:mac os用的unix内核,安卓用的linux内核。libevent还支持win系统的iocp,不过iocp不是IO多路复用,而是异步IO。

三、libevent的IO多路复用接口抽象

libevent使用eventop结构体来抽象IO多路复用相关的接口,可以在不同平台下,选择合适的IO多路复用实现。event_base结构体里有个属性evsel指向eventop,evbase指向特定模式下的数据结构。evnetop结构体的属性和含义如下表:

属性 含义
const char *name 多路复用名称
void *(*init)(struct event_base *) 完成初始化,返回改模式下专用的结构体
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo) 添加fd上的事件监听
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo) 删除fd上的事件监听
int (*dispatch)(struct event_base *, struct timeval *) 检测哪些事件已激活,并执行event的激活回调函数
void (*dealloc)(struct event_base *) 当fork了进程时,调用dealloc来重初始化一些属性
int need_reinit 是否要重新初始化event_base
enum event_method_feature features 当前机制支持的feature
size_t fdinfo_len 额外数据结构的长度

具体的调用关系图,如下:
在这里插入图片描述

四、libevent的select封装

select相关的数据放在selectop结构体里,具体的定义如下:

属性 含义
int event_fds 最大的fd编号
int event_fdsz 集合数组的最大容量
int resize_out_sets 标记是否需要重算集合大小
fd_set *event_readset_in 监听read的fd集合
fd_set *event_writeset_in 监听write的fd结合
fd_set *event_readset_out 调用select前,会把event_readset_in的数据拷贝过来,避免select修改集合数据
fd_set *event_writeset_out 调用select前,会把event_writeset_in的数据拷贝过来,避免select修改集合数据

五、libevent封装的select源码

static void * select_init(struct event_base *base)
{
   
   
	//初始化selectop,并返回该对象
}

static int select_dispatch(struct event_base *base, struct timeval *tv)
{
   
   
	int res=0, i, j, nfds;
	struct selectop *sop = base->evbase;

	check_selectop(sop);
	if (sop->resize_out_sets) {
   
   
		fd_set *readset_out=NULL, *writeset_out=NULL;
		size_t sz = sop->event_fdsz;
		if (!(readset_out = mm_realloc(sop->event_readset_out, sz)))
			return (-1);
		sop->event_readset_out = readset_out;
		if (!(writeset_out = mm_realloc(sop->event_writeset_out, sz))) {
   
   
			/* We don't free readset_out here, since it was
			 * already successfully reallocated. The next time
			 * we call select_dispatch, the realloc will be a
			 * no-op. */
			return (-1);
		}
		sop->event_writeset_out = writeset_out;
		sop->resize_out_sets = 0;
	}

	memcpy(sop->event_readset_out, sop->event_readset_in,
	       sop->event_fdsz);
	memcpy(sop->event_writeset_out, sop->event_writeset_in,
	       sop->event_fdsz);

	nfds = sop->event_fds+1;

	EVBASE_RELEASE_LOCK(base, th_base_lock);

	res = select(nfds, sop->event_readset_out,
	    sop->event_writeset_out, NULL, tv);

	EVBASE_ACQUIRE_LOCK(base, th_base_lock);

	check_selectop(sop);

	if (res == -1) {
   
   
		if (errno != EINTR) {
   
   
			event_warn("select");
			return (-1);
		}

		return (0);
	}

	event_debug(("%s: select reports %d", __func__, res));

	check_selectop(sop);
	i = random() % nfds;
	for (j = 0; j < nfds; ++j) {
   
   
		if (++i >= nfds)
			i = 0;
		res = 0;
		if (FD_ISSET(i, sop->event_readset_out))
			res |= EV_READ;
		if (FD_ISSET(i, sop->event_writeset_out))
			res |= EV_WRITE;

		
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值