您的位置:首页 > 其它

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

2017-09-20 21:47 483 查看

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  libevent