Libevent-2.1.8源码分析——event_base(二)

1. 简介

上一节中,我们学习了如何创建一个默认的event_base。但是在实际的应用中,默认的event_base是远远满足不了需求的。更多的时候我们是需要根据具体的使用情况,来建立一个复杂的event_base。关于如何建立复杂的event_base,其实在上一篇章中,我们已经提及。接下来我们将更深入的了解如何建立一个复杂的event_base。

2. event_base_new_with_config

建立一个复杂的event_base需要将设置好的event_config传递给event_base_new_with_config,该函数返回我们所需要的event_base。
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
	int i;
	struct event_base *base;
	int should_check_environment;

#ifndef EVENT__DISABLE_DEBUG_MODE
	event_debug_mode_too_late = 1;
#endif
	//调用内存管理函数分配event_base结构
	if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
		event_warn("%s: calloc", __func__);
		return NULL;
	}

	if (cfg)
		base->flags = cfg->flags;	//设置flags标志
	//判断是否需要检查EVENT_ * 环境变量
	should_check_environment =
	    !(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));

	{
		struct timeval tmp;
		int precise_time =
		    cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER);	//是否设置了使用更精准的时间标志
		int flags;
		//如果没有设置使用更精准的时间标志,但是在环境变量中设置 使用更精准的时间那就设置标志
		if (should_check_environment && !precise_time) {
			precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL;	//获取环境变量
			base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER;
		}
		flags = precise_time ? EV_MONOT_PRECISE : 0;
		evutil_configure_monotonic_time_(&base->monotonic_timer, flags);	//配置是否使用更精准的时间

		gettime(base, &tmp);
	}
	//初始化超时时间优先级队列(最小堆)
	min_heap_ctor_(&base->timeheap);

	base->sig.ev_signal_pair[0] = -1;
	base->sig.ev_signal_pair[1] = -1;
	//初始化socketpair,用于唤醒event_base通过其他线程
	base->th_notify_fd[0] = -1;
	base->th_notify_fd[1] = -1;

	//初始化队列
	TAILQ_INIT(&base->active_later_queue);

	evmap_io_initmap_(&base->io);
	evmap_signal_initmap_(&base->sigmap);
	event_changelist_init_(&base->changelist);

	base->evbase = NULL;

	//下面的这操作用于根据event_config进行设定
	if (cfg) {
		memcpy(&base->max_dispatch_time,
		    &cfg->max_dispatch_interval, sizeof(struct timeval));
		base->limit_callbacks_after_prio =
		    cfg->limit_callbacks_after_prio;
	} else {
		base->max_dispatch_time.tv_sec = -1;
		base->limit_callbacks_after_prio = 1;
	}
	if (cfg && cfg->max_dispatch_callbacks >= 0) {
		base->max_dispatch_callbacks = cfg->max_dispatch_callbacks;
	} else {
		base->max_dispatch_callbacks = INT_MAX;
	}
	if (base->max_dispatch_callbacks == INT_MAX &&
	    base->max_dispatch_time.tv_sec == -1)
		base->limit_callbacks_after_prio = INT_MAX;

	//选择使用的后端方法
	for (i = 0; eventops[i] && !base->evbase; i++) {
		//根据event_config的配置,是否屏蔽某些后端或设置某项特征
		if (cfg != NULL) {
			/* determine if this backend should be avoided */
			if (event_config_is_avoided_method(cfg,
				eventops[i]->name))
				continue;
			if ((eventops[i]->features & cfg->require_features)
			    != cfg->require_features)
				continue;
		}
		//环境变量的设置
		/* also obey the environment variables */
		if (should_check_environment &&
		    event_is_method_disabled(eventops[i]->name))
			continue;
		//找到要使用的后端
		base->evsel = eventops[i];

		base->evbase = base->evsel->init(base);
	}
	//没找到可使用的后端,返回 NULL
	if (base->evbase == NULL) {
		event_warnx("%s: no event mechanism available",
		    __func__);
		base->evsel = NULL;
		event_base_free(base);
		return NULL;
	}

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

	/* allocate a single active event queue */
	if (event_base_priority_init(base, 1) < 0) {
		event_base_free(base);
		return NULL;
	}

	/* prepare for threading */

#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE)
	event_debug_created_threadable_ctx_ = 1;
#endif

