Repository navigation
[Feature] 时间子系统调研:设时一致性、clock 语义与计时连续性问题及统一时间维护建议 #11927
Description
RT-Thread Version: master a59e72fb5c
Affected area: Other / Not sure(横跨内核 clock、components/drivers/rtc 与 clock_time、components/libc、RT-Smart VDSO、PM)
速览
| 项目 | 要点 |
|---|---|
| 问题 | tick/clock_time/RTC/libc/VDSO 各维护时间基线,读取路径可能不一致,部分 API 名不符实 |
| 已有基础 | 调度 tick、clock_time source/event 抽象、RTC 框架、VDSO offset、PM tickless 补 tick |
| 关键不足 | 10 项,见 3.8(设时不统一、clockid 语义混乱、软 RTC 假同步、回绕/溢出、hrtimer 回退、PM 分叉、启动授时状态/发布缺口、NTP 版本边界) |
| 目标 | 统一 timekeeper + 单一设时入口 + clockid 语义契约 + 授时方向仲裁 |
| 路径 | 五步:接口一致(P0) → 最小 timekeeper(P1) → 授时与 PM 边界(P1,普通 MCU 终点) → 平台高精度(P2) → 高级能力(P2) |
| 裁剪 | 语义契约/错误码/兼容性不可裁;微秒 source/slew/PTP/VDSO/tickless 按需 |
| 边界 | 基线 a59e72f,静态分析,未编译未上板;核查日期 2026-10-24 |
详细导航:现状 §3 · 工作项 §1.1 · OS 对比 §4 · 目标 §5 · 实施 §6 · 验证 §7
1. 范围与结论
本文梳理 RT-Thread 调度时间、日历时间、RTC 和 NTP 的关系,参考其他 OS 提出理顺方向。
- 基线:
master提交a59e72fb5c;行号对应该基线。外部参考为在线 master/latest(核查日期 2026-10-24),未固定发行版。 - 验证:源码静态检查;无编译、上板、休眠、回绕或 VDSO 复现。
- 本文是调研与设计建议,不代表已实现全部 POSIX/Linux 时钟语义。
核心结论:并非缺少时间基础设施,而是各子系统间缺少统一的时间维护协议。先解决语义和一致性,再完善对时策略。
1.1 问题速览与工作项
下表将本文结论组织为 A–F 六项可跟踪的工作项。各问题的触发条件、影响与边界以第 3 节对应小节为准;表中范围与验收为摘要,完整内容见第 6 节对应实施步骤。A–F 不按文档顺序实现,满足依赖即可并行。P2 项按平台/产品需要启动;普通 MCU 走完 A–D 即达阶段性终点(6.1)。验收以第 6 节对应步骤为准;被裁剪项标注"不适用"。
| 工作项 | 优先级 | 范围(摘要) | 验收(摘要) | 依赖 | 证据与详情 |
|---|---|---|---|---|---|
| A. 统一设时与 clock 语义 | P0 | 所有设时入口接入同一更新协议并同步 VDSO;clockid 能力矩阵与明确错误行为,未实现类型明确拒绝 | 各读取路径一致;能力可查询 | 无,可最先启动 | 3.8.1–3.8.3;第 6 节第 1 步 |
| B. 最小 timekeeper 与软 RTC 修复 | P1 | 提取"日历基线 + 单调计数经过"的最小日历核心;修复软 RTC 自读自写与亚秒丢弃 | 读取与 RTC 设备解耦一致;走时连续、未授时可查 | A | 3.8.4、3.8.5、5.1;第 6 节第 2 步 |
| C. RTC 能力矩阵与兼容 | P1 | 秒/timeval/timespec/resolution 驱动能力矩阵,统一兼容回退或明确不支持 | 矩阵可查询,分辨率报告与实际能力一致 | 可与 A 合并设计 | 3.8.2;第 6 节第 1 步 |
| D. 授时状态与低功耗边界 | P1 | 统一启动授时与发布流程;对时统一入口;同步方向仲裁(可信等级决定 RTC 与系统谁跟谁);逐电源模式确认 source 停走、event 唤醒与补偿归属 | 各电源模式不漏计不重计;统一发布流程;无方向振荡 | A、B | 3.8.8–3.8.10、5.5、5.6;第 6 节第 3 步 |
| E. 平台高精度按需 | P2 | 选定平台接入自由运行 source 与独立 event;回绕扩展与 deadline 范围;回调契约 | 以真实事件链与延迟测量验收,实际后端可查询 | A;与 B–D 可并行,按平台需要启动 | 3.8.5–3.8.7、5.3 第一阶段;第 6 节第 4 步 |
| F. 对时增强与高级能力按需 | P2 | slew/校频与 RAW 区分;RTC 受控回写;统一事件仲裁与 tickless | 独立参考源实测达到产品自定目标;仲裁不破坏既有回调契约 | B、D | 3.8.10、5.3 第二阶段;第 6 节第 5 步 |
2. 先区分四种角色
| 角色 | 解决的问题 | 不应混用的场景 |
|---|---|---|
| 调度 tick | 时间片、内核超时、软定时器 | 不能直接当作 Unix 日历时间 |
| 单调时间与 boottime | 测量间隔、运行时计时,以及按约定是否计入 suspend | 不应被设置日历时间的 step 污染 |
| 系统日历时间 REALTIME | 当前日期、日志时间戳、网络协议中的时刻 | 可能跳变,不宜直接用于相对超时 |
| RTC 与 NTP | RTC 提供持久授时;NTP 提供外部时间校准 | 都不应各自绕过统一入口维护一份互不一致的系统时间 |
时区仅用于日历解释和显示。UTC、POSIX 时间戳与 TAI 是不同概念:UTC 存在闰秒,POSIX 秒数不逐一计入闰秒,不能将 UTC 定义为“没有闰秒的时间”。
3. 当前 RT-Thread 实现
3.1 调度 tick
src/clock.c 提供 rt_tick_get()、rt_tick_increase()、rt_tick_increase_tick()、rt_tick_get_delta() 和毫秒换算接口。
- BSP 时钟中断推进 tick,驱动时间片和
src/timer.c的定时器检查。 rt_tick_increase_tick()支持批量补偿 tick。- tick 的位宽、回绕和换算精度需要按配置考虑。
- 现有
rt_tick_set()不应作为日历校时接口;直接修改调度 tick 可能影响超时逻辑。
3.2 clock_time 与运行时间
主要代码位于 components/drivers/clock_time/:
| 文件 | 职责 |
|---|---|
clock_time_core.c |
可替换计数源、频率与分辨率等接口;默认源是 tick |
clock_boottime.c |
将源 counter 换算为运行时间 |
clock_hrtimer.c |
高精度定时与延时支持,实际精度取决于配置和源能力 |
clock_timer.c |
定时事件设备相关支持 |
不能仅因启用 RT_USING_CLOCK_TIME 就承诺高精度、长期严格单调或完整 suspend 语义:默认源仍为 tick;将窄计数器转换成 64 位不等于自动完成回绕扩展。
3.3 RTC 框架与软 RTC
RTC 框架位于 components/drivers/rtc/,主要包括 dev_rtc.c、dev_soft_rtc.c、dev_alarm.c,以及多个具体 RTC 驱动。
- 通用 libc 主要通过名为
rtc的设备访问日历时间。 - 设备可能是软 RTC、片内 RTC、MMIO RTC 或 I²C RTC,不能概括为每次读取都访问 I²C。
set_date()、set_time()涉及本地日历转换;时间戳接口与时区转换应分开理解。- 声明了某个 RTC 控制命令,不等于所有驱动都实现该命令。
软 RTC 已采用"日历基线 + 运行时间增量"外推结构,但周期同步实现不能称为纠漂:
rt_soft_rtc_sync()(359–372):自读整数秒,以零小数重设基线——同源自读自写,无法估计频率误差,丢弃亚秒还可能回退。rt_soft_rtc_set_source()(399–407):仅检查设备存在,未保存或读取指定源。RT_USING_SYSTEM_WORKQUEUE是编译条件,不自动启动同步任务。
3.4 libc 时钟接口及配置差异
主要实现:components/libc/compilers/common/ctime.c。
| 接口或配置 | 本地基线行为 |
|---|---|
time() 无 RT_USING_RTC |
通用弱实现返回 (time_t)-1,见 351–369 |
gettimeofday() |
通过 RTC 读取,具有 timeval 到秒的回退;无 RTC 时失败 |
| POSIX clock 实现 | 受 RT_USING_POSIX_CLOCK && RT_USING_CLOCK_TIME 控制,见 657–1014 |
| REALTIME / REALTIME_COARSE,有 RTC | 请求 RTC GET_TIMESPEC,没有与 gettimeofday 相同的秒级回退 |
| REALTIME / REALTIME_COARSE,无 RTC | 在所查实现中落入 boottime 分支;不代表获得可信日历时间 |
| MONOTONIC / RAW / COARSE / BOOTTIME | 当前均映射到同一 boottime,见 842–858 |
| PROCESS / THREAD CPU clock | 当前也返回 boottime,代码标注 TODO,不是真正 CPU 消耗时间 |
旧 RTC 框架控制分发 dev_rtc.c:55–90 不处理 TIMESPEC/TIMERES。因此可能出现同一设备上 time() 和 gettimeofday() 成功,而 clock_gettime(CLOCK_REALTIME) 失败的情况。
3.5 Smart VDSO 与设时一致性
components/lwp/arch/common/vdso_kernel.c:176–217 已维护 realtime offset;用户快路径可以直接根据共享时间数据和计数器外推 REALTIME,不必每次读取 RTC。
目前设时路径未统一:
| 入口 | RTC 写入 | 对应 VDSO 通知 |
|---|---|---|
settimeofday() |
有 | 有 |
clock_settime() |
有 | 有 |
stime() |
有 | 未见对应通知 |
set_timestamp() |
有 | 未见对应通知 |
证据:ctime.c:379–389、569–589、952–956;dev_rtc.c:238–250。
在 Smart/VDSO 快路径有效时,绕过通知的设时操作可能使 RTC 读取和用户态快路径读取不一致。这是静态发现,仍需运行复现。
3.6 低功耗与补偿
components/drivers/pm/pm.c:444–470 已有 tickless 补偿(低功耗计时器测量,恢复后 rt_tick_increase_tick() 补 tick),不能说没有低功耗补偿。
待明确:独立 source 低功耗期间是否停走;tick 补偿是否覆盖该 source;MONOTONIC/BOOTTIME 的 suspend 语义区分;持续运行 source 与额外 sleep offset 的重复计时。
Linux 参考:MONOTONIC 排除 suspend,BOOTTIME 包含 suspend;tickless idle ≠ 系统 suspend。
3.7 NTP 与 lwIP SNTP
主仓 RTC 框架不承担完整 NTP 客户端职责。所查在线 netutils/ntp/ntp.c 中:
- 使用服务器 transmit timestamp 获取时间,未见完整四时间戳 offset/delay 计算和多样本统计过滤。
ntp_sync_to_rtc()在较新分支直接执行 RTCSET_TIME,旧分支调用set_time()/set_date(),并非统一通过settimeofday()。- 所查实现为整数秒 step,未见 slew;直接写 RTC 也可能绕过 VDSO 通知。
上述结论仅针对所查在线版本,项目实际 netutils 版本仍需确认。
lwIP 有 SNTP 源码和可覆盖设时钩子,但所查 2.1.2/2.2.1 SConscript 定义了源码列表却未加入最终 src;默认 SNTP_SET_SYSTEM_TIME 可为空操作。不能断言"已编入"或"无人接线",需检查目标工程构建、宏覆盖与 sntp_init() 调用。
3.8 当前实现的不足:触发条件、影响与验收
以下分析基于本文代码基线,没有运行复现或上板验证。"静态确认"指源码路径或实现边界明确,不表示所有 BSP 都会触发。应区分接口缺陷、条件性风险与可选架构能力,不能将尚未采用完整 timekeeper 等同于系统不可用。各项验收标准统一由第 6 节对应实施步骤承载(对应关系见各步骤标题括号)。
3.8.1 系统设时入口不统一,RTC 与 VDSO 可能形成两份日历状态
- 触发与影响:
stime()直接写 RTC 秒(ctime.c:379–390),settimeofday()、clock_settime()成功后才更新 VDSO offset(ctime.c:569–592、952–957),所查 netutils 也直接写 RTC。当 Smart/VDSO 已启用且先经受支持入口建立有效 REALTIME 快路径,随后经未通知 VDSO 的入口改时,设备时间已变而用户快路径继续外推旧 offset,同一业务从time()、gettimeofday()、系统调用和 VDSO 读取时间可能观察到不一致。 - 边界:静态调用链确认的风险,未运行复现;未启用 VDSO 的系统不触发。直接设置某个 RTC 设备不一定应修改系统时间,关键是明确设备操作与系统设时的接口契约。
3.8.2 RTC 命令支持不一致,基础取时成功不代表 POSIX clock 可用
- 触发与影响:启用 POSIX clock 与 clock_time、且选用仍经旧框架分发命令的 RTC 驱动时——旧框架
dev_rtc.c:55–93只分发秒、timeval 和 alarm 命令,不处理 TIMESPEC/TIMERES——gettimeofday()有 timeval 到秒的回退,而clock_gettime(CLOCK_REALTIME)、clock_settime()、clock_getres()分别依赖 TIMESPEC/TIMERES、无等价通用兼容路径(ctime.c:807、847、952)。同一设备可能让time()、gettimeofday()成功,却让clock_gettime(CLOCK_REALTIME)或clock_getres()失败;应用仅验证 shell 日期或秒级取时,不能证明 POSIX 接口已正确接入。 - 边界:不能扩展为"所有硬 RTC 都不支持高精度接口";软 RTC、自定义 control 和不同 DM 驱动须分别检查;timespec 转换只兼容数据格式,不能凭空增加硬件精度。
3.8.3 时钟名称多于实际语义,应用难以选择正确时间域
- 触发与影响:
ctime.c:842–858将 MONOTONIC、RAW、COARSE、BOOTTIME 及 PROCESS/THREAD CPU clock 全部映射到同一 boottime(源码保留 TODO);未启用 RTC 时 REALTIME 分支还落入 boottime,启用了 RTC 但没有设备时则可能失败。CPU clock 无法据此统计线程或进程实际占用 CPU 的时间,BOOTTIME 与 MONOTONIC 没有独立的 suspend 语义,无 RTC 的某些配置把启动经过时间当作日历时间返回,应用可能误将小的 epoch 数值用于证书、日志和协议时间戳。 - 同类缺陷:
clock_nanosleep()相对模式(未置TIMER_ABSTIME)无论 clockid 恒返回-RT_EINVAL——ctime.c:869–907中err初始化为-RT_EINVAL,仅 ABSTIME 分支赋值取时结果,合法相对睡眠请求无法到达后面的相对nanosleep()分支。 - 边界:未校频时 RAW 与 MONOTONIC 数值相同、COARSE 与普通版本相同本身不违规;真正问题是缺少清晰能力声明,以及 CPU 时间、日历时间等不同语义被静默替代。
3.8.4 软 RTC 的“同步”没有消费外部源,还会丢弃亚秒
- 触发与影响:
rt_soft_rtc_set_source(name)仅检查设备存在,没有保存该源并读取它(dev_soft_rtc.c:399–407);周期 work 继续调用的 sync 函数从软 RTC 自身读取整数秒,再以零纳秒重设基线(dev_soft_rtc.c:359–373)。指定源的名字不等于真正接入了该源,同源自读自写无法识别晶振频率误差;重设时丢掉原有小数秒,存在回退与相位扰动风险,不能将周期执行成功作为“已纠漂”的证据。 - 边界:启用 workqueue 宏不等于周期任务自动启动;默认软 RTC 仍能按基线 + elapsed 提供时间,不能把同步逻辑问题泛化为整个软 RTC 不工作。
3.8.5 64 位接口不等于长期连续计时,hrtimer 还有独立半圈限制
- 问题一(source 回绕):默认 source 将
rt_tick_get()作为 counter,窄位宽 source 未扩展就回绕时,运行时间可能随之回绕(clock_boottime.c不替所有后端提供通用回绕扩展),属长期取时问题。 - 问题二(hrtimer 半圈):32 位平台
clock_hrtimer.c的队列计数使用unsigned long,经有符号差比较并限制单次 delay 小于半圈——即使 source 已正确扩展为 64 位,同源同频 1 MHz 下单次 delay 上限约 35.8 分钟,超限不能作为普通合法请求直接传入。 - 问题三(换算中间值溢出):
clock_boottime.c与clock_time_core.c按(cnt * res) / RT_CLOCK_TIME_RESMUL换算,乘法在 64 位无符号整数内完成,除法不能挽救此前的溢出;以 1 MHz(res=10⁹,RESMUL=10⁶)为例,counter 约 18446 秒(约 5.1 小时)时中间乘积首次溢出,运行时间骤降——远早于 64 位 source 自身回绕,"64 位回绕扩展 + 现有换算"不足以闭环长期连续计时。 - 边界:按实际 source 位宽、频率、扩展方式和平台字长判断,不能声称所有后端同一时长失效;计数分辨率不是回调最坏延迟保证;高层延时 API 参数范围(如 32 位平台纳秒参数)独立受限,各层需分别验证。
3.8.6 事件后端选择与静默回退,使“启用 hrtimer”不等于高精度生效
- 触发与影响:
clock_time_core.c:101–109自动选择首个符合条件的 source/event(clock_timer.c另有桥接 owner),clock_hrtimer.c:87–125的默认实现先尝试硬件 event、失败再静默转换为 tick 定时器shrtimer,且部分 BSP 会覆盖 weak 接口,不能只检查通用注册路径。仅打开RT_USING_CLOCK_TIME,或拥有高频 SOURCE-only 设备,都不能证明定时事件已脱离 tick 粒度;多设备注册时初始化顺序可能影响默认选择,指定硬件存在但 probe 失败也可能让系统仍能运行,却不满足精度要求。 - 边界:tick 回退是有价值的兼容机制,不是 bug;Rockchip 等 BSP 有专用硬件路径,STM32 已有
drv_tim.cEVENT 基础,不能概括为“都没有硬事件”。
3.8.6.1 BSP drv_tim.c 与时间架构的分工
BSP 的定时器驱动(如 STM32 bsp/stm32/libraries/HAL_Drivers/drivers/drv_tim.c)是硬件桥梁,不是系统时间本身:
- BSP 驱动负责:初始化 TIM 时钟/分频/计数模式,管理 CCR/ARR/中断,清除中断标志,调用框架 ISR 入口,暴露设备操作与能力;不直接维护日历时间,也不绕过统一入口修改系统时间。
- 时间架构负责:选择默认 source/event,将硬件计数解释为运行时间,维护 MONOTONIC/BOOTTIME/REALTIME,管理 hrtimer deadline 队列,决定授时、回写、VDSO 更新与低功耗补偿,保证同一事件资源不被多个 owner 同时重编程。
drv_tim.c 已通过 rt_clock_timer_register()/rt_clock_timer_isr() 提供 EVENT 后端基础,因此该类平台已有 EVENT 能力,不是“完全没有 hrtimer 后端”;但“已有 EVENT”不等于“已启用且默认生效”,也不等于“已有连续 SOURCE”。对采用该驱动的目标平台,EVENT 路径可复用/改造,SOURCE(自由运行计数 + 回绕扩展)需新增或独立评估,并需目标板配置与实测验证。该工作对应第 6 节第 4 步(工作项 E),属于平台按需能力,不是普通 MCU 的默认义务。
3.8.7 hrtimer 回调执行约束较强,慢回调会扩大计时路径延迟
- 触发与影响:到期处理在持锁状态下直接调用
timeout_func(clock_hrtimer.c:232–239持锁进入,174–204调用回调),硬件 event 通常从 ISR 进入这条路径;start/stop 重编程时也可能在调用线程内同步执行已到期队列项(clock_hrtimer.c:206–225、261–280),回调因此存在中断与线程双上下文。回调耗时不仅影响自身,还可能延后同队列其他 deadline 的处理,并扩大锁占用和中断受限窗口;回调内调用可能再次获取相同锁的定时器操作时,需专门检查重入契约,不能默认普通线程中安全的操作在这里同样安全。 - 边界:执行模型约束与条件性风险,未测量实际延迟,也不意味着回调都应改成线程执行;锁外派发同样需解决对象生命周期、并发 stop 和周期重挂语义。
3.8.8 PM 已补调度 tick,但独立 source 的睡眠连续性尚需单独闭环
- 触发与影响:
pm.c:444–470已通过低功耗计时器测量并调用rt_tick_increase_tick()补偿;若 boottime 仍以 tick 为 source 可间接受益,但换成独立硬件 source 后补 tick 不会自动修复它的 counter 或时间映射——独立 source 深睡停走而恢复后仅补 tick,则调度计时与该 source 派生时间不再对应;source 一直运行却无条件注入睡眠增量,则重复计时。普通 TIM 无法唤醒 STOP 时,精密 deadline 不能仅靠该 event 保证。 - 边界:source 停走取决于芯片和电源模式;tickless idle ≠ 系统 suspend;MONOTONIC 排除 suspend、BOOTTIME 包含 suspend,不能以“所有时钟都加睡眠时间”统一修复。
3.8.9 启动有时间数值,但缺少统一授时状态与发布流程
- 触发与影响:硬 RTC 的通用读取路径主要按需访问设备,软 RTC 默认从 2018 年基线外推;VDSO REALTIME 有独立 ready 标志,首次有效设时前回退系统调用,不会仅因 RTC 注册就建立 offset(
vdso_kernel.c:59–60、164–218)。应用拿到合法日期却无法判断来源(默认值/RTC 保持/人工/网络同步),读取成功与授时可信混同,业务可能过早校验日期或把未验证时间回写硬 RTC。 - 边界:VDSO 未 ready 时回退是正确保护,不是返回错误时间;硬 RTC 是否有效、是否保留掉电时间由驱动与硬件决定;统一授时状态是需补充的管理能力,不要求小系统实现复杂同步服务。
3.8.10 对时链路依赖包版本和工程接线,协议与校钟策略尚需分层
- 触发与影响:所查 netutils 在线版本以服务器 transmit timestamp 的整数秒直接 step 到 RTC,未见完整四时间戳估计、样本过滤或 slew;主仓 lwIP 的 SNTP 源码存在,但所查标准 SConscript 未加入其源列表,默认设时钩子也可能为空。网络往返延迟、服务器异常和亚秒截断可能直接进入设时结果;频繁 step 会使日历时间跳变,而晶振频率误差仍继续积累。仅有包配置或收到响应,不等于系统已经正确校时,更不代表 VDSO、RTC 回写与可信状态均已同步。
- 边界:netutils 行为仅适用所查在线版本,不代表工程锁定版本;简单 SNTP 在低要求设备上可足够,不必默认引入完整 NTP 守护进程;对时能力取决于实际构建、宏覆盖与调用链,而非文件是否存在。
修复顺序:先 P0 设时与语义(3.8.1–3.8.3)→ 再 P1 最小 timekeeper、基础 source 连续性、授时状态与 PM 边界 → 按需开展 P2 平台高精度及高级对时能力。具体问题与工作项映射见 1.1。
4. 其他 OS 的架构参考
| OS | 单调与日历时间 | RTC 与同步 | 借鉴点 |
|---|---|---|---|
| Linux | clocksource、timekeeping、clockevents/hrtimer 分层;REALTIME、MONOTONIC、RAW、BOOTTIME 语义分开 | RTC 用于启动授时、按策略回写、休眠边界;用户态同步服务配合内核调时接口 | 单一时间维护核心;step 与频率校正分开;明确 suspend 语义 |
| NuttX | 系统计数与日历基准关联,具备统一 clock 接口和可选 timekeeping/调时能力 | 启动 RTC 授时、恢复同步;所查 NTP 客户端使用 clock_settime step | 内核时间与 RTC 明确衔接;客户端只 step 不等于内核没有 slew 能力 |
| Zephyr | uptime 是内核计时基础;POSIX clock 提供日历时间接口;time utilities 支持时标转换与同步辅助 | RTC 驱动与 SNTP 查询分开;ENODATA 表示时间不可用,不能推出每次启动都清零 | 协议查询、时间映射和设备驱动解耦 |
| FreeRTOS | 内核主要维护 tick,日历时间由应用集成 | coreSNTP 提供协议能力,应用维护时间和 RTC | 小系统可用精简时间维护层,不必照搬 Linux 全部复杂度 |
| ThreadX | tx_time_get/tx_time_set 提供应用可读写的 tick 计数,不能直接当成严格不可设置的单调钟 | 日历时间由应用与 RTC 集成;NetX Duo 提供 SNTP | 明确 API 契约,不能仅根据 tick 名称推定严格单调 |
需要注意的语义:
- Linux MONOTONIC 不受日历 step 影响但可受 NTP 频率校正;RAW 区分未校频时间;CLOCK_TAI 非 1958 起累计秒,也不保证不受人工修改 REALTIME 影响。
- Linux RTC 回写有条件("11 分钟模式"非无条件保证);FreeRTOS 标准接口是
xTaskGetTickCount()返回TickType_t,位宽可配置。
5. 建议的目标架构
以下是演进建议,不是当前实现描述。目标架构分为两条路径:计时路径回答“现在是什么时间”,事件路径负责“何时中断、何时执行”。clock source 与 clock event 是逻辑角色,可以来自同一硬件,也可以由不同硬件提供。
**状态图例(相对本文代码基线):**绿色「已有」表示相关机制存在,不代表所有 BSP 都启用;橙色「需改」表示已有基础但语义或连接需要调整;红色「待增」表示目标中的统一机制尚缺,允许复用现有代码实现;灰色「待验」表示依赖具体 BSP/硬件,尚未验证。图中连线为目标关系,不能因两个节点已有就认定连接已实现。标注可组合使用,如「需改/待验」表示已有基础需调整、且依赖具体 BSP/硬件验证。
flowchart TB
subgraph TimePath["计时路径"]
Counter["待验 · 连续硬件 counter"] --> Source["已有 · clock source 抽象"]
Source --> TK["待增 · 统一 timekeeper"]
TK --> Mono["需改 · MONOTONIC 语义"]
TK --> Boot["需改 · BOOTTIME 睡眠语义"]
TK --> Real["需改 · REALTIME 后端统一"]
Mono --> API["需改 · 内核取时 / libc / VDSO 一致性"]
Boot --> API
Real --> API
Real --> TZ["已有 · 时区转换 / 日历显示"]
end
subgraph SyncPath["授时与持久化"]
RTC["已有 · RTC 框架 / 硬件驱动"]
Sync["需改 · 对时与人工设置接入"] --> Set["待增 · 统一校时入口及同步状态"]
end
RTC -->|启动授时| Set
Set --> TK
TK -->|可信时间按策略回写| RTC
subgraph EventPath["高精度事件路径"]
HR["需改 · 已有 hrtimer 队列 / 补时间域联动"] -->|编程最近事件| Event["已有 · clock event 抽象"]
Event -->|到期中断| ISR["已有 · 到期处理 / 回调"]
ISR -->|重挂下一事件| HR
end
TK -->|当前时间 / deadline 换算| HR
subgraph TickPath["初期独立调度路径"]
Periodic["已有 · 独立周期事件 / SysTick"] --> Tick["已有 · OS tick"]
Tick --> Sched["已有 · 调度 / 时间片"]
Tick --> Check["已有 · rt_timer_check"]
Check --> Hard["已有 · rt_timer 硬定时器 / 中断回调"]
Tick -.->|推进计时基准| SoftThread["已有 · 软件定时器线程 / 按 deadline 等待"]
SoftThread --> Soft["已有 · rt_timer 软定时器 / 线程回调"]
end
PM["需改 · PM 扩展到独立源与事件协调"] -.->|源停走 / 恢复| Source
PM -.->|睡眠时长补偿| TK
PM -.->|事件重挂| HR
PM -.->|调度时间补偿| Tick
PM -.->|低功耗唤醒配置| RTC
classDef existing fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
classDef modify fill:#fff3e0,stroke:#ef6c00,color:#7f3600
classDef missing fill:#ffebee,stroke:#c62828,color:#8e0000
classDef verify fill:#f5f5f5,stroke:#616161,color:#212121
class Source,TZ,RTC,Event,ISR,Periodic,Tick,Sched,Check,Hard,SoftThread,Soft existing
class Mono,Boot,Real,API,Sync,HR,PM modify
class TK,Set missing
class Counter verify
实线表示主要计时、授时和事件调用关系;虚线表示协调或计时依赖,不表示每个 tick 都唤醒软件定时器线程。RTC 唤醒能力取决于具体硬件。图中 timekeeper 向 hrtimer 提供时间基准,并不表示所有 hrtimer 都使用 REALTIME。软定时器路径需启用 RT_USING_TIMER_SOFT。
图中 hrtimer 与 OS tick 不要求存在上下级关系。初期保留独立 OS tick,hrtimer 不负责推进调度 tick;后续若共享事件硬件,再引入统一 deadline 仲裁。无论是否有到期事件,clock source 都应能保持计时连续性,不能依靠 hrtimer 回调推进系统日历时间。
5.1 职责划分
时间状态所有权
- 时间维护层拥有系统日历状态;RTC 是持久授时设备,不是每次读取的唯一载体。
- REALTIME 是应用可见的日历时钟语义而非某个 rtc 设备;软 RTC 的基线外推结构可提取为最小 timekeeper 的迁移基础,但不得与 timekeeper 双重维护状态。
- 所有设时入口(libc/shell/NTP/驱动)走同一更新协议并同步 VDSO;RTC 设备操作与系统设时需明确区分。
- 同步具有可信度状态:区分未授时、RTC 初始化、网络同步、失锁保持、人工设置;RTC 回写由状态与策略决定。
时钟语义
- 明确 MONOTONIC/RAW/BOOTTIME/CPU clocks 的支持范围;不支持的类型明确拒绝,不静默伪装。
- step、slew 与校频分开:首次授时和大偏差 step,小偏差可渐进修正;MONOTONIC 可调速但不因日历 step 跳变或倒退。
计时与事件
- source 提供连续计数,event 编程到期中断;修改事件截止点不应重置共享计数基准。
- hrtimer 管理 deadline,不拥有系统时间;相对超时不受日历 step 影响,REALTIME 绝对定时需在日历调整后重新判断。
- OS tick 属于调度计时;默认可用 tick 兜底,但独立 source 不应依赖 tick 中断逐次累计;日历校时不得修改调度 tick。
共享与仲裁
- 共享事件硬件必须单一仲裁;独立 SysTick 和 TIM 可先并存,统一 tickless 是后续工作。
- PM 显式管理睡眠边界:区分 source 停走、suspend 语义、event 唤醒能力。
rt_timer软/硬定时器(src/timer.c)保留原有 API、tick 单位和上下文语义,不强行替换为 hrtimer:硬定时器沿 tick 中断路径回调,软定时器由专用线程按最近 deadline 等待(非每 tick 扫描),hrtimer 提供更细粒度 deadline 服务。- 当前依赖方向:tick ISR 直接推进 tick 不经 hrtimer;hrtimer 事件不可用时回退 tick 定时器
shrtimer(clock_hrtimer.c:87–124);默认 source 读rt_tick_get(),方向是 tick→clock_time。 - 统一仲裁应合并最近截止时间而非统一所有定时器对象;软定时器线程的等待沿实际调用链去重,事件分发保持既有回调契约。
裁剪原则
- 不强制照搬 Linux:小系统先实现统一基线与更新协议,复杂频率驯服可选。
5.2 建议优先级
| 优先级 | 工作 | 原因 |
|---|---|---|
| P0 | 统一系统设时入口与 VDSO 更新 | 先保证不同读取路径看到同一时间 |
| P0 | 明确 clock API 的配置矩阵、回退和错误行为 | 避免 API 名称与实际语义不符 |
| P1 | 核查 source 回绕、安全换算、切换和低功耗行为 | 保证长期计时与恢复连续性 |
| P1 | 修订软 RTC source 同步及亚秒处理 | 自读重设不能作为纠漂,且有回退风险 |
| P1 | 最小 timekeeper:解耦 RTC 设备与运行期系统时间维护 | 减少硬件访问并统一状态所有权 |
| P2 | 复用/改造目标平台现有 EVENT 后端,评估接入自由运行 source,初期保留 OS tick 路径 | 平台按需能力,通用抽象与库驱动 EVENT 已有基础,连续 source 与 CCR 直接编程待目标板验证,不同时重构调度 |
| P2 | 完善对时采样、step/slew、校频及 RTC 回写策略 | 在基础语义和一致性可靠之后提高精度 |
| 后续 | 按需统一调度与 hrtimer 的事件仲裁,实现 tickless | 共享事件资源前需解决 deadline 合并和调度时间推进 |
5.3 两阶段事件架构
第一阶段:独立事件通道,先保证正确性。 例如保留既有周期 tick 中断(如 SysTick)产生 OS tick,另用一个独占的自由运行定时器提供连续 source 和比较 event,驱动 hrtimer。此时不存在 hrtimer → OS tick 的必经关系,系统日历时间统一由 timekeeper 外推。
flowchart LR
subgraph TIM["独占自由运行定时器(如 32 位 TIM)"]
CNT["待增/待验 · 计数读取 / 回绕扩展后端"]
CCR["需改/待验 · 复用/改造现有 EVENT 的比较编程 / ISR 后端"]
end
CNT --> Source["已有 · clock source 抽象"] --> TK["待增 · 统一 timekeeper"]
TK -->|当前时间 / deadline 换算| HR["需改 · hrtimer 时间域联动"]
HR -->|设置下一事件| Event["已有 · clock event 抽象"]
Event -->|编程| CCR
CCR -->|比较中断 / 到期处理| HR
SysTick["已有 · 周期 tick 中断(如 SysTick)"] --> Tick["已有 · OS tick"]
Tick --> Sched["已有 · 调度 / 时间片"]
Tick --> Hard["已有 · rt_timer 硬定时器 / 中断回调"]
Tick -.->|计时依赖| SoftThread["已有 · 软件定时器线程"]
SoftThread --> Soft["已有 · rt_timer 软定时器 / 线程回调"]
classDef existing fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
classDef modify fill:#fff3e0,stroke:#ef6c00,color:#7f3600
classDef missing fill:#ffebee,stroke:#c62828,color:#8e0000
classDef verify fill:#f5f5f5,stroke:#616161,color:#212121
class Source,Event,SysTick,Tick,Sched,Hard,SoftThread,Soft existing
class HR modify
class CNT,TK missing
class CCR verify
第二阶段:按需统一事件仲裁。 将调度器、tick 定时器和 hrtimer 的下一截止时间转换到可比较的基准,选择最近事件后编程硬件:
统一仲裁不等于必须把 tick 实现成一个普通 hrtimer 回调。调度时间的推进、时间片、到期处理顺序和中断上下文约束需要由内核明确规定,不能仅按“每次中断加一个 tick”处理。深睡期间还需由 PM 选择可用的低功耗事件设备,并在恢复后补偿和重挂。
5.4 现状与目标差距清单
| 模块或连接 | 当前基础 | 缺少或需要修改的部分 |
|---|---|---|
| 统一 timekeeper【待增】 | 软 RTC 有基线外推,Smart VDSO 有 realtime offset | 复用已有能力,统一状态所有权、计数换算、日历偏移和读写发布;不是从零新增所有计时算法 |
| 统一设时入口【待增】 | settimeofday/clock_settime 可通知 VDSO,其他入口不一致 | stime、set_timestamp、实际 NTP 等入口统一接入;保证 RTC、内核与 VDSO 一致,定义时间可信度状态 |
| libc 时钟语义【需改】 | 多种 clockid 已有接口,但部分共用同一 boottime,CPU clocks 为 TODO | 明确支持范围、RTC 精度回退与错误行为;区分 MONOTONIC/BOOTTIME,不能将 uptime 冒充 CPU 时间 |
| RTC 授时与回写【需改】 | RTC 框架和硬件驱动已有,libc 当前主要直接访问 rtc | 接入统一启动授时和受控回写策略;运行期 REALTIME 与具体 RTC 设备解耦 |
| 软 RTC【需改】 | 已有基线法和周期任务 | 修复指定 source 未实际使用及亚秒丢失问题;迁移后不能与 timekeeper 双重维护状态 |
| source/event 抽象【已有】 | clock_time_core 与相关设备接口 | 保留复用;具体 source 的回绕、变频、切换和睡眠行为需逐项验证 |
| 目标平台 TIM 后端【需改/待验】 | 部分平台库驱动已通过 rt_clock_timer_register/rt_clock_timer_isr 提供 EVENT;连续自由运行 SOURCE 尚缺 |
复用/改造现有 EVENT 后端,评估新增自由运行 SOURCE(64 位一致读取、回绕扩展、比较竞态处理与资源独占);需目标板配置验证 |
| hrtimer【需改】 | 队列、到期回调、事件编程和 tick 回退已有 | 补目标时间域转换与改时/校频后的事件重编程;处理 32 位半圈范围,不必重写基础队列 |
| OS tick【已有】 | SysTick/架构定时器推进调度 tick | 第一阶段保留;第二阶段才改造截止时间导出和统一事件分发 |
| rt_timer 软/硬定时器【已有】 | 硬定时器在 tick 中断路径回调,软定时器由专用线程处理 | 第一阶段保持 API、tick 单位和上下文语义;统一仲裁时接入硬定时器 deadline 与软定时器线程唤醒需求,避免重复登记线程超时 |
| PM【需改/待验】 | 已有低功耗计时器与 tick 补偿 | 扩展到独立 source、hrtimer 和低功耗事件协调;按 BSP 验证,不能称为完全无补偿 |
| NTP/PTP 接入与调时策略【需改/待增】 | 有包或可选组件,版本和实际调用链需确认 | 统一接入设时服务;按需求补采样、slew/校频与状态管理,不认定所有协议能力已集成 |
| 统一事件仲裁【待增,后续可选】 | 各子系统各自具有部分 deadline/事件机制 | 合并调度、tick 定时器和 hrtimer 的最近事件,定义处理顺序与调度时间推进;不是当前已有功能 |
本表是目标差距,不代表每个部署都需要实现所有可选能力。优先级见 5.2,代码依据见第 3 节,验证见第 7 节。
5.5 系统启动时 REALTIME 的来源
当前行为
当前通用 libc 主要在读取时访问 rtc 设备,不是统一的“启动读一次 RTC,装入独立 timekeeper”的 hctosys 模型。
| 启动配置 | 初始日历时间来源与限制 |
|---|---|
| 硬件 RTC 已注册且时间有效 | 读取硬件 RTC 当前时间;是否跨掉电保持取决于备用电源与驱动初始化行为 |
| 启用软 RTC | 默认日期基线加运行时间增量;未授时不代表日期可信 |
开启 RTC 支持但无 rtc 设备 |
通用 libc 日历时间读取失败 |
| 未开启 RTC 支持 | 通用 time()/gettimeofday() 失败;所查 POSIX clock 实现中 REALTIME 落入 boottime,不能视为有效日期 |
软 RTC 的 SOFT_RTC_TIME_DEFAULT 未覆盖时为 RTC_TIME_INIT(2018, 1, 1, 0, 0, 0),在初始化中以 timegm() 建立基线,见 components/drivers/rtc/dev_soft_rtc.c:22–35、294–348。这只是初始占位日期,不是自动获取的网络时间。
硬件 RTC 设备存在也不等于时间有效。需要检查电池失效、振荡器停止、硬件有效标志以及驱动是否提供错误反馈;具体驱动可能具有各自的默认日期或初始化策略,不能一概假定保留时间。另外,当前 clock_gettime(REALTIME) 需要相应 RTC TIMESPEC 命令支持,不能仅依据 time() 可用就认定它可用。
Smart VDSO 启动时 realtime offset 尚未就绪:vdso_kernel.c:59–60、164–218 通过有效标志控制快路径,未就绪时用户态回退 syscall。当前 settimeofday()/clock_settime() 可建立 offset,但启动时不会仅因 RTC 已注册就自动完成该步骤。因此,RTC 已正确走时并不代表 VDSO REALTIME 快路径已经有效;周期发布 monotonic 数据也不等于完成日历授时。
目标启动流程【待增:统一启动授时与发布】
flowchart TD
Start["初始化 clock source 与统一 timekeeper"] --> Check{"选定硬件 RTC 可读<br/>且时间有效?"}
Check -->|是| RTC["采样 RTC 与当前 counter<br/>建立 REALTIME 基线 / 记录 RTC 来源"]
Check -->|否| Saved{"有可用的持久时间记录?"}
Saved -->|有| Restore["恢复最后已知时间下界或估计值<br/>标记未同步"]
Saved -->|无| Default["采用明确的初始基准<br/>标记未授时"]
RTC --> Publish["统一发布内核 / libc / VDSO 时间状态"]
Restore --> Publish
Default --> Publish
Publish --> Wait["timekeeper 持续计时"]
Wait --> Sync["可信 NTP / PTP / 人工授时可用后<br/>经统一入口校准并更新状态"]
Sync --> Publish
classDef missing fill:#ffebee,stroke:#c62828,color:#8e0000
class Start,Check,RTC,Saved,Restore,Default,Publish,Wait,Sync missing
本图红色表示统一流程待建立,并非 RTC 驱动、VDSO 或协议组件全部缺失。应在所选 RTC 可用且时间发布基础设施就绪后完成一次协调初始化;异步或较晚注册的 RTC 需要明确后续授时策略,不应由每个 RTC 驱动无条件覆盖系统时间。
启动授时还应满足以下约束:
- RTC 来源 ≠ 可信网络同步:是否足够可信由产品策略决定。
- 持久记录 ≠ 准确当前时间:不知掉电时长,只能作最后已知时间/下界;持久化可选,需考虑写入寿命。
- 无有效源仍可持续计时,但必须可识别未授时:明确 API 初始值/失败语义;统一初始化所有读取路径;后续校准不重置硬件 counter 或调度 tick。
5.6 授时源与系统时间的同步方向仲裁
有外部授时时周期将系统时间回写 RTC、无外部授时时 RTC 锚定系统时间——这不是两套机制,也不是 RTC 的特例,而是一个通用仲裁框架的两种结果:同步永远是“高可信源 → 低可信方”的单向流动,方向由各源可信等级决定,由 timekeeper 统一仲裁。RTC 只是授时源的一种实例。
5.6.1 授时源集合与可信等级
系统可接入的授时源不限于 RTC,每个源用同一组属性描述,由产品策略赋予可信等级:
| 授时源实例 | 持久性 | 接入实现的典型粒度 | 典型可用性 |
|---|---|---|---|
| 硬件 RTC(片上/外置) | 跨断电持续走时(依赖备用供电与有效标志) | 秒级(部分有亚秒寄存器) | 本地常驻 |
| 静态持久记录(flash/文件时间戳) | 仅存最后已知点,不持续走时 | 取决于实现 | 启动时一次 |
| NTP/SNTP | 不持久 | 协议时间戳含秒的小数部分;实际粒度取决于接入实现(本项目所查 netutils 客户端只取整数秒) | 依赖网络 |
| PTP / GPS(1PPS 等) | 不持久 | 捕获能力亚微秒级~消息粒度,端到端精度取决于硬件与链路条件 | 依赖硬件与链路 |
| 人工 / 主机串口授时 | 不持久 | 不限于整数秒,取决于输入与接线方式 | 事件性 |
粒度列描述的是具体接入实现的能力,不能由协议名直接推断端到端精度。
方向规则:当前最高可信源授时系统;系统可信级高于某持久源时,才向该持久源受控回写。同一时刻只有一个权威源,不存在多源同时写系统时间。
可信等级(示例):未授时 < 静态持久记录(下界)< RTC(持续走时)< 人工/NTP < PTP/GPS
方向规则:
系统可信级 < 源 S 可信级 → S ──► 系统(锚定/授时,禁止向 S 回写)
系统可信级 > 持久源 P 可信级 → 系统 ──► P(受控低频回写)
等级仅针对已通过有效性、来源及质量校验的样本;协议类型本身不构成可信度保证——未认证的公网 NTP 不一定优于维护良好的 RTC,失锁的 GPS 不一定优于有效 NTP。可信等级表是产品策略,不是框架硬编码(见 5.6.4)。
注意区分两类“持久源”:持续走时源(RTC,断电期间仍走时)可参与周期锚定与校频;静态持久记录(flash/文件时间戳)只保存最后已知点,仅用于启动恢复下界,不得用于周期锚定或校频——用同一静态记录重复锚定会使系统时间回退,用其差值校频没有物理意义。
5.6.2 状态机
stateDiagram-v2
[*] --> 持久源锚定: 启动时有有效持续走时源(5.5,条件式)
[*] --> 未授时: 无持久源或读数无效
未授时 --> 持久源锚定: 持续走时源读数有效
未授时 --> 外部授时: 外部源校验通过(无持久源时直达)
持久源锚定 --> 外部授时: 更高可信源校验通过(方向反转:源→系统→持久源)
持久源锚定 --> 未授时: 锚定源确认失效(振荡器停止/掉电失效;瞬时读错误经产品策略的确认阈值判定,不直接降级)
外部授时 --> 失锁保持: 授时源失效(按已校或名义频率走时)
失锁保持 --> 外部授时: 授时恢复
失锁保持 --> 持久源锚定: 长期失效且持久源更可信(滞回后回退)
外部授时 --> 外部授时: 更高可信或同级替代源校验通过(平滑交接)
下表为完整能力示例(各行为可按 5.6.4 的裁剪自由度简化):
| 状态 | 权威源 | 持续走时源 → 系统 | 系统 → 持久源回写 |
|---|---|---|---|
| 持久源锚定(典型:仅硬 RTC) | 持续走时源 | 周期锚定、长窗校频(均按 5.6.4 裁剪自由度取舍)或仅启动对齐(见 5.6.3) | 禁止——系统没有比持久源更准的依据 |
| 外部授时 | 外部源 | 停止,持久源不再仲裁系统 | 启用时低频周期回写(类比 Linux 11 分钟模式,仅在已同步状态执行) |
| 失锁保持(holdover) | 系统(已校或名义频率) | 停止 | 按产品策略:可继续或暂停 |
5.6.3 慢速/低分辨率源的锚定方法
持久源(RTC 通常秒级)分辨率低于系统 counter(亚秒级)时,正确分工不是互比两个完整时间戳,而是各管一段——该方法适用于任何比系统 counter 慢的锚定源:
系统时间 = 源读数(锚点,精确到其分辨率)+ counter 外推的亚分辨率偏移
T_sys(t) = T_src_k + (c(t) − c_k) / freq
- 边界锚定:源读数在其分辨率内的亚部分本来未知,理想锚点打在读数翻转处。但周期事件不天然等于时间边界:RTC wakeup 可从使能时刻起计、相位不保证对齐日历秒;GPS 1PPS 需有效锁定且需时间标签指明脉冲对应的整秒,单独脉冲不能确定日期与秒号。只有已验证与时间标签边界对应的事件才可直接打点(误差含中断/唤醒/调度/读取延迟);否则采用有界采样窗口(如轮询翻转),并显式报告打点不确定度。
- 比较只在源边界进行:禁止直接
系统时间 − 源读数判漂移——源的量化噪声(秒级源 ±0.5~1 秒)会被误当成系统漂移,引入虚假 step。诊断时将系统读数截断到源分辨率再比较,仅用于越界粗检。 - 量化误差不积累,频率误差积累:长窗校频把量化误差摊薄——端点量化误差固定,相对频率测量不确定度 ≈ 端点量化与打点误差之和/测量窗(秒级源、天级窗约 ±12 ppm 量级)。这是测量分辨能力,不是系统相对 UTC 的误差保证,也不消除参考源自身频偏。校准值可低频持久化,重启后加载前需确认 source 频率配置与硬件未变更(温度漂移需重新收敛)。
- 锚点更新的相位修正:残余大小不预设——打点条件良好时可小至 µs~ms 级,非对齐事件或低频轮询可能达明显秒内比例。按测得残余、误差界与产品阈值决定:小残余并入换算系数做有界、渐进的相位修正(重建连续锚点,维持非负走时速率,不重新解释历史 counter,不破坏 deadline 契约);超出阈值走 step/告警路径。
- 精度误差预算:系统日历精度上限 = 权威源自身精度 + 量化与打点/传输延迟 + 外推期频率误差,无法突破源本身;源的亚分辨率能力(如 RTC 的 SSR 类寄存器)是加分项,非通用假设。
5.6.4 仲裁规则
任何配置都必须满足的不可裁约束:同步是高可信源→低可信方的单向流动;方向判定只有唯一仲裁点;启动优先由有效持续走时源→系统重建基线,仅有静态持久记录时恢复最后已知时间下界并保留未授时标记,无源则明确未授时、外部源可直接接管。
- 唯一仲裁点:方向判定只在统一设时入口/timekeeper;驱动、协议客户端、shell 不得自行决定写持久源或锚定系统。
- 可信等级由产品定义:排序与升降级条件是策略;切换滞回——源短暂抖动不触发方向反复切换,失锁先进入 holdover 观察期。
- 回写前置条件:系统已获可信授时才回写(类比 Linux
ntp_synced());回写前比较差异,阈值内可跳过;flash 类记录考虑写入寿命。 - 回写粒度匹配持久源语义:任意时刻截断写秒级 RTC 引入近 1 秒相位偏差,须借秒边界或按设备生效规则处理。
- 掉电闭环:有持续走时源时启动优先由其→系统重建基线;无源进入未授时,外部源可直接接管。
能力可裁部分:holdover 可裁,滞回可简化为固定阈值,周期锚定与校频可独立于状态机裁剪,回写可整项裁掉(仅损失跨断电的最近一次授时结果),多源分级可退化为持久源/授时源两档;能力取舍与 6.2 的系统整体配置档相互独立、可组合。
本节是工作项 D(授时状态与边界)与第 6 节第 3 步的架构依据;方向仲裁本身不要求新增硬件,但周期锚定需使用已验证与时间标签边界对应的事件,或采用有界采样窗口,并计入打点不确定度。
6. MCU 适配与分阶段实施
本章是普通 MCU 上的分步实施路径与裁剪边界。本章是实施建议,不代表已实现;每步验收均受第 3 节的证据边界约束,量化目标由产品定义并以实测确认,本文不提供未验证的精度数值。
6.1 目标定位:正确性契约与分档能力,而非 Linux 移植
- 目标不是移植 Linux timekeeping。 落地两条主线:①正确性契约——所有读取路径一致、设时入口统一、不支持的语义明确拒绝;②分档能力——最小核心满足小系统,高精度/对时/校频按需叠加。
- 普通 MCU 走完第 1–3 步即阶段性终点。 第 4、5 步是平台增强与高级能力,不做不破坏前三步契约。
- 无硬 RTC 系统:运行期日历 = 基线 + 单调计数经过;断电期间无法准确走时;持久记录只能作下界(5.5)。这是固有能力边界,应如实暴露授时状态。
6.2 最小可裁剪核心与可选能力
按“最小核心 + 可选叠加”组织能力,每个配置档只要求其声明范围内的行为正确:
| 层级 | 内容 | 裁剪属性 |
|---|---|---|
| 最小核心 | 调度 tick;至少一条语义如实的日历时间读取路径(可用,或明确不可用);统一设时入口(至少覆盖人工设时) | 任何目标配置都应满足 |
| 基础可选 | 软/硬 RTC 接入统一基线;授时状态(来源、有效性)可查询;RTC 驱动能力矩阵与兼容回退 | 无 RTC 设备不等于裁掉日历服务:仍可经人工/网络授时启用软件日历;完全不提供日历服务时才可裁,但读取需如实返回不可用 |
| 进阶可选 | VDSO 快路径一致性;MONOTONIC/BOOTTIME 的 suspend 语义区分;NTP/SNTP 授时;hrtimer 独立事件后端 | 按产品需要启用,启用才参与验收 |
| 高级可选 | slew/校频与 RAW 区分;RTC 受控回写;统一事件仲裁与 tickless | 有明确指标要求时才投入 |
MCU 典型配置档(描述行为契约,不含未验证的精度指标):
| 配置档 | 组件组合 | 可达效果 | 明确不具备 |
|---|---|---|---|
| 裸最小 | 最小核心;无 RTC | 运行期时间可读、可人工设置;未授时状态如实暴露 | 跨断电走时、POSIX 完整矩阵、高精度 |
| 基础日历 | + 软/硬 RTC 接入统一基线与状态查询 | time/gettimeofday/clock_gettime(REALTIME) 一致;授时后运行期日历可信 | suspend 语义区分、网络对时、高精度 |
| 标准低功耗 | + 启动授时/持久下界 + PM 边界闭环 | 第 1–3 步阶段性终点;睡眠前后各时钟符合声明语义 | 高精度事件、校频、tickless |
| 高精度 | + 平台 source/event 后端 | 亚 tick 粒度延时与定时,事件实际生效可查询 | 校频与统一仲裁(属高级档) |
| 高级 | + 对时增强/slew 校频/统一事件仲裁 | 长期精度与功耗的精细优化 | —(全部按需实现) |
6.3 建议分五步实施
每步给出范围、适用场景、完成验收与可裁项。验收均为行为验收;量化指标由产品定义并以实测确认。
第 1 步:现有接口一致与兼容(对应 3.8.1–3.8.3,建议工作项 A/C)
- 范围:
- stime、set_timestamp、实际 NTP 等入口统一接入同一更新协议并同步 VDSO(无 VDSO 时保持单一协议)。
- 建立 RTC 驱动秒/timeval/timespec/resolution 能力矩阵,统一兼容回退或明确不支持。
- 明确各 clockid 支持矩阵、初始状态与错误行为;未实现的 CPU clock 等明确拒绝。
- 适用场景:所有启用 RTC 或 POSIX clock 的配置;不依赖硬件改动,可最先开展。
- 完成验收:
- 逐一调用各设时入口后,各读取路径在采样延迟与设备分辨率允许范围内一致。
- 能力矩阵可查询,分辨率报告与实际能力一致;日历 step 不影响相对超时与单调时序。
- 可裁项:无 VDSO 可裁 VDSO 通知但保留单一协议;无 POSIX clock 可只做入口统一与错误行为文档化。
第 2 步:最小软件日历(最小 timekeeper,对应 3.8.4、5.1,建议工作项 B)
- 范围:
- 以软 RTC"日历基线 + 运行时间增量"结构为基础提取最小 timekeeper:日历基线 + counter 换算 + 统一读写发布。
- REALTIME 运行期统一经 timekeeper,不再逐次访问 RTC 设备;无硬 RTC 系统由此承担运行期走时(边界见 6.1)。
- 修复软 RTC 自读自写与丢弃亚秒;所选基础 source(含默认 tick source)的回绕连续性与溢出安全换算在本步闭环(见 3.8.5),不随高精度步骤裁剪。
- 适用场景:无硬 RTC 但需要运行期日历的系统;有硬 RTC 且希望解耦设备访问、统一状态所有权的系统。
- 完成验收:初始化采样一致性与运行期误差分开验收。
- 有硬 RTC:启动采样建基线后运行期读取不触发设备访问,采样时刻与直接读设备在采样误差与分辨率允许范围内一致;运行期差异按实际频偏与产品误差预算评估(两源晶振不同会自然漂移)。
- 无硬 RTC:授时后走时连续、未授时状态可查询;跨 source 回绕与换算溢出临界点读数连续;日历 step 不污染单调计数;软 RTC 保留时不与 timekeeper 双重维护状态。
- 可裁项:极小系统可暂不启用独立 timekeeper(仍受第 1 步契约约束);授时状态可先只分"未授时/已授时"两态。
第 3 步:授时与低功耗边界(对应 3.8.8–3.8.10 基础部分、5.5、5.6,建议工作项 D)——普通 MCU 阶段性终点
- 范围:
- 按 5.5 实现启动授时(RTC 有效则授时、持久记录作最后已知时间下界、无源标记未授时)并统一发布到各读取路径。
- 按 5.6 实现同步方向仲裁(三条红线不可裁,锚定/回写等能力按 5.6.4 的裁剪自由度取舍);NTP/SNTP/人工授时统一入口,不旁路直写 RTC。
- 逐电源模式确认独立 source 是否停走、event 能否唤醒、补偿由谁执行,tick 补偿与 sleep offset 不重复计。
- 适用场景:有低功耗、网络授时需求,或需要明确"时间不可信"语义的产品。
- 完成验收:
- 启动、晚注册 RTC、首次网络授时均经同一发布流程,读取结果与授时状态一致。
- 各电源模式同时测量 tick、MONOTONIC、BOOTTIME 与 REALTIME,符合声明 suspend 语义,不漏计不重计;对时异常有可观测状态。
- 可裁项:无网络产品可裁 NTP,仅保留人工与 RTC 授时;不使用深睡可声明不支持的电源模式;授时状态可先只在内核内一致。
完成第 1–3 步即普通 MCU 的阶段性终点:正确性契约成立,运行期日历可信(授时后),授时与低功耗边界明确。
第 4 步:高精度按需(平台相关,对应 3.8.5 新增 source 部分、3.8.6–3.8.7,建议工作项 E)
- 范围:
- 为选定平台接入自由运行 source 与独立 event 后端;新增平台 source 的回绕扩展与单次 deadline 范围处理(默认/既有 source 已在第 2 步闭环)。
- 实际生效的 source/event/owner/回退状态可查询;回调上下文、可调用 API 与重入契约明确(已过期 deadline 可能在 start 调用线程上下文同步执行回调,见 3.8.7)。
- 适用场景:需要亚 tick 精度延时与定时的产品;按平台逐个实现,不要求全平台覆盖。
- 完成验收:以真实事件链和延迟测量验收,而非只看配置宏或 API 名称;跨回绕读数连续;超范围请求可分段或可诊断拒绝;回调约束有测试覆盖并测最坏延迟。
- 可裁项:tick 回退路径保留为兼容机制;无高精度需求可整步跳过,不影响前三步契约。
第 5 步:高级能力按需(对应 5.2 P2 与后续项,建议工作项 F)
- 范围:slew 与校频(区分 MONOTONIC 可调速与 RAW 不调速);RTC 受控回写策略(由授时状态与产品策略决定);统一事件仲裁与 tickless(5.3 第二阶段)。
- 适用场景:对长期精度、功耗有明确指标,且愿意投入独立参考源测量验证的产品。
- 完成验收:
- 以独立可信时间源测量长期误差并达到产品自定目标;校频不修改 RAW。
- 相对超时不受日历 step 污染;校频按声明时间域换算并按需重编程事件,不倒退、不重复触发、不意外取消;统一仲裁后既有回调契约不被破坏。
- 可裁项:全部可裁;无此需求停在第 3 或第 4 步即可。
同一底层改动可能同时服务多步(如统一设时入口贯穿第 1、3 步),实现时可合并设计、分开验收;步骤划分不改变第 3 节各问题的证据与边界。
7. 后续验证清单
以下清单与第 6 节步骤对应:第 1 条是第 2/4 步前置确认;第 2–6 条对应第 1/2 步;第 7–8 条对应第 2/3 步;第 9 条对应第 5 步。步骤被裁剪时对应清单项随之裁剪。
- 确认目标 BSP、
.config、clock source、计数器位宽及实际频率。 - 覆盖有无 RTC、有无 CLOCK_TIME、旧 RTC 与 DM RTC、Smart/VDSO 等配置。
- 交叉验证 time、gettimeofday、clock_gettime 和 VDSO 的读数与错误行为。
- 分别调用 settimeofday、clock_settime、stime、set_timestamp 和实际 NTP 入口,检查各路径一致性。
- 测试正向/反向 step 不影响相对超时和单调时序。
- 测试 tick 回绕、counter 回绕、源切换前后连续性。
- 测试 tickless idle 与不同 suspend 模式,分别覆盖计数源停走和继续运行的情况。
- 固定 netutils/lwIP 版本,核对实际构建、设时钩子和启动调用链。
- 以独立可信时间源测量长期频率误差,不用自读重设证明“纠漂有效”。
8. 外部参考
外部在线源码与文档的核查日期为 2026-10-24;链接指向 main/master/latest 的内容可能随后变化,引用本文结论前建议复核。
- Linux 时钟源、事件与时间维护:timekeeping
- Linux 内核取时接口:core-api/timekeeping
- Linux 时钟语义:clock_gettime(2)
- Linux 调时接口:adjtimex(2)
- Linux RTC:RTC documentation
- Zephyr 内核计时:Kernel Timing
- Zephyr 时间工具:Time Utilities
- Zephyr RTC:RTC
- NuttX 时钟初始化:clock_initialize.c
- NuttX 调时实现:clock_adjtime.c
- NuttX NTP 客户端:ntpclient.c
- FreeRTOS 标准 API:task.h
- FreeRTOS SNTP:coreSNTP
- ThreadX 时间服务:ThreadX chapter 3
- RT-Thread netutils:ntp.c
Activity
github-actions commentedon Oct 24, 2026
如需认领并处理此 Issue,请评论
/claim。To claim and work on this issue, comment
/claim.[-]时间子系统调研:设时一致性、clock 语义与计时连续性问题及统一时间维护建议[/-][+][Feature] 时间子系统调研:设时一致性、clock 语义与计时连续性问题及统一时间维护建议[/+]