故障速览
- 现象:WSL2 容器内的 FreeSWITCH 播音变慢、声音低沉,下行 RTP 发包间隔由标准的 20ms 沦为 21.82ms(比值为 1.091)。
- 根因:WSL2 内部微软原生
chronyd与 Ubuntusystemd-timesyncd冲突。为平滑拉回网络 NTP 造成的巨大偏差,chronyd触发最大降速率(1/12),将内核tick锁死在9167 µs,导致单调时钟慢了 $8.33%$($12/11 \approx 1.090909x$)。 - 根治:
sudo systemctl mask --now systemd-timesyncd禁用冲突服务,仅保留原生 Hyper-V PTP 同步即可。
1. 问题现象:变慢的声音与离奇的 21.82ms
在 Windows 宿主机通过 SIP 软电话呼入 WSL2 容器内的 FreeSWITCH 播音分机时,语音明显变慢,女声变成了低沉拖沓的“机械男声”。
排除 TTS 语速设置与音频采样率后,对下行 RTP 语音包进行抓包分析:
- 标称值:VoIP 默认
ptime = 20ms,即每秒均匀发送 50 个 RTP 包。 - 实测值:RTP 包到达间隔中位数变成了
21.82ms。
$$21.82 \div 20.00 \approx 1.0910$$
每个包都被延迟了约 1.82ms,整段语音时长被拉长约 $9.1%$,造成慢放失真。FreeSWITCH 依赖 Linux CLOCK_MONOTONIC(单调时钟)做定时唤醒,这意味着虚拟机自身的时钟变慢了。
2. 测量实锤:1.090909x 的数学巧合
通过精确测量 Windows 物理时间与 WSL2 内部时钟流逝:
| 计时指标 | Windows 物理时间 | WSL2 虚拟机时间 | 比值 (Windows / WSL) | 状态 |
|---|---|---|---|---|
CLOCK_MONOTONIC |
20.001 s |
18.334 s |
1.090909x |
慢了 8.3333% |
CLOCK_MONOTONIC_RAW |
20.001 s |
20.001 s |
1.000000x |
物理对齐 |
数据表明:
- 硬件计数器(RAW)与宿主完全 1:1 对齐,底层虚拟化时钟源正常。
- 常见的
CLOCK_MONOTONIC精确慢了 $8.3333%$,比例稳定在小数点后多位的1.090909。
通过 adjtimex 检查内核参数,发现了关键线索:
- 标准 tick:每秒 100 个滴答(
USER_HZ = 100),标准值为10000 µs。 - 现场 tick:被篡改成了
9167 µs。
这就是 1.090909x 的来源:
- 削减量:$10000 - 9167 = 833 \text{ µs}$,占标准值的 $\frac{833}{10000} \approx \frac{1}{12}$。
- 内核实际时钟推进速率缩水为 $\frac{11}{12}$。
- 外部时间与虚拟机内部时间的比率即为:
$$\frac{1}{11/12} = \frac{12}{11} = \mathbf{1.090909\dots}$$
3. 根因剖析:双校时冲突
为什么内核的 tick 会被锁死在 9167?排查发现 WSL2 内部有两个校时服务在“打架”:
- 微软原生
/init启动的chronyd:直接追踪 Hyper-V 提供的硬件 PTP 时钟(/dev/ptp0),与 Windows 宿主机保持亚微秒级同步。 - Ubuntu 发行版自带的
systemd-timesyncd:默认连接公网 NTP 服务器,但受宿主网络/防火墙影响频繁超时丢包,间歇性往内核写入数秒的虚假漂移(如 offset +7s)。
当原生的 chronyd 监测到内核时间被外部篡改出现巨大偏差时,由于已超过启动阶段的快速跳变窗口,为避免系统时间倒流,它只能进入 平滑校正(Slew) 模式。
Chrony 默认的最大平滑减速率 maxslewrate 恰好是:
$$\text{maxslewrate} = 83,333 \text{ ppm} = \frac{1}{12}$$
为了以最快速度拉回时间偏差,chronyd 将内核频率直接打满到极限降速——将 tick 从 10000 压低 $\frac{1}{12}$,也就是 9167 µs。而由于 systemd-timesyncd 持续制造偏差,减速过程永远无法结束,FreeSWITCH 的音频便被永久慢放。
4. 两步极简根治
无需编写后台看门狗脚本,只要消除冲突源,WSL2 原生时钟链路即可恢复正常:
步骤 1:禁用发行版 NTP 服务
1 | sudo systemctl stop systemd-timesyncd |
将 systemd-timesyncd 屏蔽后,系统仅保留微软原生的 PTP 时钟同步,不再受外网超时干扰,chronyd 永不触发 Slew 降速。
步骤 2:恢复干净的宿主 .wslconfig
清理排障过程中误加的临时参数(如误关 Hyper-V 同步的 hv_utils.timesync_implicit=0),保持配置纯净。
5. 验证效果
完成上述配置后重启 WSL2:
- 内核参数:
adjtimex中的tick稳定在标准的10000 µs,状态码为TIME_OK。 - 时钟比率:Windows 与 WSL2 单调时钟比值恢复为严格的
1.000000x。 - 通话实测:FreeSWITCH 呼叫测试分机,RTP 包间隔稳定在
20.00 ms,人声恢复正常清晰的语速,故障彻底解决。