我是靠谱客的博主 单纯柜子,最近开发中收集的这篇文章主要介绍libevent学习笔记十二:libevent时间管理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

libevent学习笔记十二:libevent时间管理

 

     定时器启动运行,Libevent必然要和系统时间打交道,主要涉及到时间的加减辅助函数、时间缓存、时间校正和定时器堆的时间值调整等。本文就结合源代码来做一下和介绍。

1、系统时间初始化检测

       Libevent在初始化时,会检测系统时间的类型,这个检测是通过调用函数detect_monotonic()完成的,它通过调用clock_gettime()来检测系统是否支持monotonic时钟类型:

static void detect_monotonic(void)
{
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
     struct timespec    ts;                         //定义时间变量
     if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)  //检测是否是monotonic时间
        use_monotonic = 1;                          // 给monotonic时间赋标识
#endif
}

     Monotonic时间指示的是系统从boot启动后到当前时间所经过的时间长度,如果系统支持Monotonic时间就将全局变量use_monotonic设置为1,设置use_monotonic的作用,在后面介绍时间校正时就可以明了。

2、时间缓存

 结构体event_base中的tv_cache,用来记录时间缓存。这个还要从函数gettime()说起,先来看看该函数的代码:

 static int gettime(struct event_base *base, struct timeval *tp)
 {
     // 如果tv_cache时间缓存已设置,就直接使用
     if (base->tv_cache.tv_sec) {
         *tp = base->tv_cache;
         return (0);                //直接返回
     }
     // 如果支持monotonic,就用clock_gettime获取monotonic时间
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
     if (use_monotonic) {          //前面设置use_monotonic  ,这里开始使用
         struct timespec    ts;     //定义struct timespec  
         if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
             return (-1);            //CLOCK_MONOTONIC检测异常后返回
         tp->tv_sec = ts.tv_sec;
         tp->tv_usec = ts.tv_nsec / 1000;
         return (0);                 //正常设置后结束
     }
#endif
     // 否则只能取得系统当前时间
     return (evutil_gettimeofday(tp, NULL));   //取得当前系统时间
}

      如果tv_cache已经设置,那么就直接使用缓存的时间;否则需要再次执行系统调用获取系统时间。
     函数evutil_gettimeofday()用来获取当前系统时间,在Linux下其实就是系统调用gettimeofday();Windows没有提供函数gettimeofday,而是通过调用_ftime()来完成的。
     在每次系统事件循环中,时间缓存tv_cache将会被相应的清空和设置,再次来看看下面event_base_loop的主要代码逻辑:

 int event_base_loop(struct event_base *base, int flags)
 {
     // 清空时间缓存
     base->tv_cache.tv_sec = 0;
     while(!done){
         timeout_correct(base, &tv);          // 时间校正
         // 更新event_tv到tv_cache指示的时间或者当前时间(第一次)   
         // event_tv <--- tv_cache
         gettime(base, &base->event_tv);
         // 清空时间缓存-- 时间点1
         base->tv_cache.tv_sec = 0;
         // 等待I/O事件就绪
         res = evsel->dispatch(base, evbase, tv_p);
         // 缓存tv_cache存储了当前时间的值-- 时间点2
         // tv_cache <--- now
         gettime(base, &base->tv_cache);
         // .. 处理就绪事件
     }
     // 退出时也要清空时间缓存
     base->tv_cache.tv_sec = 0;
     return (0);
}

     时间event_tv指示了dispatch()上次返回,也就是I/O事件就绪时的时间,第一次进入循环时,由于tv_cache被清空,因此gettime()执行系统调用获取当前系统时间;而后将会更新为tv_cache指示的时间。
     时间tv_cache在dispatch()返回后被设置为当前系统时间,因此它缓存了本次I/O事件就绪时的时间(event_tv)。
     从代码逻辑里可以看出event_tv取得的是tv_cache上一次的值,因此event_tv应该小于tv_cache的值。
     设置时间缓存的优点是不必每次获取时间都执行系统调用,这是个相对费时的操作;在上面标注的时间点2到时间点1的这段时间(处理就绪事件时),调用gettime()取得的都是tv_cache缓存的时间。

3、时间校正

     如果系统支持monotonic时间,该时间是系统从boot后到现在所经过的时间,因此不需要执行校正。
     根据前面的代码逻辑,如果系统不支持monotonic时间,用户可能会手动的调整时间,如果时间被向前调整了,比如从5点调整到了3点,那么在时间点2取得的值可能会小于上次的时间,这就需要调整了,下面具体代码可以看到由函数timeout_correct()完成的调整步骤:

 static void timeout_correct(struct event_base *base, struct timeval *tv)
 {
     struct event **pev;
     unsigned int size;
     struct timeval off;
     if (use_monotonic) // monotonic时间就直接返回,无需调整
         return;
     gettime(base, tv); // tv <---tv_cache
     // 根据前面的分析可以知道event_tv应该小于tv_cache
     // 如果tv < event_tv表明用户向前调整时间了,需要校正时间
     if (evutil_timercmp(tv, &base->event_tv, >=)) {
         base->event_tv = *tv;
         return;
     }
     // 计算时间差值
     evutil_timersub(&base->event_tv, tv, &off);
     // 调整定时事件小根堆
     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);
     }
     base->event_tv = *tv; // 更新event_tv为tv_cache
}

    在上面的代码中调整小根堆时,因为所有定时事件的时间值都会被减去相同的值,因此虽然堆中元素的时间键值改变了,但是相对关系并没有改变,不会改变堆的整体结构。因此只需要遍历堆中的所有元素,将每个元素的时间键值减去相同的值即可完成调整,不需要重新调整堆的结构。当然调整完后,要将event_tv值重新设置为tv_cache值了。

 

4、总结

    本文主要分析了一下libevent对系统时间的处理,时间缓存、时间校正和定时堆的时间值调整等,逻辑还是很简单的,时间的加减、设置等辅助函数则非常简单,主要在头文件evutil.h中,这里不做赘述。

 

最后

以上就是单纯柜子为你收集整理的libevent学习笔记十二:libevent时间管理的全部内容,希望文章能够帮你解决libevent学习笔记十二:libevent时间管理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部