Linux AUX Clock:多时间域的历史痛点、内核原理与同步实践
Linux 早已能提供很多时钟,但过去很难同时满足这样一个需求:多个应用共享几套各自独立校准的时间,其中一套服务于 UTC 日志,另一套跟踪设备或业务参考时钟,而且都能以统一接口低成本读取。
问题不在于缺少 clock_gettime() 的 clock ID,而在于系统时钟背后只有一套核心校时状态。CLOCK_REALTIME 和 CLOCK_TAI 不能分别跟踪两台独立参考时钟;把设备时间同步进系统时间,常常意味着替换原本服务于整台主机的时间基准。直接使用 PHC 或应用自行维护软件时钟虽然能绕开部分限制,却又引入访问成本、接口分裂和跨域转换的复杂性。
AUX clock 功能为此提供了独立于核心 timekeeper、可单独设置与调频的内核软件时钟,并提供 POSIX 读写接口和 vDSO 读取路径。它让额外时间域不必再争用主机墙钟。这是本文的主线:过去的痛点是什么、这一功能具体改变了什么,以及怎样用 linuxptp、chrony 和 PTP 设备构建多时间域系统。
Linutronix 的文章《PTP Multi-Domain Time Sync with Linux》提供了一个直观案例:Global Time 用于绝对时间关联,Working Clock 用于独立的工作周期。TSN 是这种多时间域需求的场景之一;同样的设计问题也会出现在设备采集、分布式测量和需要隔离时间基准的应用中。
本文的内核分析基于 Linux 7.3-rc6 之后的主线快照 v7.3-rc6-78-g47324d3a5b3ab,对应提交为 47324d3a5b3ab。用户态支持状态以 2026-10-15 的 master 分支为准:linuxptp · 227bbd4715bf、chrony · 7fab5413c58e。
过去的痛点:多个时钟名字,仍然只有一套核心校时状态
业务需要的是独立时间域,不只是不同显示格式
假设一台主机同时承担两类任务:日志需要与其他机器的 UTC 时间对齐,采集应用需要跟踪一套独立设备参考时间。两套参考可能有不同的 epoch、频率误差和失锁处理策略。某一套参考重启或发生相位调整时,另一套业务不应被迫改变自己的时间基准。
这里的时间域至少包含四项属性:时间起点、走时速率、校准参考、连续性契约。时间域可以共享硬件振荡器,也可以具有相同的当前读数;只要它们需要独立控制,就不能被当作同一套 clock 的不同格式。
REALTIME、TAI 和 MONOTONIC 没有提供独立的第二套 servo
Linux 的 CLOCK_REALTIME、CLOCK_TAI、CLOCK_MONOTONIC 和 CLOCK_BOOTTIME 来自核心 timekeeper。概念上:
1 | CLOCK_TAI = CLOCK_REALTIME + 内核维护的 TAI offset |
所以 REALTIME 与 TAI 不能分别接受两个独立参考源的调频。TAI 避免 UTC 闰秒对应的时间尺度不连续,但不意味着管理员修改墙钟时它仍保持独立。
MONOTONIC 不随墙钟 step 跳变,却仍会受到核心频率校准影响。MONOTONIC_RAW 不接受这种 NTP 频率校准,但也没有提供一个可被第二套 servo 独立校准的目标。BOOTTIME 与 MONOTONIC 的主要区别是前者计入 suspend 时间,同样没有增加独立校时自由度。
这形成了一个实际矛盾:把设备参考同步进系统 clock,方便应用读钟,却会让整台主机的核心时基跟着该参考运行;保留主机原有 UTC 同步,又缺少标准的第二套可校准系统软件时钟。如果启动两个 daemon 去调整同一个目标,它们会互相抵消校准动作,而不是产生两个时间域。
传统绕行方案解决了什么,又留下了什么
| 过去的做法 | 能解决的需求 | 仍然存在的问题 |
|---|---|---|
| 把另一个 PHC 同步到 REALTIME | 应用通过标准系统 clock 读设备时间 | 替换主机原有校时参考,两个域不能独立控制 |
| 把 REALTIME 和 TAI 当作两个域 | UTC/TAI 之间的尺度换算 | 二者共享核心校时状态,不能分别跟踪不同参考 |
直接读取 /dev/ptpN 的动态 clock ID |
保留设备自身独立时基 | 通常需要进入驱动读钟,读取成本与 vDSO 不同;不自动获得系统定时器能力 |
应用维护 a × counter + b 软件时钟 |
可表达独立 epoch 与频率 | 每套应用/库要处理并发发布、映射更新和接口接入 |
| 增加独立硬件时钟 | 获得更多物理可调目标 | 硬件成本和部署约束增加,CPU 侧统一访问仍需设计 |
用户态软件时钟可以通过共享内存等方式做得很高效;它的局限是这套时钟的生命周期、同步访问和适配责任仍落在应用生态,而不是统一的内核 clock 接口。PHC 本身也一直能独立校准,AUX 并不是让 Linux 第一次拥有多台物理时钟。
AUX clock 功能解决了什么
从一套核心校时状态,扩展到多套独立软件 timekeeper
AUX 的关键变化是把“共享硬件计数器”和“共享校时状态”分开。多个软件时钟可以读取同一个 CPU clocksource,但各自维护时间基准、转换速率、偏移和校准状态。
图中橙色路径表示同步程序的校准动作。AUX 引入前,两个参考争用同一个目标;引入后,核心 timekeeper 与 AUX 分别接受各自的校准。
例如,主机可以继续让 chronyd 维护 UTC 墙钟,同时让 phc2sys 把设备 PHC 同步到 AUX0。应用读取 AUX0,得到的是设备域在 CPU 软件时钟上的表示;这条校准链不必修改 REALTIME 的校准状态。
功能由哪些核心系列实现
AUX clock 功能由独立 timekeeper、POSIX 读写与校准接口、vDSO 快速读取,以及与 PHC 同步所需的跨时钟采样接口共同组成。下面按实现部分列出核心 patch series 的 lore 线程;每个链接都指向系列的 cover letter。
| 实现部分 | 核心 patch series | 作用 |
|---|---|---|
| timekeeper 基础设施 | timekeeping: Provide support for auxiliary timekeepers(V2 00/26,2025-05-19) | 提出独立 AUX timekeeper、clock ID、校准状态与访问接口;其中前 15 项先行合入 |
| POSIX 接口与生命周期 | timekeeping: Provide support for auxiliary clocks - Remaining series(V3 00/11,2025-06-25) | 修订并完成上述 V2 剩余的 11 项,提供读钟、设置时间、调频/调相、周期更新与 sysfs 启停接口 |
| vDSO 快速读取 | vdso: Add support for auxiliary clocks(00/14,2025-07-01) | 将 AUX 转换参数发布到 vDSO 数据页,在支持通用 vDSO 的架构上提供用户态读钟路径 |
| PHC 与 AUX 的配对采样 | ptp: Provide support for auxiliary clocks for PTP_SYS_OFFSET_EXTENDED(V2 0/3,2025-07-01) | 让 PTP 扩展采样接口选择 AUX 作为 PHC 读取前后的系统时间基准,供用户态同步程序估计两者关系 |
| 后续 snapshot 与硬件 cross timestamp 扩展 | timekeeping/ptp: Expand snapshot functionality(V2 00/25,2026-05-29) | 将 AUX 纳入 snapshot 与硬件 cross timestamp 核心机制,并扩展底层计数值的捕获能力 |
五月的 V2 与六月的 V3 属于同一项 timekeeping 工作的前后修订,V3 只覆盖当时尚未合入的部分。vDSO 和 PTP 接入则分别推进。初始 AUX clock 功能进入 Linux 6.17;2026 年的 snapshot 系列继续扩展跨时钟关联能力。它们共同构成本文讨论的功能实现,而不是某一个提交独自提供全部能力。
功能带来的能力与边界
| 历史痛点 | AUX 提供的改变 | 没有自动解决的部分 |
|---|---|---|
| 两个参考争用核心校时状态 | 每个 AUX 拥有独立 timekeeper 和校准入口 | 同一个 AUX 仍不能被多个无协调 servo 争用 |
| 额外时间域依赖应用专有读钟接口 | 可用 clock_gettime()、clock_settime()、clock_adjtime() |
应用仍需选择正确 clock,并识别同步状态 |
| 高频读 PHC 的设备访问成本 | 在满足条件时通过 vDSO 读取 AUX 软件时钟 | PHC→AUX 同步残差和 CPU/NIC 时基漂移仍存在 |
| 额外域难以共享与统一管理 | 内核维护域内时间转换和并发读取一致性 | 域命名、服务依赖和重建策略仍由系统设计负责 |
| 希望按独立域设置 deadline | 为独立时间域提供基础表示 | 本文分析的内核尚未给 AUX 注册 sleep/timer 回调 |
因此,AUX clock 功能最核心的价值是:额外参考时间终于可以拥有不争用核心时钟的内核软件表示,并由标准接口读写、校准和共享。 它没有自动完成 PTP 协议、时钟之间的同步,也没有把所有原来只接受特定 clock ID 的子系统都扩展到 AUX。
内核原理:AUX 如何维护独立时间
AUX 是软件 timekeeper,不是新增硬件计数器
CONFIG_POSIX_AUX_CLOCKS 依赖 CONFIG_POSIX_TIMERS。UAPI 定义:
1 |
所以第 0 个 AUX 的数值 ID 为 16。CLOCK_AUX0 是 linuxptp 接受的用户态名称;内核头文件里的基准符号是 CLOCK_AUX。不要假设 libc 已定义同名的 CLOCK_AUX0 宏。
每个 AUX 使用独立的 tk_data / timekeeper 状态,包括锁、sequence counter、转换参数、偏移及相应的 NTP 校准状态。它们共享系统 clocksource,但不共享核心 timekeeper 的校时执行状态。因此,共享同一个振荡器,并不妨碍软件维护独立的时间尺度;也意味着它们仍共享硬件故障来源。
启用、读取、校准的状态链
源码中的主要路径如下:
1 | 写 /sys/kernel/time/aux_clocks/0/aux_clock_enable |
aux_clock_enable() 借用 raw timekeeper 对硬件速率的估计来建立初始转换,源码明确要求用户态继续通过 clock_adjtime() 校准偏差。启用并不等于对齐 PHC,也没有自动发现 PTP domain 的过程。
ktime_get_aux() 在 sequence counter 保护下读取基准和偏移,再把当前 cycle 增量换算成纳秒。简化表达为:
1 | AUX_i(now) = tkr_mono.base_i + offs_aux_i + converted_cycle_delta_i |
这里的 tkr_mono 是该 AUX timekeeper 的字段,不能把它理解成直接读取系统 CLOCK_MONOTONIC 再加固定 offset。AUX 可以有不同频率。核心 tick 的 tk_aux_advance() 会推进已启用的 AUX;clocksource 切换时,tk_aux_update_clocksource() 会先推进旧基准,再切换转换参数。
停用使 clock_valid 失效。重新启用会清理之前状态,不能当作保留校准状态的暂停/恢复,也不应在应用仍使用该域时由另一个服务擅自启停。
独立性不等于天然单调
AUX 的 setter 和 ADJ_SETOFFSET 可以改变相位。如果同步程序选择 step,应用看到的时间可以突变甚至后退。业务时间域要求的单调性必须由同步策略保证:启动时允许初始化,进入运行态后限制为有界 slew;无法恢复时切换到明确的故障或重新建域流程。
本文内核的 timekeeping_validate_timex() 对 AUX 还有专门限制:不允许 ADJ_TAI,不允许设置 UTC 插秒/删秒状态,也不提供 PPS discipline 状态支持。这与“任意时间起点、独立于 UTC 闰秒”的定位一致,但不能据此推断 AUX 自动同步到 TAI。
为什么 AUX 能提供较快的读取路径
PHC 访问通常需要经系统调用进入设备驱动;AUX 可将软件时间转换状态发布到 vDSO 数据页。vdso_time_update_aux() 更新该 AUX 的基准、mult/shift 和 clock mode;lib/vdso/gettimeofday.c 的 do_aux() 在用户态读取计数器,并通过 sequence counter 检查状态一致性。
因此,应用可以使用正常的 clock_gettime() 接口读取同步后的域,而不需要每次直接访问 NIC。是否实际走 vDSO,仍取决于架构、clocksource、内核与 libc;clock mode 不可用时会退回系统调用。clock_getres() 的纳秒分辨率也不代表纳秒级同步精度。
vDSO 改善的是读取成本,不是 PHC→AUX 同步误差。独立 AUX 越多,也越需要评估 tick 更新、缓存和同步 daemon 的额外成本。
当前能力矩阵:读钟不等于定时器
本文源码的 clock_aux 注册了四项回调:
1 | const struct k_clock clock_aux = { |
| 功能 | 本文内核状态 | 应用应如何理解 |
|---|---|---|
clock_gettime() |
支持已启用的 AUX | 仍需检查返回值和同步状态 |
clock_getres() |
有接口 | 不代表 clock 已锁定或精度达到该值 |
clock_settime() |
支持,受权限检查 | 可以破坏运行期连续性 |
clock_adjtime() |
支持,受 AUX 专用约束 | 同步 servo 的控制入口 |
| vDSO 读取 | 有实现 | 必须满足架构/clocksource/libc 条件 |
clock_nanosleep() |
无 .nsleep |
当前调用路径返回 EOPNOTSUPP |
POSIX timer_create() |
无 .timer_create |
当前创建路径拒绝,不能依赖 AUX 定时 |
timerfd_create() |
未纳入其支持的 clock ID 集合 | 不能因 POSIX 读钟可用就使用 AUX timerfd |
CONFIG_POSIX_TIMERS=y 只是基础设施依赖,不会替每种 clock 自动提供 timer。具体支持范围取决于内核配置、UAPI 和对应 callback。
多时间域的最佳实践
多时间域没有唯一的部署拓扑。下面给出两种例子:一种复用同一个硬件计数输入,另一种让 NTP 与 PTP 分别维护独立时钟。virtual PTP clock 是可选方案,不是 AUX 的前置依赖。
拓扑一:一个物理 PHC,多个 virtual PTP clock
PHC 是设备提供的硬件时钟,通常通过 /dev/ptpN 暴露。virtual PTP clock(vclock) 在稳定硬件计数输入上维护独立的时间基准和频率转换参数,从而让同一个物理计数输入承载多个可独立校准的 PTP 域。
图中两个 ptp4l 实例分别校准 vPHC A 与 vPHC B,两个 phc2sys 实例再分别校准 CLOCK_REALTIME 与 AUX0:
| 时间域 | 设备侧目标 | CPU 侧目标 |
|---|---|---|
| 全局时间域 | vPHC A,由 ptp4l A 校准 | CLOCK_REALTIME,由 phc2sys A 校准 |
| 独立业务域 | vPHC B,由 ptp4l B 校准 | AUX0,由 phc2sys B 校准 |
这适合只有一个物理 PHC、却需要跟踪多个独立 PTP 参考的部署。图中采用父 PHC free-running 的模型,设备编号仅为示例。
拓扑二:NTP 同步 REALTIME,PTP 同步独立业务域
chronyd 从 NTP 服务器取得 UTC 参考,继续维护主机的 CLOCK_REALTIME。另一条链路中,ptp4l 根据业务 PTP 参考校准一个物理 PHC,再由 phc2sys 将它同步到 AUX0。两条链路控制不同的目标,不需要创建 vclock。
这适合保留主机原有 NTP 校时、同时增加独立业务时间域的部署,无需 vclock。业务应用也可以直接读取 PHC,按需再同步到 AUX。
linuxptp 与 chrony 的支持现状
截至本文核对的 master,两个项目的支持边界如下:
| 项目 | 已有能力 | AUX 支持状态 |
|---|---|---|
| linuxptp master | ptp4l 维护 PTP/PHC,phc2sys 同步主机内不同 clock |
phc2sys 已支持 CLOCK_AUX0—CLOCK_AUX7,可把 PHC 时间同步到 AUX |
| chrony master | PHC 参考源可供 chronyd 校准系统时间 | master 尚未包含 AUX 目标选择;统一 PHC/AUX 管理仍在独立 MR 中开发 |
linuxptp 已提供 PHC→AUX 的直接路径;chrony 的 PHC 参考源能力主要解决设备参考→主机时间的同步。一个清楚的组合是让 chronyd 维护系统 UTC,让 phc2sys 维护独立 AUX,各自拥有明确的受控目标。
chrony 的多时钟同步草案(MR !33)正在探索统一多时钟控制,但尚不能算作 master 已有功能。相关设计还需处理网络时间戳所属的时钟域、不同 clock 的速率关系和任意 epoch 的语义。
References
- Linutronix:PTP Multi-Domain Time Sync with Linux
- Linux 内核源码
- linuxptp master 源码
- chrony master 源码
- Linux 文档:ktime accessors
- Linux 文档:PTP hardware clock infrastructure
- Linux 文档:Timestamping
- linuxptp 文档:ptp4l
- linuxptp master 文档:phc2sys
- chrony master 文档:chrony.conf
- timekeeping: Provide support for auxiliary timekeepers(V2 00/26)
- timekeeping: Provide support for auxiliary clocks - Remaining series(V3 00/11)
- vdso: Add support for auxiliary clocks(00/14)
- ptp: Provide support for auxiliary clocks for PTP_SYS_OFFSET_EXTENDED(V2 0/3)
- timekeeping/ptp: Expand snapshot functionality(V2 00/25)
- ptp: support virtual clocks and timestamping(v5)
- chrony MR !33:AUX 与 PHC 多时钟同步
- Miroslav Lichvar:chrony AUX 时钟支持的设计讨论