从boost库到时间戳

本篇涉及的知识点有:

  • gdb 实际案例调试
  • gdb 中可以使用set命令自定义变量、可以实际用ptype命令查看变量、函数返回值、typedef后的类型的真实类型
  • 2038年时间戳问题: 采用32位整型有符号类型存储时间戳,最多可以表示到 2038年1月19日03:14:07 UTC
  • boost 1.67.0版本以前存在的2038年时间戳的问题、以及问题解决方案

一、以问题引入
授权证书一般有到期时间的说法,公司测试同事在测试更新后的证书时,将系统时间调到了2050年,重启服务后发现各个进程的cpu占用率特别高;结合日志分析,发现这些进程 都在不停的刷heartbeat()的日志,也就是在频繁的进行心跳。

信息1:查看代码可知心跳间隔在配置文件中设置的是30秒
信息2:公司使用的mysql服务也在频繁重启,查看日志发现 This MySQL server doesn’t support dates later then 2038
信息3: 将系统时间调到2037年,发现mysql无问题,进程cpu占用率也下来了。

二、确定问题点或问题方向

#include <boost/date_time.hpp>						//boost的date_time模块的头文件
#include <boost/thread.hpp>
#include <iostream>

int main(){
    while(1){
        auto sec = boost::posix_time::seconds(30); //心跳间隔30秒
        boost::this_thread::sleep(sec);
        std::cout<<"=== "<<sec<<std::endl;	       //使用cout代替心跳通信逻辑
    }
    return 0;
}

简化heartbeat()函数逻辑,分别在2037 2038 2050年测试,发现代码内采用的boost库计时功能在2038 2050年不准确(终端频繁打印输出)。
加上其他同事提示int32位存储时间戳会存在溢出的情况,就逐步确定问题是2038年时间戳的方向了

其他辅助:
相同测试代码在 boost-1.58 boost-1.71环境下对比,高版本始终无问题,低版本在2038年有问题,所以细分方向是boost-1.58版本的date_time模块在时间戳方便存在缺陷

三、boost低版本date_time模块在时间戳方面缺陷的解决方法
修改boost/date_time/time_resolution_traits.hpp 文件中 类模板time_resolution_traits的默认模板参数类型
typename var_type = boost::int32_t 替换为 boost::int64_t即可

对比boost-1.71版本内的该文件,可知1.67版本开始,官方已经开始采用int64_t类型来存储时间戳了
在这里插入图片描述
四、gdb定位流程(可选择性阅读)
图1
图1
图 2
在这里插入图片描述
图3
在这里插入图片描述

图4
在这里插入图片描述

图5
在这里插入图片描述

图6
在这里插入图片描述

图7
在这里插入图片描述

图8
在这里插入图片描述

图9
在这里插入图片描述

图10
在这里插入图片描述

图11
在这里插入图片描述
注:实际调试过程可能并没有这么精简,可能会有下面的或者更多的疑问

  • boost::this_thread 这个命名空间在哪个头文件定义的?
  • boost::this_thread::sleep()对应的头文件我找到了,但是函数重载了,会走到哪个函数呢?应该给哪个加断点呢?
  • 在total_seconds()函数返回负数时,我们可能不会直接反应到要去看它的函数定义、看它的类定义、看模板参数使用情况

五、复测
尝试将上述图11定位到的代码修改为int64_t, 再次测试,可以发现在2038年 2050年不会再存在问题(终端不会频繁输出)

六、2038年时间戳的大致梳理

  1. int32_t 本质是 int 类型,即32位有符号整数,取值范围 − 2 31 -2^{31} 231 2 31 − 1 2^{31}-1 2311,即 − 2147483648 -2147483648 2147483648 2147483647 2147483647 2147483647
  2. 使用python脚本输出 2038年1月19日03:14:07 UTC 对应的时间戳,结果是 2147483647
from datetime import datetime, timezone  
import time  
  
# 设置目标日期和时间(UTC)  
target_date_time = datetime(2038, 1, 19, 3, 14, 7, tzinfo=timezone.utc)  
  
# 将datetime对象转换为时间戳(秒)  
timestamp = int(target_date_time.timestamp())  
  
# 打印时间戳  
print(timestamp) 

  1. 使用gdb对时间戳进行尝试
(gdb) set $aa=2147483647    # 赋值32位有符号整型数的最大值给aa,对应的是 2038年1月19日03:14:07 UTC
(gdb) ptype $aa             # 打印aa的类型,结果是int
type = int
(gdb) p $aa                 # 打印aa的值
$8 = 2147483647
(gdb) set $aa=$aa+1         # aa+1,对应的是 2038年1月19日03:14:08 UTC
(gdb) ptype $aa             # 可以看到aa的类型不变
type = int
(gdb) p $aa                 # 超出了int类型的最大值,溢出了,直接变为int类型的最小值
$9 = -2147483648
(gdb)

  1. 如果时间戳采用64位整型数表示,情况会怎么样?
    先明确64位有符号整型数的取值范围 − 2 63 -2^{63} 263 2 63 − 1 2^{63}-1 2631,即 − 9223372036854775808 -9223372036854775808 9223372036854775808 9223372036854775807 9223372036854775807 9223372036854775807
(gdb) set $bb=9223372036854775807    # 赋值64位有符号整型数的最大值给bb
(gdb) ptype $bb                      # 打印bb的类型,输出是long long
type = long long
(gdb) p $bb
$10 = 9223372036854775807
(gdb) p $bb/(12*30*24*60*60)         # 使用64位可以表示2965亿年的时间戳
$11 = 296533308798
(gdb)

  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值