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 引入前,两个参考争用同一个目标;引入后,核心 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
2
3
#define CLOCK_AUX       MAX_CLOCKS  /* 本文源码中 MAX_CLOCKS 为 16 */
#define MAX_AUX_CLOCKS 8
#define CLOCK_AUX_LAST (CLOCK_AUX + MAX_AUX_CLOCKS - 1)

所以第 0 个 AUX 的数值 ID 为 16。CLOCK_AUX0 是 linuxptp 接受的用户态名称;内核头文件里的基准符号是 CLOCK_AUX。不要假设 libc 已定义同名的 CLOCK_AUX0 宏。

每个 AUX 使用独立的 tk_data / timekeeper 状态,包括锁、sequence counter、转换参数、偏移及相应的 NTP 校准状态。它们共享系统 clocksource,但不共享核心 timekeeper 的校时执行状态。因此,共享同一个振荡器,并不妨碍软件维护独立的时间尺度;也意味着它们仍共享硬件故障来源。

启用、读取、校准的状态链

源码中的主要路径如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
写 /sys/kernel/time/aux_clocks/0/aux_clock_enable
→ aux_clock_enable_store():检查 CAP_SYS_TIME,串行化启停
→ aux_clock_enable():清理旧状态,基于当前系统 clocksource 初始化
→ clock_valid = true,发布 timekeeper 与 vDSO 数据

clock_gettime(CLOCK_AUX + 0)
→ 可用时走 libc/vDSO
→ 否则走系统调用 → clock_aux.clock_get_timespec
→ aux_get_timespec() → ktime_get_aux_ts64() → ktime_get_aux()

clock_settime(CLOCK_AUX + 0, ...)
→ aux_clock_set():计算 offs_aux,更新并发布状态

clock_adjtime(CLOCK_AUX + 0, ...)
→ aux_clock_adj() → __do_adjtimex(该 AUX 的 tk_data, ...)

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 能提供较快的读取路径

AUX 读钟与校准路径:vDSO 快速读取、内核回退与独立校准

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
2
3
4
5
6
const struct k_clock clock_aux = {
.clock_getres = aux_get_res,
.clock_get_timespec = aux_get_timespec,
.clock_set = aux_clock_set,
.clock_adj = aux_clock_adj,
};
功能 本文内核状态 应用应如何理解
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

Linux PTP 分层架构:两个虚拟 PHC 经统一接口服务于独立时间域

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 同步独立业务域

NTP 与 PTP 分层架构:chrony 维护 REALTIME,物理 PHC 经 phc2sys 同步到 AUX

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