libevent源码阅读笔记(三)

    前面分析了libevent中对IO事件、定时器和signal的基本处理方法,也分析了libevent中基本的数据结构。从本章开始,将开始从代码结构和执行的角度,详细分析各功能的代码实现方式。

    本文将主要讲述事件处理的整体整体流程。

    1).event_base_new()函数源码分析:

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__);

	event_sigcb = NULL;
	event_gotsig = 0;

	detect_monotonic();
	gettime(base, &base->event_tv);
	
	min_heap_ctor(&base->timeheap);
	TAILQ_INIT(&base->eventqueue);
	base->sig.ev_signal_pair[0] = -1;
	base->sig.ev_signal_pair[1] = -1;
	
	base->evbase = NULL;
	for (i = 0; eventops[i] && !base->evbase; i++) {
		base->evsel = eventops[i];

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

	if (base->evbase == NULL)
		event_errx(1, "%s: no event mechanism available", __func__);

	if (evutil_getenv("EVENT_SHOW_METHOD")) 
		event_msgx("libevent using: %s\n",
			   base->evsel->name);

	/* allocate a single active event queue */
	event_base_priority_init(base, 1);

	return (base);
}

    这个函数是初始化libevent执行环境的函数,主要会初始化使用的event_base;主要执行流程为:

     a).初始化当前时间和event_base中的时间;

     b).min_heap_ctor()函数构造event_base中使用的小堆;

  c).TAILQ_INIT(&base->eventqueue);初始化注册事件队列;

  d).根据能找到的系统事件处理函数,注册event_base中的evsl,即系统的事件操作函数;同时,对应平台的*_init()(如epoll_init())里面,会调用int evsignal_init(struct event_base *base),该函数主要初始化信号的处理,后面信号专题里面会详细分析;

  e).event_base_priority_init(base, 1); 这个函数设置event_base支持的最大事件优先级,并初始化active queue的相关结构;


  2).event_base_loop()函数分析:

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 */
		if (base->event_gotterm) {
			base->event_gotterm = 0;
			break;
		}

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

		/* You cannot use this interface for multi-threaded apps */
		while (event_gotsig) {
			event_gotsig = 0;
			if (event_sigcb) {
				res = (*event_sigcb)();
				if (res == -1) {
					errno = EINTR;
					return (-1);
				}
			}
		}

		timeout_correct(base, &tv);

		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;

		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处理事件的主循环, event_dispatch() 和event_loop()等函数最后都是调用这个函数来实现事件处理的主循环。

  下面详细解释一下该函数的处理流程:

  a).while()主循环循环处理产生的事件,当done为true时退出主循环;

  b).如果base->event_gotterm或者base->event_break被设置时,退出主循环,这个当程序执行相关exit loop的函数时会被设置;

  c).如果捕获到信号,则调用event_sigcb()回调函数处理信号;

  d).调用timeout_correct()函数校正event_base中的时钟;

  e).调用event_base->evsel->dispatch() 来调用相关平台如epoll等的event dispatch函数,如epoll_wait();在evsel->dispatch()函数中,先处理检测到的信号,然后将激活的IO事件添加到event_base的activequeue中;

  f).timeout_process(base);处理所有的定时器事件,循环检测小堆中的定时器事件,如果有到期的定时器,则将事件添加到active队列中;

  g).event_process_active(base);如果有激活的事件,则调用这个函数处理所有的激活队列中的事件;


  3).epoll_dispatch()函数分析:

static int
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
	struct epollop *epollop = arg;
	struct epoll_event *events = epollop->events;
	struct evepoll *evep;
	int i, res, timeout = -1;

	if (tv != NULL)
		timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;

	if (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;
	}

	res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);

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

		evsignal_process(base);
		return (0);
	} else if (base->sig.evsignal_caught) {
		evsignal_process(base);
	}

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

	for (i = 0; i < res; i++) {
		int what = events[i].events;
		struct event *evread = NULL, *evwrite = NULL;
		int fd = events[i].data.fd;

		if (fd < 0 || fd >= epollop->nfds)
			continue;
		evep = &epollop->fds[fd];

		if (what & (EPOLLHUP|EPOLLERR)) {
			evread = evep->evread;
			evwrite = evep->evwrite;
		} else {
			if (what & EPOLLIN) {
				evread = evep->evread;
			}

			if (what & EPOLLOUT) {
				evwrite = evep->evwrite;
			}
		}

		if (!(evread||evwrite))
			continue;

		if (evread != NULL)
			event_active(evread, EV_READ, 1);
		if (evwrite != NULL)
			event_active(evwrite, EV_WRITE, 1);
	}

	if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
		/* 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 = realloc(epollop->events,
		    new_nevents * sizeof(struct epoll_event));
		if (new_events) {
			epollop->events = new_events;
			epollop->nevents = new_nevents;
		}
	}

	return (0);
}

  这个函数是epoll平台的事件dispatch函数的实现;对该函数的解释如下:

  a).调用epoll_wait()函数等待产生事件;

  b).在epoll_wait()执行完成后,表明程序被中断或者有到达的事件;

  c).调用evsignal_process()函数先处理可能检测到的信号;

  d).对于每一个被激活的事件,调用event_active()函数将事件添加到activequeue中;


  本节主要分析了libevent的event_base的初始化和事件的整体处理流程,在下一节,将主要分析事件的添加和删除;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值