libevent中的时间及相关的管理
在介绍时间之前,先说明几个与时间相关的函数及其用法
1、基础
1.1 clock_gettime(精度比较高,ns级)
#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);
clk_id:检索和设置的clk_id指定的时钟时间
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-10:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
struct timespec结构表示为:
struct timespec
{
time_t tv_sec;//秒
long tv_nsec;//纳秒
};
1.2 ftime(ms级)
在windows下:
#include <sys/timeb.h>
void _ftime(struct _timeb *tp);
而在linux下:
void ftime(struct timeb *tp);
结构体表示为:
struct timeb
{
time_t time;//秒
unsigned short millitm;//毫秒
short timezone;
short dstflag;
}
2、时间检测
在初始化是检测系统是不是支持monotonic,是通过调用clock_gettime来检测的,如果支持将use_monotonic设置为1,具体的实现为
static void
detect_monotonic(void)
{
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec ts;
static int use_monotonic_initialized = 0;
if (use_monotonic_initialized)
return;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
use_monotonic = 1;
use_monotonic_initialized = 1;
#endif
}
3、时间缓存
base中的tv_cache用来记录时间缓存,主要是为了防止频繁的调用系统函数来获取时间,而获取时间的调用为gettime,其代码为
static int
gettime(struct event_base *base, struct timeval *tp)
{
EVENT_BASE_ASSERT_LOCKED(base);
if (base->tv_cache.tv_sec) { //时间缓存秒非0,直接将缓存值返回
*tp = base->tv_cache;
return (0);
}
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
if (use_monotonic) { //支持monotonic,用clock_gettime获取时间
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
return (-1);
tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000;
if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL
< ts.tv_sec) { //如果此次获取的时间与上次更新时间相关大于1秒,就更新base中的tv_clock_diff,并将last_updated_clock_diff设置为此次获取的时间
struct timeval tv;
evutil_gettimeofday(&tv,NULL);
evutil_timersub(&tv, tp, &base->tv_clock_diff);
base->last_updated_clock_diff = ts.tv_sec;
}
return (0);
}
#endif
return (evutil_gettimeofday(tp, NULL));
}
在
event_base_loop
主循环中,在处理分发事件前,总是将时间缓存清
0
,分发后再更新时间缓存
while (!done) {
...........................
clear_time_cache(base);
res = evsel->dispatch(base, tv_p);
......................
update_time_cache(base);
.......................
}
4、时间校正
如果系统支持monotonic,就不需要校正,系统不支持monotonic,而用户可能会修改系统时间,将时间向前调,这时,就需要校正,由函数timeout_correct来完成
static void
timeout_correct(struct event_base *base, struct timeval *tv)
{
/* Caller must hold th_base_lock. */
struct event **pev;
unsigned int size;
struct timeval off;
int i;
if (use_monotonic) //支持monotonic直接返回
return;
/* Check if time is running backwards */
gettime(base, tv); //获取当前时间
if (evutil_timercmp(tv, &base->event_tv, >=)) {//时间不有向前调,将base的event_tv设置为当前时间,返回
base->event_tv = *tv;
return;
}
event_debug(("%s: time is running backwards, corrected",
__func__));
evutil_timersub(&base->event_tv, tv, &off); //计算时间差
/*
* We can modify the key element of the node without destroying
* the minheap property, because we change every element.
*/
pev = base->timeheap.p;
size = base->timeheap.n;
for (; size-- > 0; ++pev) { //将时间堆中的时间减去上面计算出的时间差
struct timeval *ev_tv = &(**pev).ev_timeout;
evutil_timersub(ev_tv, &off, ev_tv);
}
for (i=0; i<base->n_common_timeouts; ++i) {//将超时事件队列中的时间也调整,送去时间差
struct event *ev;
struct common_timeout_list *ctl =
base->common_timeout_queues[i];
TAILQ_FOREACH(ev, &ctl->events,
ev_timeout_pos.ev_next_with_common_timeout) {
struct timeval *ev_tv = &ev->ev_timeout;
ev_tv->tv_usec &= MICROSECONDS_MASK;
evutil_timersub(ev_tv, &off, ev_tv);
ev_tv->tv_usec |= COMMON_TIMEOUT_MAGIC |
(i<<COMMON_TIMEOUT_IDX_SHIFT);
}
}
/* Now remember what the new time turned out to be. */
base->event_tv = *tv;
}
参考:
http://blog.csdn.net/sparkliang/article/category/660506