clock_monotonic 是唯一靠谱的选择,因其提供内核滴答驱动、不受系统时钟调整影响的单调递增时间;gettimeofday 和 std::chrono::system_clock::now() 易受跳变干扰,clock_process_cputime_id 仅统计 cpu 占用时间,非流逝时间。

直接用 clock_gettime(CLOCK_MONOTONIC, &ts),别碰 gettimeofday 或 std::chrono::system_clock::now() —— 它们要么受系统时钟调整干扰,要么语义不是内核单调时间。
为什么 CLOCK_MONOTONIC 是唯一靠谱的选择
你真正要的“高精度内核时间”,本质是内核滴答驱动的、不随 settimeofday 或 NTP 调整跳变的单调递增计数器。Linux 内核通过 vvar 页暴露这个值,CLOCK_MONOTONIC 就是它的标准用户态接口。
常见错误包括:
- 用
gettimeofday:它返回的是 wall-clock 时间,adjtime或ntpdate可能导致倒流或跳变 - 用
std::chrono::system_clock::now():底层通常映射到gettimeofday或clock_gettime(CLOCK_REALTIME),同样不稳定 - 误以为
CLOCK_PROCESS_CPUTIME_ID是“内核时间”:它只统计本进程实际占用 CPU 的时间,sleep 期间不走,和“流逝时间”完全不是一回事
必须包含 <time.h></time.h>,链接时加 -lrt(部分旧构建环境需显式指定);返回值为 0 表示成功,-1 表示失败(查 errno)。
如何正确读取并转成 double 秒数
struct timespec 的 tv_sec 是秒,tv_nsec 是纳秒,相加后转 double 才便于计算差值。别手写除法或拼字符串——容易溢出或精度丢失。
推荐写法:
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
double now_sec = ts.tv_sec + ts.tv_nsec * 1e-9;
}
注意点:
-
ts.tv_nsec范围是 [0, 999999999],不能直接除以 1000000000.0 后加tv_sec—— 浮点舍入可能在极端情况下吃掉 1 纳秒 - 不要用
std::chrono::duration_cast<:chrono::nanoseconds></:chrono::nanoseconds>包一层再转,那只是绕路,且可能引入额外开销 - 如果要做两次采样求差,直接用整数运算更安全:
(ts2.tv_sec - ts1.tv_sec) * 1000000000LL + (ts2.tv_nsec - ts1.tv_nsec)
跨平台 fallback 不等于“兼容所有系统”
macOS 和 Windows 都不支持 CLOCK_MONOTONIC。但它们各自有等效替代:
- macOS:用
mach_absolute_time()+mach_timebase_info()换算,结果单位是纳秒,语义一致 - Windows:用
QueryPerformanceCounter()+QueryPerformanceFrequency(),注意频率可能因电源策略动态变化,需缓存一次
关键不是“有没有函数”,而是“是否单调、是否内核级、是否不受用户态时间调整影响”。很多所谓“跨平台封装”把 GetTickCount64 或 std::chrono::steady_clock 直接当 CLOCK_MONOTONIC 用,这是错的 —— 前者会溢出(虽是 64 位,但文档未保证单调性),后者在某些 MSVC 实现中仍基于 QueryPerformanceCounter,但标准未规定其是否内核滴答源。
真正难的不是调哪个函数,而是确认你拿到的那个数字,确实是内核调度器维护的、不可逆的、纳秒级分辨率的时间戳 —— 其他都是妥协。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