#ifndef EVENT__DISABLE_THREAD_SUPPORT
	//使能了线程的支持
	if (EVTHREAD_LOCKING_ENABLED() &&
	    (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {
		int r;
		EVTHREAD_ALLOC_LOCK(base->th_base_lock, 0);
		EVTHREAD_ALLOC_COND(base->current_event_cond);
		//用于初始化通知
		r = evthread_make_base_notifiable(base);
		if (r<0) {
			event_warnx("%s: Unable to make base notifiable.", __func__);
			event_base_free(base);
			return NULL;
		}
	}
#endif

#ifdef _WIN32
	if (cfg && (cfg->flags & EVENT_BASE_FLAG_STARTUP_IOCP))
		event_base_start_iocp_(base, cfg->n_cpus_hint);
#endif

	return (base);
}


2.1 evutil_configure_monotonic_time_

这个函数是用来配置使用时间精度精准或比较粗糙。通过是否设置了使用精准的时间标志选择。
int
evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base,
    int flags)
{
	/* CLOCK_MONOTONIC exists on FreeBSD, Linux, and Solaris.  You need to
	 * check for it at runtime, because some older kernel versions won't
	 * have it working. */
#ifdef CLOCK_MONOTONIC_COARSE	//内核是否支持CLOCK_MONOTONIC_COARSE
	const int precise = flags & EV_MONOT_PRECISE;
#endif
	const int fallback = flags & EV_MONOT_FALLBACK;
	struct timespec	ts;

#ifdef CLOCK_MONOTONIC_COARSE
	//关于CLOCK_MONOTONIC_COARSE和POSIX中CLOCK_MONOTONIC类似,只不过是精度比较粗糙的版本
	if (CLOCK_MONOTONIC_COARSE < 0) {
		/* Technically speaking, nothing keeps CLOCK_* from being
		 * negative (as far as I know). This check and the one below
		 * make sure that it's safe for us to use -1 as an "unset"
		 * value. */
		event_errx(1,"I didn't expect CLOCK_MONOTONIC_COARSE to be < 0");
	}
	//如果flag设置使用精准的时间,那么是不会调用下面的
	if (! precise && ! fallback) {
		if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
			base->monotonic_clock = CLOCK_MONOTONIC_COARSE;
			return 0;
		}
	}
#endif
	//CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响,方便用于两个事件的间隔
	if (!fallback && clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
		base->monotonic_clock = CLOCK_MONOTONIC;
		return 0;
	}

	if (CLOCK_MONOTONIC < 0) {
		event_errx(1,"I didn't expect CLOCK_MONOTONIC to be < 0");
	}

	base->monotonic_clock = -1;
	return 0;
}

2.2 gettime

这个函数用于获取一个timeval时间
/** Set 'tp' to the current time according to 'base'.  We must hold the lock
 * on 'base'.  If there is a cached time, return it.  Otherwise, use
 * clock_gettime or gettimeofday as appropriate to find out the right time.
 * Return 0 on success, -1 on failure.
 */
static int
gettime(struct event_base *base, struct timeval *tp)
{
	//如果使能调试锁,那么会断言event_base是否持有锁,这边要求必须持有锁,没有则断言失败
	EVENT_BASE_ASSERT_LOCKED(base);	
	//如果时间有了缓存,直接返回。避免过多的时间获取调用
	if (base->tv_cache.tv_sec) {
		*tp = base->tv_cache;
		return (0);
	}
	//会根据evutil_configure_monotonic_time_中设置的monotonic_clock 来选择是否
	//使用gettimeofday或clock_gettime获取得到tp
	if (evutil_gettime_monotonic_(&base->monotonic_timer, tp) == -1) {
		return -1;
	}
	//宏CLOCK_SYNC_INTERVAL定义为5,这里的意思是5s更新一次libevent内部时间不同(可能使用gettimeofday或clock_gettime)
	if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL
	    < tp->tv_sec) {
		struct timeval tv;
		evutil_gettimeofday(&tv,NULL);
		evutil_timersub(&tv, tp, &base->tv_clock_diff);
		base->last_updated_clock_diff = tp->tv_sec;
	}

	return 0;
}

3. 使用范例

#include <stdio.h>
#include <string.h>
#include <event2/event-config.h>
#include <event2/event.h> 

//cc -I/usr/local/include -o test main.cpp -L/usr/local/lib -levent

int main(void)
{
	struct event_base *pBase;
	struct event_config *pConfig;

	pConfig = event_config_new();

	//设置event_base_config_flag:不检测环境变量和使用更精准的定时器
	event_config_set_flag(pConfig, EVENT_BASE_FLAG_IGNORE_ENV | EVENT_BASE_FLAG_PRECISE_TIMER);
	//告诉event_base不使用epoll和poll后端方法
	event_config_avoid_method(pConfig, "epoll");
	event_config_avoid_method(pConfig, "poll");
	//对后端的特性进行筛选
	event_config_require_features(pConfig, EV_FEATURE_FDS);
	//设置CPU数
	event_config_set_num_cpus_hint(pConfig, 1);
	//设置事件优先级相关的
	event_config_set_max_dispatch_interval(pConfig, NULL, -1, 0);

	pBase = event_base_new_with_config(pConfig);
	if (pBase == NULL)
	{
		fprintf(stderr, "event_base_new_with_config error!\r\n");
		event_config_free(pConfig);
		return 0;
	}

	const char *pCurrMethod = event_base_get_method(pBase);
	if (pCurrMethod != NULL)
		printf("Current method is %s\r\n", pCurrMethod);

	//do something...

	event_config_free(pConfig);
	
	event_base_dispatch(pBase);
	event_base_free(pBase);

	return 0;
}
在Linux上运行如下所示:
Current method is select

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值