在分布式系统各个通信的过程中,有的应用场景需要把事件发生的时间戳放在消息中一起传递,接收端根据时间戳来判断事件发生的先后顺序。为此,就需要能够获取精确时间的函数,比如下面的代码:
clock_gettime(CLOCK_MONOTONIC, &ts);
msg.hb_send_ts = ts;
ret_val = msg_send(dest_id, MSG_OP_HBEAT, (char *)&msg, sizeof(msg));
此外,在性能统计的过程中,我们也需要设置固定的时间间隔,以便获取期间的运算次数或者IO数量。比如下面的代码:
pthread_mutex_lock(&(info->stat_lock));
if (info->stat_run != 0) {
info->stat_run = 0;
clock_gettime(CLOCK_MONOTONIC, &tv);
tv.tv_sec += 1;
pthread_cond_timedwait(&(info->stat_thd_exist), &(info->stat_lock), &tv);
pthread_mutex_unlock(&(info->stat_lock));
}
上面的两个应用场景中都用到了clock_gettime()函数,问题是这种调用方法正确么?
这就要求对clock_gettime()的不同clk_id有深入的了解,根据个人的理解,下表列出了不同类型的区别:
这里需要补充的是:上面提到的NTP (NetWork Time Protocal)里后台会调用adjtime(),因此不收NTP影响也就不会受adjtime()函数影响。
在我们实际运行的Linux操作系统中,比如CentOS 7中, NTP服务chronyd默认会开启,用户可以参考下面的命令去检查:
[[email protected] ~]# systemctl status chronyd
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2017-09-27 16:52:07 CST; 3h 9min ago
Main PID: 1287 (chronyd)
CGroup: /system.slice/chronyd.service
└─1287 /usr/sbin/chronyd -u chrony
Sep 27 16:52:07 localhost.localdomain systemd[1]: Starting NTP client/server...
Sep 27 16:52:07 localhost.localdomain chronyd[1287]: chronyd version 1.29.1 starting
Sep 27 16:52:07 localhost.localdomain chronyd[1287]: Linux kernel major=3 minor=10 patch=0
Sep 27 16:52:07 localhost.localdomain chronyd[1287]: hz=100 shift_hz=7 freq_scale=1.00000000 nominal_tick=10000 slew_delta_tick=833 max_tick_bias=1000 shift_pll=2
Sep 27 16:52:07 localhost.localdomain chronyd[1287]: Frequency -1.497 +/- 1.312 ppm read from /var/lib/chrony/drift
Sep 27 16:52:07 localhost.localdomain systemd[1]: Started NTP client/server.
Sep 27 16:52:17 localhost.localdomain chronyd[1287]: Selected source 120.25.115.20
在此情况下,如果clk_id使用CLOCK_MONOTONIC,显然会受NTP的影响,这会导致时间的时间间隔不准。为此,根据上面的对比表,上面的列举的两个应用场景,应该使用CLOCK_MONOTONIC_RAW 。读者,如果对具体的差异感兴趣,可以在LInux下输入man clock_gettime()查看具体的描述信息。
据此举一反三,不但在读取时间或时间间隔的时候,我们需要考虑到不同始终类型是否受系统时间设置函数以及NTP的影响。在调用设置时间的函数的时候,也需要考虑到对系统各种时钟会带来何种改变,这样才能保证在多个软件模块组成的系统中,不会因为时间读取、时钟设置、NTP服务等带来误差甚至错误。