当前位置:首页>Linux>Linux 内核时间三模型:jiffies、ktime_t 与 timespec64 是怎样各司其职的

Linux 内核时间三模型:jiffies、ktime_t 与 timespec64 是怎样各司其职的

  • 2026-10-11 05:54:02
Linux 内核时间三模型:jiffies、ktime_t 与 timespec64 是怎样各司其职的

应用里写个 sleep(1),进程真的睡了精确到 1 秒。但内核里没有"秒"这个东西——它只有三套不同的时间表示法,做着不同的事。

第一套:jiffies,内核自己的节拍计数器

内核最朴素的时间概念是一个全局计数器 jiffies。时钟中断的频率由 HZ 决定,x86_64 默认配置是 CONFIG_HZ_1000,也就是每秒中断 1000 次:

// arch/x86/configs/x86_64_defconfigCONFIG_HZ_1000=y

如果系统开了 CONFIG_HZ_PERIODIC,每次时钟中断 jiffies 就加 1,jiffies 自增一次 = 过去 1 毫秒。但现代 x86_64 发行版默认开的是 CONFIG_NO_HZ_IDLE=y(动态 tick),CPU 空闲时周期性时钟中断会被关闭。等 CPU 被事件唤醒时,内核读取 ktime 计算出空闲了多少纳秒,一次性补上缺失的节拍数:

jiffies += delta / (NSEC_PER_SEC / HZ)

逻辑语义没变(1 个 jiffies 代表 1ms),但实现上不是简单的"每次中断加 1"了。它是个"计数器",不表示真实日期,系统重启就归零。内核里很多低精度的时间判断就基于它——比如"这个锁我等了超过 5 秒吗",算成 jiffies + 5*HZ 个节拍来比。

第二套:ktime_t,纳秒时间戳

高精度场景(比如网络包时间戳、调度延迟统计)需要纳秒级。内核用 ktime_t 表示:

// linux/ktime.htypedef s64 ktime_t;

就是个有符号 64 位整数,单位是纳秒。加减乘除直接用运算符:

#define ktime_sub(lhs, rhs)    ((lhs) - (rhs))#define ktime_add_ns(kt, nsval)    ((kt) + (nsval))

ktime_sub(a, b) 得到两个时间点相差多少纳秒。它是纯数值,本身不携带时钟源信息——你拿它存"上次检查的时间点"也行,"两个事件相差多少纳秒"也行,语义由调用方约定。

当然,数值来源是有根的:ktime_get() 系列函数经过 timekeeping 层,而 timekeeping 绑定了当前选中的硬件 clocksource(通常是 TSC),最终从真实的硬件计数器换算得来。

内核内部所有高精度的时间计算、传递,用的都是它。

第三套:timespec64,用户态能看懂的墙钟

应用调用 clock_gettime(CLOCK_MONOTONIC, &ts),拿到的是这种结构:

// linux/time64.hstruct timespec64 {    time64_t    tv_sec;     /* seconds */    long        tv_nsec;    /* nanoseconds */};

秒 + 纳秒,跟 ktime_t 不冲突——ktime_t 是单一纳秒整数,timespec64 是秒和纳秒拆开。内核内部计算用 ktime_t,要给用户态时就转成 timespec64。

clock_gettime 的第一个参数决定你读的是哪座钟。内核定义了十几种时钟源:

// include/uapi/linux/time.h#define CLOCK_REALTIME          0   // 墙上时钟,可以被 NTP 调快调慢,甚至跳变#define CLOCK_MONOTONIC         1   // 单调时钟,从启动开始,永不回退#define CLOCK_MONOTONIC_RAW     4   // 裸单调,不受 NTP 调整影响#define CLOCK_BOOTTIME          7   // 包含 suspend 时长#define CLOCK_TAI               11  // 国际原子时,不带闰秒补偿

最关键的区分是 CLOCK_REALTIME 和 CLOCK_MONOTONIC。REALTIME 是墙上挂钟,NTP 同步时可能被往前拨或往后拨——你刚记录 10:00:00,下一秒读出来可能是 09:59:58。MONOTONIC 从系统启动开始单向增长,不跳变。做超时判断、性能计时永远用 MONOTONIC,用 REALTIME 会因为时钟回拨导致逻辑出错。

硬件时钟源:三套怎么串起来的

底层的精度不来自软件,来自硬件。CPU 有 TSC(时间戳计数器),主板有 HPET、ACPI PM Timer。内核启动时选一个最快最稳的注册成 clocksource:

硬件 TSC/HPET    ↓ 读计数器clocksource (抽象层)    ↓ 统一接口timekeeping (维护系统时间)    ↓ 提供读接口CLOCK_MONOTONIC / CLOCK_REALTIME

timekeeping 是中枢,它把硬件计数器的原始 tick 换算成纳秒,再分发给各个 clock id。应用读 clock_gettime 时,最终落到 timekeeping_get_ns(),读一下当前 clocksource 的计数值,算一下偏移,返回纳秒。

这就是为什么 clock_gettime 很快——它基本就是读一下 TSC 然后做一道乘法,不进内核态切换(vDSO 机制把这段代码映射到用户态直接跑)。

三套表示法,各用各的地盘

总结一下它们的分工:

  • • jiffies:专供内核低精度逻辑(调度时间片、锁超时判断),基于时钟中断,1ms 精度,重启归零
  • • ktime_t:内核内部传递时间用的标准货币,s64 纳秒整数,从 TSC → clocksource → timekeeping 换算得来,高精度、高频访问
  • • timespec64:分发给用户态时的表示形式(秒+纳秒),通过 clock_gettime 返回给应用,vDSO 加速

应用层一句 clock_gettime(CLOCK_MONOTONIC, &ts),内核背后跑了三层转换:硬件 TSC → timekeeping 换算纳秒 → 拆成 timespec64 的秒和纳秒返回给你。

源码:linux/ktime.h、linux/time64.h、linux/jiffies.h、include/uapi/linux/time.h(v6.6)。x86_64 默认 HZ 见 arch/x86/configs/x86_64_defconfig。

最新文章

随机文章