故障速览

  • 现象:WSL2 容器内的 FreeSWITCH 播音变慢、声音低沉,下行 RTP 发包间隔由标准的 20ms 沦为 21.82ms(比值为 1.091)。
  • 根因:WSL2 内部微软原生 chronyd 与 Ubuntu systemd-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 物理对齐

数据表明:

  1. 硬件计数器(RAW)与宿主完全 1:1 对齐,底层虚拟化时钟源正常。
  2. 常见的 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 内部有两个校时服务在“打架”:

  1. 微软原生 /init 启动的 chronyd:直接追踪 Hyper-V 提供的硬件 PTP 时钟(/dev/ptp0),与 Windows 宿主机保持亚微秒级同步。
  2. 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
2
3
sudo systemctl stop systemd-timesyncd
sudo systemctl disable systemd-timesyncd
sudo systemctl mask systemd-timesyncd

将 systemd-timesyncd 屏蔽后,系统仅保留微软原生的 PTP 时钟同步,不再受外网超时干扰,chronyd 永不触发 Slew 降速。

步骤 2:恢复干净的宿主 .wslconfig

清理排障过程中误加的临时参数(如误关 Hyper-V 同步的 hv_utils.timesync_implicit=0),保持配置纯净。

5. 验证效果

完成上述配置后重启 WSL2:

  1. 内核参数:adjtimex 中的 tick 稳定在标准的 10000 µs,状态码为 TIME_OK。
  2. 时钟比率:Windows 与 WSL2 单调时钟比值恢复为严格的 1.000000x。
  3. 通话实测:FreeSWITCH 呼叫测试分机,RTP 包间隔稳定在 20.00 ms,人声恢复正常清晰的语速,故障彻底解决。

站内搜索

没有找到内容!