真机实测(0.8.47,2026-11-06)定位到的根因不是「没有 NTP 客户端」: 固件自带 ntp-4.2.8p14(/usr/sbin/ntpd)和 /etc/ntp.conf(4 个 pool.ntp.org), 但没有任何 init 脚本/进程启动它 —— 有客户端、没启动。 实测证据: - RTC 存在且可读(/dev/rtc0,10003000.rtc),内核开机 hctosys=1, 冷启动不回到 1970 → 掉电保持;RTC 存 UTC(since_epoch == date +%s)。 - 但 RTC 会走慢且无人纠正:实测系统时间比 NTP 慢 49 s;把系统时间人为 拨错 4 h 后观察 65 s 无人纠正,RTC 也不跟随系统时间。 - UDP/123 出网正常(ntpd -q 一次成功 time set +48.877s);DNS/HTTP 正常。 - udhcpc 的 default.script 不处理 option 42,实测热点也不下发。 - ntpd -q 不会回写 RTC(步进后 sys-rtc 仍差 49 s)→ 必须 hwclock -w -u。 - ntpd 没有超时参数,无服务器时永久存活 → 必须自带看门狗。 - -p 形式的 server 首个 poll 要 ~64 s;配置里加 iburst 后 8 s 内返回。 实现(新增,不改任何基线文件): - /usr/bin/meow_ntpsync:一次性校时器。复用 ntpd -q -g + 运行时生成的 iburst 配置;内置 IP 兜底(阿里/腾讯/Cloudflare)→ DNS 名兜底; 单次 15 s 看门狗、默认路由轮询、1 h 短路、进程锁、失败静默。 成功严格校时后 hwclock -w -u 回写 RTC(RTC 存 UTC,必须 -u)。 - /etc/init.d/S45meow_ntpsync:开机只 setsid 后台拉起并立即返回 (关键路径 +0 秒;后台最长约 150 s 自行退出),失败不阻断 init。 - /usr/share/udhcpc/default.script.d/50meow_ntpsync:只在 bound/renew 触发(设备上唯一可靠的「网络就绪」事件),后台拉起后立即 exit 0; 有 $ntpsrv(option 42)就优先用。基线 default.script 本就有 HOOK_DIR 循环,目录原本为空。 - tests/run_tests.sh:宿主沙箱单测(假 ntpd/hwclock/网络探测)。 验证:dash 与 busybox sh 各 sh -n 通过;单测两套 shell 各 23/23 通过; 真机(/tmp 下运行,未装系统目录)首跑 8 s rc=0、ntpd slew 成功、 RTC 由 1793940395 写成 1793940452(49 s 偏差被纠正)、hwclock -u -r 吻合; 二次运行 0 s 短路;-b -t 3 无网络 5 s 退出 rc=1; DHCP 灌入不可达服务器 192.0.2.x 仍靠 IP 兜底在 ~10 s 内成功。
34 lines
1.1 KiB
Bash
Executable File
34 lines
1.1 KiB
Bash
Executable File
#!/bin/sh
|
||
#
|
||
# 50meow_ntpsync — udhcpc 钩子(M22)
|
||
#
|
||
# /usr/share/udhcpc/default.script 结尾会执行 default.script.d/*,
|
||
# 参数是本轮 DHCP 事件(deconfig|bound|renew|leasefail|nak)。
|
||
# 本钩子只在拿到租约(bound/renew)时动作——这是设备上唯一可靠的
|
||
# 「网络真的就绪」事件(S40network 阶段 WiFi 还没起来)。
|
||
#
|
||
# 关键:**绝不能阻塞**。default.script 是 udhcpc 同步调用的,
|
||
# 这里把校时丢后台后立即 exit 0,否则会拖住 DHCP 脚本。
|
||
#
|
||
# $ntpsrv = DHCP option 42(NTP 服务器列表)。
|
||
# 实测 2026-11-06 热点不下发(租约环境变量里没有 ntpsrv),企业网可能有;
|
||
# 有就优先用,没有就走 /usr/bin/meow_ntpsync 里的内置 IP 兜底。
|
||
#
|
||
# 重复触发由 meow_ntpsync 自己的 1 h 短路 + 锁兜住:续租每 30 min 一次,
|
||
# 最多 1 h 真同步一次,开销可忽略。
|
||
#
|
||
case "$1" in
|
||
bound|renew) ;;
|
||
*) exit 0 ;;
|
||
esac
|
||
|
||
[ -x /usr/bin/meow_ntpsync ] || exit 0
|
||
|
||
if [ -n "$ntpsrv" ]; then
|
||
setsid /usr/bin/meow_ntpsync -s "$ntpsrv" >/dev/null 2>&1 &
|
||
else
|
||
setsid /usr/bin/meow_ntpsync >/dev/null 2>&1 &
|
||
fi
|
||
|
||
exit 0
|