获取timespec的第二种方法就是使用posix的clock_gettime,它不仅能获得自1970/1/1开始的时间,还可以自定义clock的类型以便获取不同的时间值,现在是被推荐的用于获取时间的接口,在需要获取较高精度的时间值时应该优先考虑使用它:
#include <stdio.h> #include <string.h> #include <time.h> void print_time(const char *id, const struct timespec *t) { printf("%s:\nseconds: %ld nanoseconds: %ld\n\n", id, t->tv_sec, t->tv_nsec); } // 获取不同时钟的时间值并打印,不支持的时钟类型会让clock_gettime返回-1 // 你不应该模仿这个宏,我只是单纯在偷懒而已 #define get_clock(clk_id) \ do { \ if (clock_gettime(clk_id, &t) != 0) { \ printf("this system doesn't support the " #clk_id "clock\n"); \ } \ print_time(#clk_id, &t); \ memset(&t, 0, sizeof(struct timespec)); \ } while (0) int main(void) { struct timespec t = {0, 0}; // 日历时间,UTC get_clock(CLOCK_REALTIME); // 单调时钟时间,从系统启动开始计算 get_clock(CLOCK_MONOTONIC); // 类似单调时钟,但是包含了系统休眠时经过的时间 get_clock(CLOCK_BOOTTIME); }输出如下:
CLOCK_REALTIME: seconds: 1585273480 nanoseconds: 594245824 CLOCK_MONOTONIC: seconds: 12018 nanoseconds: 401860644 CLOCK_BOOTTIME: seconds: 12018 nanoseconds: 401863344因为我的系统并没有休眠,所以BOOTTIME和MONOTONIC的值是相同的。
还有更多的时钟类型,比如基于硬件的更快的单调时钟和系统时钟,记录进程/线程消耗cpu时间的时钟等,具体参见man pages。
timespec的应用也相当广泛,在clock_nanosleep,nanosleep,pthread等系统调用和库中都被广泛使用。
比如在pthread中我们规定等待互斥锁2.5秒,超时就重试或放弃:
struct timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += 2; timeout.tv_nsec += 50000000; pthread_mutex_timedlock(&mutex, &timeout);从上面可以看出超时是根据系统时间进行判断的,通过设置mutex是属性,我们还可以使用更为准确的单调时钟。
总结本文我们介绍了c/c++标准库以及Linux提供的time api一共两套时间处理方案。
对于简单的date time的处理和获取time pointer,标准库的功能就足够了;而对于超时/延时任务以及需要更高精度时间的场合我们需要系统调用的帮助。
两套api间可以在损失微秒/纳秒精度的前提下进行转换,因为tv_sec成员都是time_t类型的。
两套api各有所长,然而都有一个缺点————无法处理时区。在不引入第三方库和自己手动计算的情况下,Linux处理时区的手段只有以下两种:
函数自己定义参数和返回值使用local time还是UTC time;
系统根据环境变量TZ以及配置文件/etc/localtime等改变本地时间(local time)。
因此在处理时间时我们始终要注意当前被处理的时间是解释成本地时间还是UTC时间;同时还要注意获得的时间的本地还是UTC。因此时间处理问题不可避免的变得十分复杂,某些使用夏令时的地区这一问题还会被继续放大。