一、event_base_new
创建默认的event_base
**
* 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句柄
// 成功则返回新的event_base对象,失败则返回NULL
// 相关查看event_base_free(),event_base_new_with_config
struct event_base *
event_base_new(void)
{
struct event_base *base = NULL;
// 调用event_config_new函数创建默认的struct event_config对象,具体可以参考
// libevent源码分析(5)--2.1.8--libevent配置信息对象struct event_config的申请和释放函数分析
struct event_config *cfg = event_config_new();
if (cfg) {
// 根据输入参数创建libevent句柄-struct event_base对象
base = event_base_new_with_config(cfg);
// 创建对象完毕后,无论成功与否,都需要释放配置信息对象struct event_config
event_config_free(cfg);
}
return base;
}
二、event_base_new_with_config
使用配置信息创建event_base
/**
Initialize the event API.
Use event_base_new_with_config() to initialize a new event base, taking
the specified configuration under consideration. The configuration object
can currently be used to avoid certain event notification mechanisms.
@param cfg the event configuration object
@return an initialized event_base that can be used to registering events,
or NULL if no event base can be created with the requested event_config.
@see event_base_new(), event_base_free(), event_init(), event_assign()
*/
// 初始化libevent 的API
// 使用本函数初始化新的event_base,可以使用配置对象来屏蔽某些特定的
// 事件通知机制
// cfg:事件配置对象
// 返回初始化后的event_base对象,可以用来注册事件;失败则返回NULL
// 查看相关函数: event_base_new(), event_base_free(), event_init(), event_assign()
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
// 使用内部申请函数mm_calloc,申请struct event_base对象
// 并对每个域都赋初值1
if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) {
event_warn("%s: calloc", __func__);
return NULL;
}
// 使用配置对象的event_base工作模式,即:
// 多线程调用是不安全的,单线程非阻塞模式
// EVENT_BASE_FLAG_NOLOCK = 0x01,
// 忽略检查EVENT_*等环境变量
// EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
// 只用于windows
// EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
// 不使用缓存的时间,每次回调都会获取系统时间
// EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
// 如果使用epoll方法,则使用epoll内部的changelist
// EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
// 使用更精确的时间,但是可能性能会降低
// EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
if (cfg)
base->flags = cfg->flags;
// 获取cfg中event_base工作模式是否包含EVENT_BASE_FLAG_IGNORE_ENV
// 即查看是否忽略检查EVENT_*等环境变量
// 默认情况下,cfg->flags = EVENT_BASE_FLAG_NOBLOCK,所以是没有设置忽略环境变量,
// 因此should_check_enviroment = 1,是应该检查环境变量的
should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));
{
struct timeval tmp;
// 获取cfg中event_base工作模式中是否包含使用精确时间的设置
// EVENT_BASE_FLAG_PRECISE_TIMER含义看上文
// 如果使用精确时间,则precise_time为1,否则为0
// 默认配置下,cfg->flags=EVENT_BASE_FLAG_NOBLOCK,所以precise_time = 0
int precise_time =
cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER);
int flags;
// 如果检查EVENT_*等环境变量并且不使用精确时间,
// 则需要检查编译时的环境变量中是否打开了使用精确时间的模式;
// 这一段检查的目的是说,虽然配置结构体struct event_config中没有指定
// event_base使用精确时间的模式,但是libevent提供编译时使用环境变量来控制
// 使用精确时间的模式,所以如果开启检查环境变量的开关,则需要检查是否在编译时
// 打开了使用精确时间的模式。例如在CMakeList中就有有关开启选项
// CMakeLists.txt:1150: "${BACKEND_ENV_VARS};EVENT_PRECISE_TIMER=1")
// CMakeLists.txt:1156: //"${BACKEND_ENV_VARS};EVENT_EPOLL_USE_CHANGELIST=yes;EVENT_PRECISE_TIMER=1")
// 默认情况下,检查环境变量并且precise_time=0,所以需要执行这个if分支
// 执行结果之后,precise_time=1,base->flags = cfg->flags | EVENT_BASE_FLAG_PRECISE_TIMER
// 即base->flags = EVENT_BASE_FLAG_NOBLOCK | EVENT_BASE_FLAG_PRECISE_TIMER = 0x21
if (should_check_environment && !precise_time) {
// evutil_getenv_实际上是getenv函数的封装,用来获取环境变量信息
// 查看上面CMakeList中信息,可以看出实际上默认是打开的
precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL;
base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER;
}
// 根据precise_time的标志信息,确认是否使用MONOT_PRECISE模式
// linux下实际上一般是调用clock_gettime实现
// 默认情况下,precise_time=1,所以flags = EV_MONOT_PRECISE
// 所以默认情况下,使用EV_MONOT_PRECISE模式配置event_base的monotonic_time
flags = precise_time ? EV_MONOT_PRECISE : 0;
// 后面会分析这个函数
evutil_configure_monotonic_time_(&base->monotonic_timer, flags);
// 根据base获取当前的时间
// 如果base中有缓存的时间,则将缓存的时间赋给tmp,然后返回即可;
// 如果base中没有缓存的时间,则使用clock_gettime获取当前系统的monotonic时间;
// 否则根据上次更新系统时间的时间点、更新间隔、以及当前使用clock_gettime等函数获取当前的系统时间查看是否
// 需要更新base->tv_clock_diff以及base->last_updated_clock_diff
// 后面会分析这个函数
gettime(base, &tmp);
}
// 创建timer的最小堆
min_heap_ctor_(&base->timeheap);
// 内部信号通知的管道,0读1写
base->sig.ev_signal_pair[0] = -1;
base->sig.ev_signal_pair[1] = -1;
// 内部线程通知的文件描述符,0读1写
base->th_notify_fd[0] = -1;
base->th_notify_fd[1] = -1;
// 初始化下一次激活的队列
TAILQ_INIT(&base->active_later_queue);
// 初始化IO事件和文件描述符的映射
// 如果实在win32下,则使用hash table,一些宏定义实现,具体可以查看event-internal.h中EVMAP_USE_HT这个编译选项
// 如果实在其他环境下,#define event_io_map event_signal_map
// 则base->io名为struct event_io_map,实际上是struct event_signal_map
// 因此linux下实际上调用的是evmap_signal_initmap_,后面会大体分析一下这个函数
evmap_io_initmap_(&base->io);
// 初始化信号和文件描述符的映射
// 后面会分析这个函数
evmap_signal_initmap_(&base->sigmap);
// 初始化变化事件列表
// 后面会分析这个函数
event_changelist_init_(&base->changelist);
// 在没有初始化后台方法之前,后台方法必需的数据信息为空
base->evbase = NULL;
// 配置event loop的检查回调函数的间隔信息,默认情况下,是不检查的,即一旦event_base确认当前
// 优先级是最高优先级队列,则会一直把该优先级队列中同优先级事件都执行完再检查是否有更高优先级
// 事件发生,在执行过程中,即使有高优先级事件触发,event_base也不会中断去执行;
// 配置之后的优点是:如果配置可以支持优先级抢占,提高高优先级事件执行效率,
// 配置之后的缺点是:但是一旦配置,每次执行完回调函数都需要检查,则会轻微降低事件吞吐量:
// max_dispatch_callbacks:event_base在两次检查新事件之间的执行回调函数个数的最大个数,
// max_dispatch_interval: event_base在两次检查新事件之间消耗的最大时间。
// limit_callbacks_after_prio:是只有优先级数字>=limit_callbacks_after_prio的事件触发时,才会强迫
// event_base去检查是否有更高优先级事件发生,低于这个优先级的事件触发时,不会检查。
// 如果配置信息对象struct 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 {
// max_dispatch_time.tv_sec=-1即不进行此项检查
// limit_callbacks_after_prio=1是指>=1时都需要检查,最高优先级为0,
// 即除了最高优先级事件执行时不需要检查之外,其他优先级都需要检查
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;
// 遍历静态全局变量eventops,对选择的后台方法进行初始化
// eventops定义:
// libevent源码分析(3)--2.1.8--结构体struct event_base和struct eventop
// 我自己测试结果是,默认选择使用epoll方法,而测试环境支持的后台方法包括
// epoll,poll,select
// 下面看一下,程序是怎样选出epoll方法,跳过poll和select的
for (i = 0; eventops[i] && !base->evbase; i++) {
if (cfg != NULL) {
/* determine if this backend should be avoided */
// 如果方法已经屏蔽,则跳过去,继续遍历下一个方法
// 默认情况下,是不屏蔽任何后台方法,除非通过编译选项控制或者使用API屏蔽
if (event_config_is_avoided_method(cfg,
eventops[i]->name))
continue;
// 如果后台方法的工作模式特征和配置的工作模式不同,则跳过去
// 查看libevent源码中epoll.c、poll.c、select.c文件相应静态全局变量的方法定义,
// 发现默认情况下,各个后台方法的特征如下:
// epoll方法的特征是 :EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE
// poll方法的特征是:EV_FEATURE_FDS
// select方法的特征是:EV_FEATURE_FDS
// 默认情况下,cfg中require_features是EV_FEATURE_ET,所以poll和select都会跳过去,只有epoll执行初始化
if ((eventops[i]->features & cfg->require_features)
!= cfg->require_features)
continue;
}
/* also obey the environment variables */
如果检查环境变量,并发现OS环境不支持的话,也会跳过去
if (should_check_environment &&
event_is_method_disabled(eventops[i]->name))
continue;
// 下面两步正确情况下只会选择一个后台方法,也只会执行一次
// 保存后台方法句柄,实际是静态全局变量数组成员,具体定义在每种方法文件中定义
base->evsel = eventops[i];
// 调用相应后台方法的初始化函数进行初始化,这个就用到结构体
// struct eventop中定义的init,具体方法的init实现需要查看每种方法自己的定义
// 以epoll为例,epoll相关实现都在epoll.c中
// 后面会分析epoll的初始化函数
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;
}
// 获取环境变量EVENT_SHOW_METHOD,是否打印输出选择的后台方法名字
if (evutil_getenv_("EVENT_SHOW_METHOD"))
event_msgx("libevent using: %s", base->evsel->name);
/* allocate a single active event queue */
// 分配优先级队列成员个数,前面分配event_base时会填入初始值1,
// 此处分配的优先级队列成员个数为1,则直接返回了
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
// 注意:其他没有显式初始化的域都被前面分配空间时默认初始化为1
return (base);
}
三、event_base_free
释放event_base对象
/**
Deallocate all memory associated with an event_base, and free the base.
Note that this function will not close any fds or free any memory passed
to event_new as the argument to callback.
If there are any pending finalizer callbacks, this function will invoke
them.
@param eb an event_base to be freed
*/
// 释放event_base附属的所有空间,并释放base;
// 注意:这个函数不会关闭任何fds或者释放在执行event_new时任何传递给callback的参数
// 如果未决的关闭类型的回调,本函数会唤醒这些回调
// eb:待释放的event_base
void
event_base_free(struct event_base *base)
{
// 实际上时调用内部释放函数释放的。
event_base_free_(base, 1);
}
四、evutil_configure_monotonic_time_
配置单调递增的时间,默认情况下,会使用EV_MONOTONIC模式获取系统时间,并将event_base的monotonic_clock模式设置为EV_MONOTONIC;
monotonic时间是单调递增的时间,不受系统修改时间影响,对于计算两个时间点之间的时间比较精确;
real时间是系统时间,受系统时间影响
// libevent支持多种操作系统模式,以下只看Linux下的
#if defined(HAVE_POSIX_MONOTONIC)
/* =====
The POSIX clock_gettime() interface provides a few ways to get at a
monotonic clock. CLOCK_MONOTONIC is most widely supported. Linux also
provides a CLOCK_MONOTONIC_COARSE with accuracy of about 1-4 msec.
On all platforms I'm aware of, CLOCK_MONOTONIC really is monotonic.
Platforms don't agree about whether it should jump on a sleep/resume.
*/
// POSIX clock_gettime接口提供获得monotonic时间的方式。CLOCK_MONOTONIC基本上都是支持的;
// linux也提供CLOCK_MONOTONIC_COARSE模式,大约1-4毫秒的准确性。
// 所有平台上,CLOCK_MONOTONIC实际上是单调递增的。
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. */
// CLOCK_MONOTONIC在FreeBSD,Linux,Solaris一般是支持的。你需要运行时检查,因为
// 一些老的内核版本可能不支持
// 如果定义了CLOCK_MONOTONIC_COARSE编译选项,则检查event_base工作模式是否选择了EV_MONOT_PRECISE;
// 如果选择了EV_MONOT_PRECISE,则设置标志位precise,表明选择使用准确的时间模式;
// 默认情况下,flags采用的是EV_MONOT_PRECISE,所以precise=EV_MONOT_PRECISE=1
#ifdef CLOCK_MONOTONIC_COARSE
const int precise = flags & EV_MONOT_PRECISE;
#endif
// 设置fallback标志位,查看是否为EV_MONOT_FALLBACK=2模式
// 默认情况下,flags采用的是EV_MONOT_PRECISE,所以fallback为0
const int fallback = flags & EV_MONOT_FALLBACK;
struct timespec ts;
#ifdef CLOCK_MONOTONIC_COARSE
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");
}
// 如果既没有选择EV_MONOT_PRECISE模式,也没有选择EV_MONOT_FALLBACK模式,则使用
// CLOCK_MONOTONIC_COARSE获取当前系统时间
// 默认情况下选择的是EV_MONOT_PRECISE,所以不走此分支
if (! precise && ! fallback) {
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
base->monotonic_clock = CLOCK_MONOTONIC_COARSE;
return 0;
}
}
#endif
// 如果没有选择EV_MONOT_FALLBACK,则以CLOCK_MONOTONIC模式获取系统时间,并将
// event_base的monotonic_clock模式设置为CLOCK_MONOTONIC;
// 默认情况下,选择的是EV_MONOT_PRECISE,所以此分支执行,
// 此函数最终的结果是获取CLOCK_MONOTONIC模式的时间,并将event_base的时钟模式设置为
// 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;
}
五、gettime函数
此函数主要用来获取当前event_base中缓存的时间,并设置使用系统时间更新event_base缓存时间的时间间隔,并获取当前系统的monotonic时间和当前系统的real时间之间差值并存储在event_base中
/** 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.
*/
// 将tp设置为base的当前时间。必需在base上加锁;如果有缓存的时间,可以反回缓存的时间。
// 否则,需要调用clock_gettime或者gettimeofday获取合适的时间;
static int
gettime(struct event_base *base, struct timeval *tp)
{
EVENT_BASE_ASSERT_LOCKED(base);
// 首先查看base中是否有缓存的时间,如果有,直接使用缓存时间,然后返回即可
if (base->tv_cache.tv_sec) {
*tp = base->tv_cache;
return (0);
}
// 使用monotonic_timer获取当前时间,二选一:
// CLOCK_MONOTONIC_COARSE 和 CLOCK_MONOTONIC
// 默认情况下是 CLOCK_MONOTONIC模式
if (evutil_gettime_monotonic_(&base->monotonic_timer, tp) == -1) {
return -1;
}
// 查看是否需要更新缓存的时间
// 如果上次更新时间的时间点距离当前时间点的间隔超过CLOCK_SYNC_INTERVAL,则需要更新
if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL
< tp->tv_sec) {
struct timeval tv;
// 使用gettimeofday获取当前系统real时间
evutil_gettimeofday(&tv,NULL);
// 将当前系统real时间和monotonic时间做差,存到base中
evutil_timersub(&tv, tp, &base->tv_clock_diff);
// 保存当前更新的时间点
base->last_updated_clock_diff = tp->tv_sec;
}
return 0;
}
六、evmap_io_initmap_函数和evmap_signal_initmap_函数
当不是在win32环境下时,struct event_signal_map和struct event_io_map是相同的
void
evmap_io_initmap_(struct event_io_map* ctx)
{
evmap_signal_initmap_(ctx);
}
初始化struct event_signal_map
void
evmap_signal_initmap_(struct event_signal_map *ctx)
{
ctx->nentries = 0;
ctx->entries = NULL;
}
下面看struct event_signal_map定义
/* Used to map signal numbers to a list of events. If EVMAP_USE_HT is not
defined, this structure is also used as event_io_map, which maps fds to a
list of events.
*/
// 用来存储信号数字和一系列事件之间的映射。如果EVMAP_USE_HT没有定义,即不是在win32下,
// struct event_io_map和struct event_signal_map一致,event_io_map是将fds和事件映射到一块
struct event_signal_map {
/* An array of evmap_io * or of evmap_signal *; empty entries are
* set to NULL. */
// 存放的是evmap_io和evmap_signal对象的指针。空entries设置为NULL
void **entries;
/* The number of entries available in entries */
// 可用项目个数
int nentries;
};
七、初始化变化列表
void
event_changelist_init_(struct event_changelist *changelist)
{
changelist->changes = NULL;
changelist->changes_size = 0;
changelist->n_changes = 0;
}
结构体定义:
/* List of 'changes' since the last call to eventop.dispatch. Only maintained
* if the backend is using changesets. */
// 列举自从上一次eventop.dispatch调用之后的改变列表。只有在后台使用改变集合时才会维护这个列表,否则不维护?
struct event_changelist {
struct event_change *changes;
int n_changes;
int changes_size;
};
八、epoll方法的初始化分析
epoll.c中,epoll后台方法定义:
参考前文中在event.c中定义的静态全局变量数组:eventops,
eventops中存储就是这个epollops。
此处需要注意的是内部信号通知机制的实现,是通过管道传递外部信号的,为何不能像IO事件绑定文件描述符?
因为信号是全局的,无法真对某个事件进行绑定,只能通过函数捕捉信号,然后通过管道通知event_base来实现。
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_nochangelist_add,
epoll_nochangelist_del,
epoll_dispatch,
epoll_dealloc,
1, /* need reinit */
EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE,
0
};
struct epollop结构体为:
struct epollop {
// epoll内部定义的结构体,需要查看epoll源码
struct epoll_event *events;
// epoll中当前注册的事件总数
int nevents;
// epoll返回的句柄
int epfd;
#ifdef USING_TIMERFD
int timerfd;
#endif
};
epoll_init函数为:
static void *
epoll_init(struct event_base *base)
{
int epfd = -1;
struct epollop *epollop;
// epoll已经提供新的创建API,epoll_create1,可以设置创建模式,
// 可以通过编译选项进行配置是否支持
#ifdef EVENT__HAVE_EPOLL_CREATE1
/* First, try the shiny new epoll_create1 interface, if we have it. */
epfd = epoll_create1(EPOLL_CLOEXEC);
#endif
if (epfd == -1) {
/* Initialize the kernel queue using the old interface. (The
size field is ignored since 2.6.8.) */
// 如果内核没有支持epoll_create1,只能使用epoll_create创建
if ((epfd = epoll_create(32000)) == -1) {
if (errno != ENOSYS)
event_warn("epoll_create");
return (NULL);
}
evutil_make_socket_closeonexec(epfd);
}
// 分配struct epollop对象,并赋初值1
if (!(epollop = mm_calloc(1, sizeof(struct epollop)))) {
close(epfd);
return (NULL);
}
// 保存epoll句柄描述符
epollop->epfd = epfd;
/* Initialize fields */
// 初始化epoll句柄可以处理的事件最大个数以及当前注册事件数的初始值
epollop->events = mm_calloc(INITIAL_NEVENT, sizeof(struct epoll_event));
if (epollop->events == NULL) {
mm_free(epollop);
close(epfd);
return (NULL);
}
epollop->nevents = INITIAL_NEVENT;
// 如果libevent工作模式是启用epoll的changelist方式,则后台方法变为epollops_changelist
// 默认情况下是不选择的。
if ((base->flags & EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST) != 0 ||
((base->flags & EVENT_BASE_FLAG_IGNORE_ENV) == 0 &&
evutil_getenv_("EVENT_EPOLL_USE_CHANGELIST") != NULL)) {
base->evsel = &epollops_changelist;
}
#ifdef USING_TIMERFD
/*
The epoll interface ordinarily gives us one-millisecond precision,
so on Linux it makes perfect sense to use the CLOCK_MONOTONIC_COARSE
timer. But when the user has set the new PRECISE_TIMER flag for an
event_base, we can try to use timerfd to give them finer granularity.
*/
// epoll接口通常的精确度是1微秒,因此在Linux环境上,通过使用CLOCK_MONOTONIC_COARSE计时器
// 可以获得完美的体验。但是当用户使用新的PRECISE_TIMER模式时,可以使用timerfd获取更细的时间粒度。
// timerfd是Linux为用户提供的定时器接口,基于文件描述符,通过文件描述符的可读事件进行超时通知,timerfd、
// eventfd、signalfd配合epoll使用,可以构造出零轮询的程序,但是程序没有处理的事件时,程序是被阻塞的;
// timerfd的精度要比uspleep高
if ((base->flags & EVENT_BASE_FLAG_PRECISE_TIMER) &&
base->monotonic_timer.monotonic_clock == CLOCK_MONOTONIC) {
int fd;
fd = epollop->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
if (epollop->timerfd >= 0) {
struct epoll_event epev;
memset(&epev, 0, sizeof(epev));
epev.data.fd = epollop->timerfd;
epev.events = EPOLLIN;
if (epoll_ctl(epollop->epfd, EPOLL_CTL_ADD, fd, &epev) < 0) {
event_warn("epoll_ctl(timerfd)");
close(fd);
epollop->timerfd = -1;
}
} else {
if (errno != EINVAL && errno != ENOSYS) {
/* These errors probably mean that we were
* compiled with timerfd/TFD_* support, but
* we're running on a kernel that lacks those.
*/
event_warn("timerfd_create");
}
epollop->timerfd = -1;
}
} else {
epollop->timerfd = -1;
}
#endif
// 初始化信号通知的管道
// 当设置信号事件时,由于信号捕捉是针对全局来说,所以此处信号捕捉函数是如何通知event_base的呢?
// 答案是通过内部管道来实现的,一旦信号捕捉函数捕捉到信号,则将相应信号通过管道传递给event_base,
// 然后event_base根据信号值将相应的回调事件加入激活事件队列,等待event_loop的回调。
// 下文有分析此函数
evsig_init_(base);
return (epollop);
}