前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >libevent源码深度剖析十一 时间管理

libevent源码深度剖析十一 时间管理

作者头像
范蠡
发布于 2018-07-25 08:15:35
发布于 2018-07-25 08:15:35
74700
代码可运行
举报
运行总次数:0
代码可运行

系列目录

(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点

为了支持定时器,libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数、时间缓存、时间校正和定时器堆的时间值调整等。下面就结合源代码来分析一下。

1.初始化检测

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1static void detect_monotonic(void){
2#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
3    struct timespec    ts;
4    if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
5        use_monotonic = 1; // 系统支持monotonic时间
6#endif
7}

Monotonic时间指示的是系统从boot后到现在所经过的时间,如果系统支持Monotonic时间就将全局变量use_monotonic设置为1,设置use_monotonic到底有什么用,这个在后面说到时间校正时就能看出来了。

2.时间缓存

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1static int gettime(struct event_base *base, struct timeval *tp){
 2    // 如果tv_cache时间缓存已设置,就直接使用
 3    if (base->tv_cache.tv_sec) {
 4        *tp = base->tv_cache;
 5        return (0);
 6    }
 7    // 如果支持monotonic,就用clock_gettime获取monotonic时间
 8#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
 9    if (use_monotonic) {
10        struct timespec    ts;
11        if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
12            return (-1);
13        tp->tv_sec = ts.tv_sec;
14        tp->tv_usec = ts.tv_nsec / 1000;
15        return (0);
16    }
17#endif
18    // 否则只能取得系统当前时间
19    return (evutil_gettimeofday(tp, NULL));
20}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1int event_base_loop(struct event_base *base, int flags){
 2    // 清空时间缓存
 3    base->tv_cache.tv_sec = 0;
 4    while(!done){
 5        timeout_correct(base, &tv); // 时间校正
 6        // 更新event_tv到tv_cache指示的时间或者当前时间(第一次)
 7         // event_tv <--- tv_cache
 8        gettime(base, &base->event_tv);
 9        // 清空时间缓存-- 时间点1
10        base->tv_cache.tv_sec = 0;
11        // 等待I/O事件就绪
12        res = evsel->dispatch(base, evbase, tv_p);
13        // 缓存tv_cache存储了当前时间的值-- 时间点2
14         // tv_cache <--- now
15        gettime(base, &base->tv_cache);
16        // .. 处理就绪事件
17    }
18    // 退出时也要清空时间缓存
19    base->tv_cache.tv_sec = 0;
20    return (0);
21}

时间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时间,用户可能会手动的调整时间,如果时间被向前调整了(MS前面第7部分讲成了向后调整,要改正),比如从5点调整到了3点,那么在时间点2取得的值可能会小于上次的时间,这就需要调整了,下面来看看校正的具体代码,由函数timeout_correct()完成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1static void timeout_correct(struct event_base *base, struct timeval *tv){
 2    struct event **pev;
 3    unsigned int size;
 4    struct timeval off;
 5    if (use_monotonic) // monotonic时间就直接返回,无需调整
 6        return;
 7    gettime(base, tv); // tv <---tv_cache
 8    // 根据前面的分析可以知道event_tv应该小于tv_cache
 9    // 如果tv < event_tv表明用户向前调整时间了,需要校正时间
10    if (evutil_timercmp(tv, &base->event_tv, >=)) {
11        base->event_tv = *tv;
12        return;
13    }
14    // 计算时间差值
15    evutil_timersub(&base->event_tv, tv, &off);
16    // 调整定时事件小根堆
17    pev = base->timeheap.p;
18    size = base->timeheap.n;
19    for (; size-- > 0; ++pev) {
20        struct timeval *ev_tv = &(**pev).ev_timeout;
21        evutil_timersub(ev_tv, &off, ev_tv);
22    }
23    base->event_tv = *tv; // 更新event_tv为tv_cache
24}

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

4.小节

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-05-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能服务器开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
libevent源码深度剖析七 事件主循环
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
9730
libevent源码深度剖析七 事件主循环
深入理解libevent事件库的原理与实践技巧
libevent是一个事件通知库;封装了reactor。 libevent API 提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时而导致的回调。 libevent 旨在替换在事件驱动的网络服务器中找到的事件循环。应用程序只需要调用event_dispatch(),然后动态添加或删除事件,而无需更改事件循环。
Lion Long
2024/10/20
3330
深入理解libevent事件库的原理与实践技巧
libevent源码深度剖析(六) 初见事件处理框架
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
1.2K0
libevent源码深度剖析九 集成定时器事件
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
1.4K0
libevent源码深度剖析九 集成定时器事件
精通Linux时间管理,从这7个接口开始
最近的项目开发中,频繁遇到了时间戳相关的问题,如时间回退至1970年、时区错误及时间同步不准确等。鉴于此前仅对时间接口的使用有所了解而未深入探究其原理,本篇文章进行一次系统性整理,以便后续参考。文章若存在一些错误,可在留言区明确指出。
开源519
2025/02/27
860
精通Linux时间管理,从这7个接口开始
libevent实现定时器
在之前的文章里我们讲过,libevent最后处理都是在event_base_loop调用了相应的dispatch函数,定时器也是在dispatch函数中处理的。
cpp加油站
2021/04/16
1.3K0
libevent的事件机制
从上一篇文章《libevent是怎么选择底层实现的》可以看出来,调用event_base_new()函数就是初始化好底层实现,给event_base结构体中evsel赋值,evsel是一个eventop结构体,我们再来看下:
cpp加油站
2021/04/16
8240
libevent源码深度剖析八 集成信号处理
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
8560
libevent源码深度剖析八 集成信号处理
libevent源码深度剖析三 libevent基本使用场景和事件流程
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
2K1
libevent源码深度剖析三 libevent基本使用场景和事件流程
libevent源码深度剖析(五) libevent的核心:事件event
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
1.2K0
libevent源码深度剖析(五) libevent的核心:事件event
Netty杂记
入参timeoutNanos设置执行任务的超时时间. 一旦超过这个设定的时间,则停止执行任务.
书唐瑞
2022/06/02
4570
Netty杂记
libevent源码深度剖析十 支持I/O多路复用技术
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
7690
linux环境下的时间编程
Linux下提供了丰富的api以供开发者们处理和时间相关的问题。然而这些接口看似各自为政实则有有着千丝万缕的联系,在学习和时间中引发了各种各样的混乱。因此时间处理成为了许多Linux开发者的梦魇,遇到时间处理往往避之不及。不过只要你稍微花费一点点精力,学会在Linux上优雅的处理时间和日期也并不是什么难事。
不会飞的小鸟
2020/03/27
3.4K0
深入Linux C/C++ Timer定时器的实现核心原理
我曾以为像定时器这样基础的功能,操作系统会有一个完备的实现。当需要开启一个定时任务的时候,会有一个优雅的、如下形式的接口:
sunsky
2020/12/21
11.3K0
深入Linux C/C++ Timer定时器的实现核心原理
nginx架构模型分析
Nginx由内核和模块组成,从官方文档http://nginx.org/en/docs/下的Modules reference可以看到一些比较重要的模块,一般分为核心、基础模块以及第三方模块。
苏先生
2019/05/17
9840
linux时间相关结构体和函数整理
一、时间类型。Linux下常用的时间类型有4个:time_t,struct timeb, struct timeval,struct timespec,clock_t, struct tm. (1) time_t是一个长整型,一般用来表示用1970年以来的秒数. 该类型定义在<sys/time.h>中. 一般通过 time_t time = time(NULL); 获取. (2) struct timeb结构: 主要有两个成员, 一个是秒, 另一个是毫秒, 精确度为毫秒. 1 struct timeb 2
猿人谷
2018/01/17
2.9K0
【C++】开源:事件驱动库libevent配置使用
项目Github地址:https://github.com/libevent/libevent
DevFrank
2024/07/24
5660
ffmpeg 源码分析之 ffplay 主流程
很久没有研究 ffmpeg了,版本变化很大,用一天时间先把 ffplay 翻出来看看。
字节流动
2021/06/09
9610
libevent源码深度剖析二 Reactor模式
(1)libevent源码深度剖析一 序 (2)libevent源码深度剖析二 Reactor模式 (3)libevent源码深度剖析三 libevent基本使用场景和事件流程 (4)libevent源码深度剖析四 libevent源代码文件组织 (5)libevent源码深度剖析五 libevent的核心:事件event (6)libevent源码深度剖析六 初见事件处理框架 (7)libevent源码深度剖析七 事件主循环 (8)libevent源码深度剖析八 集成信号处理 (9)libevent源码深度剖析九 集成定时器事件 (10)libevent源码深度剖析十 支持I/O多路复用技术 (11)libevent源码深度剖析十一 时间管理 (12)libevent源码深度剖析十二 让libevent支持多线程 (13)libevent源码深度剖析十三 libevent信号处理注意点
范蠡
2018/07/25
1.2K0
libevent源码深度剖析二 Reactor模式
linux下libevent的安装和使用例子:数据回显
http://blog.csdn.net/fall221/article/details/9045353 (安装)
bear_fish
2018/09/20
3.4K0
linux下libevent的安装和使用例子:数据回显
相关推荐
libevent源码深度剖析七 事件主循环
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验