libevent (hello-world代码源码)----evconnlistener、event中看了evconnlistener、event的定义,现在我们知道evconnlistener是用来描述tcp服务端的结构体,event结构里有3个event_callback,分别对应超时、io和信号事件。在libevent (hello-world代码源码)----event_base中可以知道event是event_base结构体管理的事件单位。
event_base管理一堆超时、io和信号触发的event事件。
让我们来看第四行:
struct sockaddr_in sin;
创建一个 描述网络连接参数的结构体,里面包含ip、端口、协议信息。
struct sockaddr_in
{
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
};
然后下面是:
#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
其中#ifdef _WIN32由编译器(ml.exe/ml64.exe)内部定义的。具体描述是:
_WIN32:Defined for applications for Win32 and Win64. Always defined.
_WIN64:Defined for applications for Win64.
windows平台上总是会有这种宏,所以用来判断是否是windows平台。
WSAStartup(0x0201, &wsa_data);
当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。为了在应用程序当中调用任何一个Winsock API,首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务初始化。
所以这几行的意思就是如果是windows系统,初始化网络服务。(linux不需要)
接下来是创建新的event_base:
base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
event_base_new:
/**
* Create and return a new event_base to use with the rest of Libevent.
*
* @return a new event_base on success, or NULL on failure.
*
* @see event_base_free(), event_base_new_with_config()
创建并返回一个新的event_ base以与Libevent的其余部分一起使用。
@return一个新的event_ base,成功时返回,失败时返回NULL。
@see event_base_free(), event_base_new_with_config()
*/
EVENT2_EXPORT_SYMBOL
struct event_base *event_base_new(void);
失败时返回NULL,所以后面就是如果创建失败直接return 1,因为是main函数所以直接退出程序。
event_config:
/**
* Configuration for an event_base.
*
* There are many options that can be used to alter the behavior and
* implementation of an event_base. To avoid having to pass them all in a
* complex many-argument constructor, we provide an abstract data type
* where you set up configuration information before passing it to
* event_base_new_with_config().
*
* @see event_config_new(), event_config_free(), event_base_new_with_config(),
* event_config_avoid_method(), event_config_require_features(),
* event_config_set_flag(), event_config_set_num_cpus_hint()
*/
struct event_config
#ifdef EVENT_IN_DOXYGEN_
{/*Empty body so that doxygen will generate documentation here.*/}
#endif
;
event_base的配置:
有许多选项可用于更改event_ base的行为和实现。为了避免在复杂的多参数构造函数中传递它们,我们提供了一种抽象数据类型,您可以在其中设置配置信息,然后再将其传递给event_base_new_with_config()。
* @see event_config_new(), event_config_free(), event_base_new_with_config(),
* event_config_avoid_method(), event_config_require_features(),
* event_config_set_flag(), event_config_set_num_cpus_hint()
相关函数有点多,先不看了,应该都是改配置相关的吧。
event_config:
/** Internal structure: describes the configuration we want for an event_base
* that we're about to allocate. */
struct event_config {
//这个队列中存放的是config中需要避免的IO多路复用模型
TAILQ_HEAD(event_configq, event_config_entry) entries;
//CPU的个数,仅仅在win下有用
int n_cpus_hint;
// 检查两次新事件之间的最大时间间隔
struct timeval max_dispatch_interval;
// 两次检查新事件之间的执行回调函数个数的最大个数
int max_dispatch_callbacks;
//任务的优先级限制
int limit_callbacks_after_prio;
enum event_method_feature require_features;//多路IO复用函数应该满足哪些特征
enum event_base_config_flag flags;
};
内部结构:描述我们要分配的事件库的配置。其中flags:
event_base_new:
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
struct event_config *cfg = event_config_new();
if (cfg) {
base = event_base_new_with_config(cfg);
event_config_free(cfg);
}
return base;
}
可以看出先通过event_config_new()创建配置文件,(如果配置文件创建成功)通过配置文件创建event_base,然后释放配置配置文件内存。所以重点应该是通过配置文件创建event_base的event_base_new_with_config函数,不过这个函数有点长,还是先看看配置文件怎么创建的吧
event_config_new:
struct event_config *
event_config_new(void)
{//申请内存,并全置1
struct event_config *cfg = mm_calloc(1, sizeof(*cfg));
if (cfg == NULL)
return (NULL);//内存申请失败,返回NULL
//tailqueue最重要的特点就是在head中增加了一个指向末尾的指针,所以它能够直接在链表的尾部插入数据
TAILQ_INIT(&cfg->entries);//初始化一个尾部队列
cfg->max_dispatch_interval.tv_sec = -1;//检查两次新事件之间的最大时间间隔
cfg->max_dispatch_callbacks = INT_MAX;//两次检查新事件之间的执行回调函数个数的最大个数
cfg->limit_callbacks_after_prio = 1;//任务优先级限制,限制低优先级的任务执行
return (cfg);
}
然后,开始new一个event_base,event_base_new_with_config:
struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{
int i;
struct event_base *base;
int should_check_environment;
//如果是debug event_debug_mode_too_late 置1 干嘛的,不知道
#ifndef EVENT__DISABLE_DEBUG_MODE
event_debug_mode_too_late = 1;
#endif
//申请内存,自定义的内存管理函数,总之就是申请内存空间并初始化
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_BASE_FLAG_IGNORE_ENV按位与 然后和 cfg 与
//EVENT_BASE_FLAG_IGNORE_ENV:选择多路IO复用函数时,不检测EVENT_*环境变量。
//所以就是 当cfg合法并且 设置了EVENT_BASE_FLAG_IGNORE_ENV时 should_check_environment 置为0。
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
{
struct timeval tmp;
// EVENT_BASE_FLAG_PRECISE_TIMER:通常情况下我们使用的是更快的计数器,如果设置了这个标志的话我们
// 会使用更精确的时间
//默认情况下flags采用的是EV_MONOT_PRECISE
//设置了EVENT_BASE_FLAG_PRECISE_TIMER precise_time置1
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;
if (precise_time) {
base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER;
}
}
flags = precise_time ? EV_MONOT_PRECISE : 0;
// POSIX clock_gettime接口提供获得monotonic时间的方式。CLOCK_MONOTONIC基本上都是支持的;
// linux也提供CLOCK_MONOTONIC_COARSE模式,大约1-4毫秒的准确性。
// 所有平台上,CLOCK_MONOTONIC实际上是单调递增的。
// 此函数最终的结果是获取CLOCK_MONOTONIC模式的时间,并将event_base的时钟模式设置为
// CLOCK_MONOTONIC模式
// flags默认为1
evutil_configure_monotonic_time_(&base->monotonic_timer, flags);
// 根据“base”将“tp”设置为当前时间。我们必须把锁锁在“base”上。如果存在缓存时间,则返回它。
// 否则,使用clock_gettime或gettimeofday(视情况而定)查找正确的时间。
// 成功时返回0,失败时返回1。
gettime(base, &tmp);
}
//-----初始化参数-----
//最小堆最小堆,是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值。
//libevent的最小堆是通过数组的形式实现的,索引从0开始
min_heap_ctor_(&base->timeheap);
//signal.c中默认信号处理实现的数据结构
//用于从信号处理程序发送通知的Socketpair
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
//一种宽度足以容纳“socket()”或“accept()”输出的类型。在Windows上,这是一个intptr_;在其他地方,它是一个int。
//一些th_ notify函数用来唤醒主线程的socketpair。
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
//active_later_queue:尾部队列函数event_callbacks的列表,下次处理事件时应该激活,但这次不会。
//初始化active_later_queue
TAILQ_INIT(&base->active_later_queue);
//io:从文件描述符到已启用(添加)事件的映射
//初始化事件_映射以供使用
evmap_io_initmap_(&base->io);
//sigmap:从信号号到启用(添加)事件的映射
//初始化sigmap
evmap_signal_initmap_(&base->sigmap);
//changelist:在下一次调度时告知后端的更改列表。仅由O(1)后端使用。
//自上次调用eventop.dispatch以来的“更改”列表。仅在后端使用变更集时维护。
//初始化changelist
event_changelist_init_(&base->changelist);
//指向后端特定数据的指针。
base->evbase = NULL;
//根据cfg配置初始化参数
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)
//如果执行回调函数个数的最大个数为INT_MAX,而且最大时间间隔为-1 ,任务优先级置为INT_MAX
base->limit_callbacks_after_prio = INT_MAX;
//按优先顺序排列的后端数组,确定系统环境
//选择IO复用结构体
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
//确定是否应避免使用此后端(IO复用)
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;
//找到一个满足条件的IO多路复用
//函数指针和其他数据来描述这个event_base的后端(系统环境)
base->evsel = eventops[i];
//指向后端特定数据的指针
//初始化ev_base,并且会对信号监听的处理也进行初始化
base->evbase = base->evsel->init(base);
}
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 */
//llocate单个活动事件队列
if (event_base_priority_init(base, 1) < 0) {
event_base_free(base);
return NULL;
}
/* prepare for threading */
//准备线程
/*
libevent关于多线程的使用需要在所有的初始化之前加evthread_use_pthreads()函数的原因:
evthread_use_pthreads()定义在evthread_pthread.c里面。
在这个函数里,初始化了一个evthread_lock_callbacks对象 cbs,
然后调用evthread_set_lock_callbacks(&cbs);来的对cbs这个evthread_lock_callbacks对象做操作。
evthread_set_lock_callbacks定义在evthread.c里面。
在这个函数里,其实就是将cbs的值赋值给了全局变量_evthread_lock_fns。
在定义了_EVENT_DISABLE_THREAD_SUPPORT的情况下
在add_event函数里面,libevent调用了EVBASE_ACQUIRE_LOCK这个宏。
这个宏定义在evthread-internal.h, 同时EVBASE_ACQUIRE_LOCK这个宏又调用了EVLOCK_LOCK,
EVLOCK_LOCK又调用了全局变量_evthread_lock_fns的lock成员。这个_evthread_lock_fns就是之前说过的那个。
所以其实就是调用了evthread_use_pthreads()函数设置的_evthread_lock_fns这个结构体的lock成员。
而这个lock成员函数,根据evthread_use_pthreads()函数里面设置的值,就是evthread_posix_lock函数,其中参数mode是0,
参数_lock是base.th_base_lock。所以其实就是pthread_mutex_lock(base.th_base_lock)
在没有定义_EVENT_DISABLE_THREAD_SUPPORT的情况下
在add_event函数里面,libevent调用了EVBASE_ACQUIRE_LOCK这个宏。这个宏定义在evthread-internal.h,
同时EVBASE_ACQUIRE_LOCK这个宏又调用了EVLOCK_LOCK,EVLOCK_LOCK又调用了函数_evthreadimpl_lock_lock(),
参数mode是0,参数lock是base.th_base_lock。_evthreadimpl_lock_lock定义在evthread.c里面。
在_evthreadimpl_lock_lock函数里面,会先判断全局变量_evthread_lock_fns的lock存不存在。
如果存在就调用_evthread_lock_fns的lock成员,相当于就是调用evthread_posix_lock函数了,
就和定义了_EVENT_DISABLE_THREAD_SUPPORT的情况一样了。如果不存在就什么都不干,返回0。
因为我的环境里面是没有定义_EVENT_DISABLE_THREAD_SUPPORT的,所以如果不在开始的时候调用evthread_use_pthreads(),
那么全局变量_evthread_lock_fns就没有被赋值,他的lock成员自然也就是NULL了。所以,EVBASE_ACQUIRE_LOCK宏其实什么都没干,
也就没有加锁,所以在多个线程里面add_event会乱掉
*/
#if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE)
event_debug_created_threadable_ctx_ = 1;
#endif
//如果定义了EVENT__DISABLE_THREAD_SUPPORT 设置base->th_base_lock base->current_event_cond
#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);
}
似懂非懂,迷迷糊糊