4、软件时间同步实现:Linux PTP (ptp4l) 的配置与调试、phc2sys 的时钟同步、chrony 与 PTP 的协同

好,咱们进入软件时间同步这个实战环节。说实话,硬件时间同步搞定了,软件层面才是真正考验人的地方。我见过不少项目,硬件PTP跑得挺好,一到软件配置就翻车。今天咱们就把ptp4l、phc2sys和chrony这三板斧讲透。

4.1 ptp4l 的配置与调试

ptp4l是Linux上最常用的PTP实现,来自linuxptp项目。我个人习惯把它叫做「PTP的瑞士军刀」——功能全,但参数也多。

4.1.1 基本配置

先看一个最简配置。假设你的网卡是eth0,支持硬件时间戳:

# /etc/linuxptp/ptp4l.conf
[global]
# 指定PTP协议版本
network_transport      L2
# 时钟类型:OC是普通时钟,BC是边界时钟
clockClass             248
clockAccuracy          0xFE
# 主从模式:1表示slaveOnly,0表示自动协商
slaveOnly              1
# 日志打印级别
logSyncInterval        0
logAnnounceInterval    1
logMinDelayReqInterval 0
# 硬件时间戳
hwts_filter            enabled
# 网口绑定
ptp_dst_mac            01:1B:19:00:00:00

启动命令很简单:

ptp4l -f /etc/linuxptp/ptp4l.conf -i eth0 -m

-m参数会把日志打印到标准输出,调试时特别有用。我曾经在调试一个ADAS项目时,就是靠这个参数抓到了主时钟跳变的问题。

4.1.2 调试技巧

调试ptp4l,我一般看这几个关键指标:

  • offset:主从时钟偏差,单位纳秒。正常应该在±100ns以内
  • delay:网络路径延迟,单位纳秒。车载网络通常在10-50μs
  • drift:时钟漂移率,单位ppb。好的晶振在±50ppb以内

举个例子,你可能会看到这样的输出:

ptp4l[1234]: master offset        -12 s2 freq   -123 path delay       345
ptp4l[1234]: master offset         85 s2 freq   -120 path delay       340
ptp4l[1234]: master offset        -45 s2 freq   -125 path delay       348

嗯,这里要注意:如果offset一直在正负几百纳秒之间来回跳,说明网络抖动太大。我遇到过一种情况,是交换机上的PTP队列优先级没配好,导致同步报文被其他流量挤占了。

避坑指南:我曾经在调试时发现ptp4l一直报"ptp4l: timed out while polling for tx timestamp"。查了半天,原来是网卡的硬件时间戳功能没打开。记得先确认一下:ethtool -T eth0,看看有没有"hardware-transmit"和"hardware-receive"这两个标志。

4.2 phc2sys 的时钟同步

ptp4l同步的是网卡的硬件时钟(PHC),但应用程序用的是系统时钟。phc2sys就是干这个的——把PHC的时间同步到系统时钟。

4.2.1 基本用法

最常用的命令:

phc2sys -s eth0 -c CLOCK_REALTIME -m -O 0

参数说明:

  • -s eth0:源时钟,这里是网卡的PHC
  • -c CLOCK_REALTIME:目标时钟,系统实时时钟
  • -O 0:时钟偏移补偿,单位秒。如果你的系统时区不是UTC,这里要调整

启动后你会看到类似这样的输出:

phc2sys[5678]: CLOCK_REALTIME phc offset      -23 s2 freq   -456 delay      789
phc2sys[5678]: CLOCK_REALTIME phc offset       12 s2 freq   -450 delay      785

4.2.2 多网卡场景

车载网络经常有多个网卡。比如一个用于ADAS,一个用于信息娱乐。这时候phc2sys可以同时同步多个PHC:

phc2sys -s eth0 -c eth1 -m

这个命令会把eth0的PHC时间同步到eth1的PHC。说白了,就是让两个网卡的硬件时钟保持一致。

个人经验:我建议把phc2sys和ptp4l放在同一个启动脚本里。顺序很重要——先启动ptp4l,等它稳定了(大概5-10秒),再启动phc2sys。否则phc2sys一开始会报"clock not synchronized"的错误。

4.3 chrony 与 PTP 的协同

chrony是Linux上常用的NTP实现。你可能会问:有了PTP,为什么还要用chrony?

原因很简单:PTP精度高,但覆盖范围有限(通常在一个局域网内)。chrony可以跟外部时间源同步,比如GPS或者NTP服务器。两者配合,既能保证精度,又能保证可靠性。

4.3.1 配置chrony使用PTP作为参考时钟

在chrony的配置文件/etc/chrony/chrony.conf中,添加:

# 使用PTP硬件时钟作为参考源
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset 0
# 或者使用共享内存方式
refclock SOCK /var/run/ptp4l.clock.sock poll 3 dpoll -2 offset 0

这里有两种方式:

  • PHC方式:直接读取网卡的硬件时钟,精度最高
  • SOCK方式:通过共享内存读取ptp4l的时钟,稍微慢一点,但更灵活

4.3.2 优先级设置

chrony可以设置多个时间源的优先级。我一般这样配:

# PTP作为首选
refclock PHC /dev/ptp0 poll 3 dpoll -2 offset 0
# NTP作为备选
server ntp.aliyun.com iburst
# 本地时钟作为最后手段
local stratum 10

这样配置后,chrony会优先使用PTP。如果PTP信号丢失,自动切换到NTP。如果网络全断了,还有本地时钟兜底。

关键点:chrony的poll参数控制采样间隔。poll 3表示每8秒采样一次(2^3=8)。对于车载应用,我建议poll值设小一点,比如poll 2(4秒),这样响应更快。

4.3.3 协同工作的坑

chrony和PTP协同工作时,有个常见的坑:时间跳变。

为什么会这样?因为ptp4l和chrony都在调整系统时钟。如果两者同时调整,就可能打架。我遇到过的情况是:ptp4l把系统时钟往前调了100微秒,chrony检测到偏差又往后调了80微秒,结果时钟一直在震荡。

解决办法:让chrony只做「微调」,不做「大步调整」。

# 在chrony.conf中
makestep 0.001 3    # 只有偏差超过1ms才大步调整,最多3次
maxupdateskew 100   # 最大更新偏差100ppm

这样配置后,chrony会尽量通过频率调整来同步,而不是直接跳变时间。对于大多数车载应用来说,这个策略足够了。

4.4 实战检查清单

最后,我整理了一个检查清单,调试时对照着看:

步骤 检查项 验证方法
1 网卡支持硬件时间戳 ethtool -T eth0
2 ptp4l正常运行 journalctl -u ptp4l
3 PHC时钟精度 pmc -u -b 0 'GET CURRENT_DATA_SET'
4 phc2sys同步状态 phc2sys -s eth0 -c CLOCK_REALTIME -m
5 chrony时间源状态 chronyc sources -v
6 系统时钟偏差 chronyc tracking

嗯,这套流程我用了好几年,基本没出过大的问题。你想想看,车载网络的时间同步,说白了就是让所有节点对表。硬件层面搞定了,软件层面再配好,剩下的就是网络质量的问题了。

下一章咱们聊聊时间同步的测试与验证,到时候我会分享一些实际项目中的测试用例和故障排查方法。