libevent代码阅读(14)——select复用机制(一)

libevent实现几种io复用机制,本文将会重点介绍select和epoll模式。

selectops(eventop类型)对象是用于操作select复用机制的结构对象,它内部成员都是回调函数等,这些回调函数用于实现select复用机制,定义如下:

// select模式的io复用机制
const struct eventop selectops = {
	// 名字
	"select",
	// 初始化
	select_init,
	// 添加事件
	select_add,
	// 移除事件
	select_del,
	// 事件等待和分发
	select_dispatch,
	// 删除
	select_dealloc,
	// 是否需要重新初始化
	0, /* doesn't need reinit. */
	EV_FEATURE_FDS,
	0,
};

selectop结构则是存放select模式一些信息的结构,例如套接字集合等,定义如下:

// select数据结构
struct selectop {
	// 在集合中最大的文件描述符的数值
	int event_fds;		/* Highest fd in fd set */

	// 集合的大小
	int event_fdsz;

	int resize_out_sets;

	// 读集合,但是在调用select之前需要将其复制到event_readset_out中
	fd_set *event_readset_in;
	// 写集合
	fd_set *event_writeset_in;

	// 读集合,作为select的输入(select函数回修改这些集合,所以在调用之前需要备份好)
	fd_set *event_readset_out;
	// 写集合,作为select的输入(select函数回修改这些集合,所以在调用之前需要备份好)
	fd_set *event_writeset_out;
};

初始化select模式:

/*
 * select初始化
 */
static void *
select_init(struct event_base *base)
{
	struct selectop *sop;

	// 分配select对象
	if (!(sop = mm_calloc(1, sizeof(struct selectop))))
		return (NULL);

	// 分配select对象的套接字集合
	if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {
		select_free_selectop(sop);
		return (NULL);
	}

	// 信号事件处理器初始化
	evsig_init(base);

	return (sop);
}

添加事件:

/*
 * 添加事件
 */
static int
select_add(struct event_base *base, int fd, short old, short events, void *p)
{
	struct selectop *sop = base->evbase;
	(void) p;

	EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
	check_selectop(sop);
	/*
	 * Keep track of the highest fd, so that we can calculate the size
	 * of the fd_sets for select(2)
	 */
	// 如果当前记录的最大文件描述符小于fd,那么需要替换为fd
	// 即event_fds始终记录着集合中数值最大的套接字
	// 同时需要调整套接字集合大小
	if (sop->event_fds < fd) {
		int fdsz = sop->event_fdsz;

		if (fdsz < (int)sizeof(fd_mask))
			fdsz = (int)sizeof(fd_mask);

		/* In theory we should worry about overflow here.  In
		 * reality, though, the highest fd on a unixy system will
		 * not overflow here. XXXX */
		while (fdsz < (int) SELECT_ALLOC_SIZE(fd + 1))
			fdsz *= 2;

		if (fdsz != sop->event_fdsz) {
			if (select_resize(sop, fdsz)) {
				check_selectop(sop);
				return (-1);
			}
		}

		sop->event_fds = fd;
	}

	// 读事件
	if (events & EV_READ)
		FD_SET(fd, sop->event_readset_in);

	// 写事件
	if (events & EV_WRITE)
		FD_SET(fd, sop->event_writeset_in);
	check_selectop(sop);

	return (0);
}

事件等待以及分发:

/*
 * 事件等待以及分发
 */
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);

	// 判断是否需要重新调整输出集合的大小(如果event_readset_in的大小大于event_readset_out,那么必须调整event_readset_out的大小)
	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;
	}

	// 将备份集合中的数据复制到将要被select处理的集合中
	// 这样被修改的始终都是event_readset_out和event_writeset_out
	// 而event_readset_in和event_writeset_in中总是存放这最新的文件描述符集合
	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);

	// 调用select函数等待事件发生
	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;

		if (res == 0)
			continue;

		// 将事件插入到已激活事件队列中,以进行下一步的操作
		evmap_io_active(base, i, res);
	}
	check_selectop(sop);

	return (0);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main文件添加: int main int argc char argv[] { #ifdef Q OS LINUX QCoreApplication::setEventDispatcher new EventDispatcherLibEvent ; qInstallMessageHandler customMessageHandler ; #endif QCoreApplication a argc argv ; auto ser new ConfigServer; ser >startServer ; return a exec ; } 在 pro文件添加 linux{ LIBS + levent core SOURCES + common eventdispatcher libevent eventdispatcher libevent cpp common eventdispatcher libevent eventdispatcher libevent config cpp common eventdispatcher libevent eventdispatcher libevent p cpp common eventdispatcher libevent socknot p cpp common eventdispatcher libevent tco eventfd cpp common eventdispatcher libevent tco pipe cpp common eventdispatcher libevent tco cpp common eventdispatcher libevent timers p cpp HEADERS + common eventdispatcher libevent common h common eventdispatcher libevent eventdispatcher libevent h common eventdispatcher libevent eventdispatcher libevent config h common eventdispatcher libevent eventdispatcher libevent config p h common eventdispatcher libevent eventdispatcher libevent p h common eventdispatcher libevent libevent2 emul h common eventdispatcher libevent qt4compat h common eventdispatcher libevent tco h common eventdispatcher libevent wsainit h } 可以直接跨平台了使用了 csdn博客:http: blog csdn net rushroom">最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main文件添加: [更多]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值