libevent代码阅读(13)——epoll的事件等待以及分发过程

在前面的章节我们提到event_base_loop中会调用具体的io复用机制的事件等待以及分发函数evsel->dispatch,然后调用event_process_active处理已经激活的事件。

对于epoll而言,dispatch就是epoll_dispatch,我们看一下它的执行流程

// 事件分发
static int
epoll_dispatch(struct event_base *base, struct timeval *tv)
{
	struct epollop *epollop = base->evbase;
	struct epoll_event *events = epollop->events;
	int i, res;
	long timeout = -1;

	if (tv != NULL) {
		timeout = evutil_tv_to_msec(tv);
		if (timeout < 0 || timeout > MAX_EPOLL_TIMEOUT_MSEC) {
			/* Linux kernels can wait forever if the timeout is
			 * too big; see comment on MAX_EPOLL_TIMEOUT_MSEC. */
			timeout = MAX_EPOLL_TIMEOUT_MSEC;
		}
	}

	// 暂时不用管下面两个函数的调用
	epoll_apply_changes(base);
	event_changelist_remove_all(&base->changelist, base);

	EVBASE_RELEASE_LOCK(base, th_base_lock);

	// 等待事件发生,res存放事件发生的个数
	res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);

	EVBASE_ACQUIRE_LOCK(base, th_base_lock);

	// 出错
	if (res == -1) {
		if (errno != EINTR) {
			event_warn("epoll_wait");
			return (-1);
		}

		return (0);
	}

	event_debug(("%s: epoll_wait reports %d", __func__, res));
	EVUTIL_ASSERT(res <= epollop->nevents);

	// 遍历所有触发的事件
	for (i = 0; i < res; i++)
	{
		int what = events[i].events;
		short ev = 0;

		if (what & (EPOLLHUP|EPOLLERR))
		{
			// 有错误发生
			ev = EV_READ | EV_WRITE;
		}
		else
		{
			// 可读
			if (what & EPOLLIN)
				ev |= EV_READ;

			// 可写
			if (what & EPOLLOUT)
				ev |= EV_WRITE;
		}

		if (!ev)
			continue;

		// 激活io事件映射表中的事件处理器
		evmap_io_active(base, events[i].data.fd, ev | EV_ET);
	}

	// 如果所有的事件都被触发了,表示事件数组的大小还是太小了,需要扩展事件数组的大小
	if (res == epollop->nevents && epollop->nevents < MAX_NEVENT) {
		/* We used all of the event space this time.  We should
		   be ready for more events next time. */
		int new_nevents = epollop->nevents * 2;
		struct epoll_event *new_events;

		new_events = mm_realloc(epollop->events,
		    new_nevents * sizeof(struct epoll_event));
		if (new_events) {
			epollop->events = new_events;
			epollop->nevents = new_nevents;
		}
	}

	return (0);
}

我们看到epoll_dispatch中调用了一个evmap_io_active函数,这个函数会将激活的事件插入到已激活的事件列表中

/*
 * 激活io事件映射表中的事件,然后插入到已激活事件队列中
 */
void
evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
{
	struct event_io_map *io = &base->io;
	struct evmap_io *ctx;
	struct event *ev;

#ifndef EVMAP_USE_HT
	EVUTIL_ASSERT(fd < io->nentries);
#endif
	GET_IO_SLOT(ctx, io, fd, evmap_io);

	EVUTIL_ASSERT(ctx);
	TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {
		if (ev->ev_events & events)
			event_active_nolock(ev, ev->ev_events & events, 1);
	}
}
event_active_nolock函数的过程如下:

/*
 * 非阻塞的激活事件,并将它存放于己激活事件列表中
 */
void
event_active_nolock(struct event *ev, int res, short ncalls)
{
	struct event_base *base;

	event_debug(("event_active: %p (fd "EV_SOCK_FMT"), res %d, callback %p",
			ev, EV_SOCK_ARG(ev->ev_fd), (int)res, ev->ev_callback));


	/* We get different kinds of events, add them together */
	// 如果本事件已经存在于已激活事件列表中,那么就返回
	if (ev->ev_flags & EVLIST_ACTIVE) {
		ev->ev_res |= res;
		return;
	}

	base = ev->ev_base;

	EVENT_BASE_ASSERT_LOCKED(base);

	ev->ev_res = res;

	// 如果当前事件的优先级小于event_base的运行时优先级
	// 就让event_base跳过本次循环,进行下一次循环
	if (ev->ev_pri < base->event_running_priority)
		base->event_continue = 1;

	// 如果是信号事件
	if (ev->ev_events & EV_SIGNAL) {
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
		if (base->current_event == ev && !EVBASE_IN_THREAD(base)) {
			++base->current_event_waiters;
			EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
		}
#endif
		ev->ev_ncalls = ncalls;
		ev->ev_pncalls = NULL;
	}

	// 把被激活的事件插入到已激活事件列表
	event_queue_insert(base, ev, EVLIST_ACTIVE);

	if (EVBASE_NEED_NOTIFY(base))
		evthread_notify_base(base);
}

event_queue_insert函数才正真将事件插入到已激活事件队列中:

/*
 * 将事件处理器添加到各种事件队列中
 * 将io事件处理器和信号事件处理器插入注册事件队列
 * 将定时器插入通用定时器队列或时间堆
 * 将被激活的事件处理器添加到活动事件队列中
 */
static void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
	EVENT_BASE_ASSERT_LOCKED(base);

	// 避免重复插入
	if (ev->ev_flags & queue) {
		/* Double insertion is possible for active events */
		if (queue & EVLIST_ACTIVE)
			return;

		event_errx(1, "%s: %p(fd "EV_SOCK_FMT") already on queue %x", __func__,
				ev, EV_SOCK_ARG(ev->ev_fd), queue);
		return;
	}

	// 增加event_base拥有的事件处理器的数量
	if (~ev->ev_flags & EVLIST_INTERNAL)
		base->event_count++;

	// 标记该事件处理器已经被处理过
	ev->ev_flags |= queue;

	// 根据不同的事件插入到不同的队列中
	switch (queue) {
	// 将io事件处理器或信号事件处理器插入注册事件队列
	case EVLIST_INSERTED:
		TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
		break;
		// 将就绪事件处理器插入活动事件队列
	case EVLIST_ACTIVE:
		base->event_count_active++;
		TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],
				ev,ev_active_next);
		break;
		// 将定时器插入到通用定时器队列或时间堆
	case EVLIST_TIMEOUT: {
		if (is_common_timeout(&ev->ev_timeout, base)) {
			struct common_timeout_list *ctl =
					get_common_timeout_list(base, &ev->ev_timeout);
			insert_common_timeout_inorder(ctl, ev);
		} else
			min_heap_push(&base->timeheap, ev);
		break;
	}
	default:
		event_errx(1, "%s: unknown queue %x", __func__, queue);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值