PTP / IEEE 1588 高精度时间同步完全指南:linuxptp(ptp4l / phc2sys / pmc / ts2phc)部署、四大 Profile 对比、以及 8 个硬件/配置坑
NTP 能把时间同步到 1~10ms,但到了 5G O-RU 帧头对齐、金融交易所订单撮合、广电 4K 演播室 SMPTE ST 2059-2、TSN 工业以太网、高速数据采集 DAQ 这些场景,就必须上 PTP(IEEE 1588)把误差压到亚微秒~纳秒级。本文从 OC / BC / TC 三种节点、Delay_Req/Resp 与 Pdelay 两种时延测量、一步/两步时钟这些基础概念开始,按『硬件前提→linuxptp 全家桶→两台直连 OC 最小可跑 Demo→生产 GM+BC+OC 架构→phc2sys 绑 PHC 与系统时钟→每个配置字段翻译→pmc/ethtool/phc_ctl 命令→8 个高频坑→四大行业 Profile 选型表』一步步铺完,所有命令都在 Ubuntu 24.04 + Intel i210/XL710 实测可用。
PTP / IEEE 1588 高精度时间同步完全指南:linuxptp(ptp4l / phc2sys / pmc / ts2phc)部署、四大 Profile 对比、以及 8 个硬件/配置坑
适用:Ubuntu 22.04 / 24.04 LTS、Debian 12、RHEL 9 / Rocky 9; 推荐网卡:Intel i210/i225/i226 / XL710 / XXV710 / E810、Mellanox ConnectX-4 Lx 及以上、Broadcom BCM5750x / NetXtreme-E; 交换机:Mellanox SN2010 / SN2410(Spectrum ASIC 支持 TC/BC)、Cisco Nexus 9300-EX/FX、HPE Aruba 6300M、华为 CE6865/CE8850 等明确写了”IEEE 1588 BC/TC 支持”的型号。
一. 前置概念:什么是 PTP、什么时候用 PTP、精度能到多少
0.1 PTP vs NTP 对比速表
| 维度 | NTP | PTP (IEEE 1588) |
|---|---|---|
| 典型精度 | 局域网 100μs ~ 1ms,跨公网 1ms ~ 50ms | 带硬件时间戳 + BC/TC 交换机 → 亚 μs ~ 100ns;PPS/PTP Grandmaster 同源可到 <50ns;纯软件走 SW timestamp → 10~50μs(比 NTP 好一点但不到”真 PTP”) |
| 通信协议 | UDP 123(单播为主) | UDP 319(事件消息)/ 320(通用消息)+ 二层多播(01-1B-19-00-00-00)常见;消息类型里 Event Msg 才打 HW timestamp |
| 同步算法 | 客户端拉式 poll 模式 + slew | 主从周期性 Sync + 双向 delay 测量 + PI 控制器锁相环(servo) |
| 谁负责消除中间节点延迟误差 | 全靠客户端补偿,交换机排队 + 非对称路径误差无法消除 | 节点类型里的 TC(透明时钟)会把”我停留了多少 ns”写进 CorrectionField;BC(边界时钟)重新发起下一跳 Sync,等于”每段重新同步” |
| 适用部署规模 | 几千~几万台(拉模式) | 几百~上千台(域内多播 224.0.1.129 / FF0X::181) |
| 复杂度 | 一条 server 指令完事 | 需要网卡 HW 时间戳 + 交换机支持 BC/TC + BIOS 关省电 + 对 PHC/系统时钟二次同步 |
0.2 到底谁真的需要 PTP(别没事上 PTP,成本高)
| 行业场景 | 要求精度 | 上不上 PTP |
|---|---|---|
| 一般后端业务 / 数据库 / etcd / ELK | ±100ms ~ ±1s | 不用,NTP/chrony 足够(别为了”看起来高端”硬上) |
| 分布式链路追踪 / Prometheus 采集对齐 | ±10ms | 不用,chrony + 局域网上游足够 |
| 高频交易 / 交易所订单匹配 / 期货撮合 | ±1μs ~ ±10μs | 必须 PTP,甚至要 GPS PPS 同源 GM + TSC 时钟 |
| 5G O-RAN O-RU / O-DU / TDD 帧头对齐 | ±1.5μs(ITU-T G.8271.1 Class C) | 必须 PTP(电信 G.8275.1 Profile) |
| 广电 4K/8K IP 演播室 SMPTE ST 2110 | ±1μs(SMPTE ST 2059-2 Genlock) | 必须 PTP(SMPTE 2059-2 Profile) |
| TSN 工业以太网 / 汽车自动驾驶多 ECU 同步 | ±100ns ~ ±1μs(IEEE 802.1AS) | 必须 PTP(gPTP Profile) |
| 多通道高速 ADC/DAQ 数据采集、雷达同步采样 | ±100ns | 必须 PTP 或直接 PPS 硬触发 |
| 电力 PMU / 行波测距 / 变电站 | ±1μs | 必须 IEEE 1588 + IEC 61850-9-3 |
0.3 PTP 核心概念一张图 + 术语小抄
flowchart TB G["GNSS 接收机(GPS / 北斗)"] GM["Grand Master(GM)<br/>OC 角色=master;priority1 最低;BMCA 选出的全局时间基准<br/>网卡带 PHC(PTP Hardware Clock)"] BC["Boundary Clock(BC)交换机<br/>收上游 Sync → 本地 PHY 打时间戳 → 锁本地 PHC → 再给下游发新 Sync<br/>每跳重新计时,不累积误差;也可旁挂 TC 交换机"] OC1["OC Slave(Ordinary Clock)<br/>带应用的服务器 / 5G DU<br/>程序读 CLOCK_REALTIME 或 CLOCK_TAI"] OC2["OC Slave<br/>摄像机 / 麦克风<br/>SMPTE2110 Sender"] TC["TC(Transparent Clock)<br/>只转发、把驻留时间写进 CorrectionField<br/>不参与 BMCA,不改 sync"] OC3["OC Slave<br/>PLC / 采集卡<br/>TSN 端点"] G -->|"PPS / 1PPS / ToD 串口或 NMEA"| GM GM -->|"Sync / Follow_Up<br/>每 125ms / 250ms / 1s 发"| BC BC -->|"下游 Sync"| OC1 BC --> OC2 BC --> TC BC --> OC3
术语小抄(每条都是你看配置文件会反复出现的):
| 术语 | 一句话 | 常见值/注意 |
|---|---|---|
| OC | Ordinary Clock 普通时钟 | 只有一个 PTP 端口;要么当 Master 要么当 Slave;我们大多数服务器就是 OC Slave |
| BC | Boundary Clock 边界时钟 | 多个 PTP 端口,上游 Slave、下游 Master,逐段重新同步 |
| TC | Transparent Clock 透明时钟(有 E2E TC / P2P TC) | 不改 Sync 内容,只把”我这台交换机排队滞留了多少 ns”写到 CorrectionField 里 |
| GM / Grandmaster | BMCA 算法选出来的全局时间基准源(老大) | priority1/priority2 越小越优先 |
| BMCA | Best Master Clock Algorithm 最佳主时钟选举 | 看 priority1→class→accuracy→variance→priority2→UUID,两步走 |
| Domain Number (Domain) | 同一链路上可以跑多组互不干扰的 PTP | 0~127;G.8275.1 常用 domain 24;SMPTE 常用 domain 128;两边不一样永远收不到消息 |
| Sync / Follow_Up / Delay_Req / Delay_Resp | PTP 四个核心事件/通用消息 | 详见 §0.4 |
| Pdelay_Req / Pdelay_Resp / Resp_Follow_Up | P2P(点到点延迟测量)用的一组(per link) | 整条链路所有 TC 都必须支持 P2P 才能用 |
| One-step(一步时钟)/ Two-step(两步时钟) | One-step:Sync 帧里就带精确 TX 时间戳(硬件立刻改帧内容);Two-step:Sync 先发、随后再发 Follow_Up 补真实 TX 时间戳(现在多数网卡是 Two-step,ptp4l 会自动判断 Support) | 网卡手册写着 1588 One-step / support follow_up |
| Correction Field | PTP 头里 64 位 ns 字段,TC 把排队驻留时间加上,从端用它修正 offset | 越大说明路径抖动越大 |
| PHC | PTP Hardware Clock:网卡芯片上独立的 64 位 ns 计数器(和主机系统时钟独立!) | /dev/ptp0、/dev/ptp1 字符设备,由驱动注册 |
| meanPathDelay | 单向平均链路延迟(从 master→slave 的 ns 值) | 由 delay_meas 算出来,每几秒更新一次 |
| offsetFromMaster | slave 当前比 master 偏了多少 ns(正=我比它快,负=我慢) | 这个值压到 ±100ns 就是健康的 PTP |
| Servo / PI controller | ptp4l 内部用比例积分控制器把 offset 压向 0,类似锁相环 | 配置 pi_proportional_const / pi_integral_const |
| Sync interval(logSyncInterval) | 每 2^n 秒发一次 Sync,写 log2 | -3=每 125ms 发一次(电信/广电常见)、0=每 1s(default profile) |
0.4 一次同步的四个报文(E2E 两步时钟时序,最通用的模式)
sequenceDiagram participant M as Master participant S as Slave M->>S: Sync(t1 还不知道,等 Follow_Up 补) Note over S: Slave 收到记 t2(RX 硬件时间戳) M->>S: Follow_Up(携带真实 t1 = Master TX 硬件时间) S->>M: Delay_Req(t3 = Slave TX 硬件时间) Note over M: Master 收到记 t4(RX 硬件时间戳) M->>S: Delay_Resp(携带真实 t4) Note over S: meanPathDelay = ((t2-t1) + (t4-t3)) / 2 Note over S: offsetFromMaster = (t2-t1) - meanPathDelay Note over S: 然后用 PI servo 把 offset 慢慢调到 0
如果是 P2P:在 Delay_Req/Resp 那步换成 Pdelay_Req/Pdelay_Resp,并且每一对直连邻居单独算 path delay,不再依赖 GM 的回程时间;P2P TC 会把自己驻留+链路延迟都写 CorrectionField 里。电信 G.8275.1 要求 P2P。
二. PTP 版本与四大行业 Profile(决定你怎么写 ptp4l.conf)
先选 Profile 再搭环境,不要一股脑用 default,行业设备互不兼容会白折腾。
| Profile | 对应标准 | 领域 | Domain Number 默认 | Transport | Delay 测量 | Sync Interval | 硬件要求 |
|---|---|---|---|---|---|---|---|
| Default Profile ⭐(实验室先跑通用这个) | IEEE 1588-2008 Annex J / 1588-2019 | 通用 PTP 教学、普通高精度服务器 | 0(推荐改) | UDP IPv4/v6 或 L2 | E2E | logSyncInterval=0 (1s) | 任意支持 HW TS 的网卡即可,不要求交换机 TC/BC |
| G.8275.1 (PTP 电信相位/时间同步 Profile) | ITU-T G.8275.1 (2022) | 5G O-RAN / 电信传输 / 基站 ToD | 24(业界默认) | L2 Ethernet(不封装 IP/UDP) | P2P(强制) | logSyncInterval=-3 (125ms) | T-BC/T-T-TSC 全链路 BC/TC;普通服务器做不了 GM,要专用 GM 设备 |
| G.8265.1 (PTP 频率 Profile) | ITU-T G.8265.1 | 同步网频率(不关心墙上时间,只要频率稳) | 4 | UDP/IP | E2E | 16 包/秒 | 支持 SyncE 最佳 |
| SMPTE ST 2059-2 | SMPTE | 广电 IP 演播室 2110 / Genlock | 128(SMPTE 约定) | UDP/IP | E2E 或 P2P | 125ms ~ 1s | 要求 SMPTE 2059-2 Profile 交换机 BC |
| IEEE 802.1AS / gPTP | IEEE 802.1AS-2020 | TSN 工业/汽车自动驾驶/AVB 音视频 | 0 | L2 | P2P(强制) | 125ms ~ 8s | 全链路必须 802.1AS-capable;普通网卡不行,车上用汽车级以太网 PHY |
本文后面的”两台直连 Demo”用 Default Profile,生产”GM + BC 架构”以 Default/G.8275.1 混合举例。
三. 硬件前提(PTP 最容易踩坑的部分:硬件不满足,调一个月也出不来亚微秒)
2.1 网卡:必须带 PHC(PTP Hardware Clock)+ HW timestamping
先查你机器网卡到底支不支持,别瞎调:
# 查 PCI 网卡型号
lspci | grep -i ether
lshw -C network | grep -E 'product|vendor|serial|bus info'
# === 最关键的一条命令:ethtool -T ===
sudo ethtool -T eth0能跑”真 PTP”的 ethtool 输出(i210 / ConnectX-6 Dx 典型):
Time stamping parameters for eth0:
Capabilities:
hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE) ✅ 必须
software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)
hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE) ✅ 必须
software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)
software-system-clock (SOF_TIMESTAMPING_SOFTWARE)
hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE) ✅ 必须(= 暴露 /dev/ptpX)
PTP Hardware Clock: 0 ✅ 会在 /dev/ptp0 看到
Hardware Transmit Timestamp Modes:
HWTSTAMP_TX_ON ✅ 单包开启 HW 打戳
HWTSTAMP_TX_ONESTEP_SYNC ✅ One-step 同步帧
Hardware Receive Filter Modes:
HWTSTAMP_FILTER_NONE
HWTSTAMP_FILTER_ALL
HWTSTAMP_FILTER_PTP_V2_L4_EVENT
HWTSTAMP_FILTER_PTP_V2_L2_EVENT
HWTSTAMP_FILTER_PTP_V2_L4_SYNC
HWTSTAMP_FILTER_PTP_V2_L2_SYNC常见合格网卡速查表:
| 厂商 | 型号 | PHY 时间戳 One-step | 典型用途 |
|---|---|---|---|
| Intel | i210 / i210-IS(带 i210 驱动 igb) | 支持 | 入门 PTP 首选;便宜、驱动稳 |
| Intel | i225/i226 / TSN 版 i226-LM | 支持 | 新 2.5G 工业小机 |
| Intel | XL710 / XXV710 (i40e) | 支持 | 25G SFP28 机房主流 |
| Intel | E810-C / E810-XXVDA4 (ice) | One-step Sync | 10G~100G,4G/5G 基站回传 |
| Mellanox/NVIDIA | ConnectX-4 Lx / 5 / 6 / 7 Dx | 支持,Follow_Up 模式常见 | 10G~400G 全场景,mlx5_core 驱动自带 ts |
| Broadcom | BCM57414 / BCM57504 / NetXtreme-E | 支持 | HPE/Dell 板载 |
常见不合格的坑:
- Realtek RTL8111H/RTL8125:基本都是 SW Timestamp,别试
- VMWare VMXNET3 / KVM Virtio-NIC:完全没有 HW TS,虚拟机上跑 PTP 意义不大(SW Timestamp 10μs 顶天)
2.2 交换机:必须支持 802.1AS / BC / TC,不要用”傻交换机”
- 合格:写了”IEEE 1588v2 BC / End-to-End Transparent Clock / Peer-to-Peer TC”的交换机
- 不合格:普通千兆无管理交换机、一般园区接入交换机(会把几百 ns~几 μs 的排队抖动直接吃进 path delay)
- 偷懒技巧:两台机器网线直连,没有中间交换机就不用考虑 TC/BC,是入门最稳的
2.3 服务器 BIOS:关省电、开性能模式
- CPU C-state / C1E / Intel SpeedStep / AMD PowerNow → Disable(PC 省电会让 TSC 跳)
- PCIe ASPM (L0s / L1) → Disable(ASPM 进入低功耗会让 PHY 时钟漂移几十 ns)
- Memory power saving / DRAM refresh partial self-refresh → Performance
- SR-IOV 无关时关
2.4 布线 / 物理层
- 铜缆:Cat6a 屏蔽(UTP 够了,别用 Cat5e),长度尽量一致(左右 <10m 差),减少路径非对称性
- 光模块:单模 1310nm LR、多模 850nm SR 都行,主从尽量用同一型号光模块(RX/TX 延迟对称性好)
- 不要走 NAT / 多跳 / 路由器跨 VLAN(PTP 默认走二层多播;走三层要静态 IP Multicast Routing + PIM 加组)
四. Linux PTP 栈:linuxptp 全家桶安装与字符设备
PTP 不是一个程序搞定的,是一个组合拳,分工如下:
| 程序 | 职责 | 类比 NTP 什么角色 |
|---|---|---|
ptp4l | 运行 IEEE 1588 状态机:BMCA 选 GM、发/收 Sync/FollowUp/DelayReq,算 offset,用 PI 伺服器调整 PHC 时钟 | 相当于 chronyd,但是调的是网卡 PHC,不是系统时钟 |
phc2sys | PHC ↔ CLOCK_REALTIME(系统时钟)双向同步:把 ptp4l 锁好的 PHC 同步给系统时钟,或从 GPS PPS 得到的系统时钟往 PHC 刷 | chrony 的 rtcsync 对应,只不过是 PHC 和系统时钟之间的胶水 |
ts2phc | 外部 1PPS / GPS NMEA / 10MHz → 写入网卡 PHC(GM 机专用,把 GPS 接收机的秒脉冲喂给 PHC 成为 Grandmaster) | 相当于 ntpd 里的 server 127.127.x.* PPS refclock |
pmc | PTP Management Client:向 ptp4l 发管理查询,读 offsetFromMaster / meanPathDelay / GM 信息 | 相当于 chronyc(但只读多) |
phc_ctl | 调试用:直接读 /dev/ptpX,两个 PHC 对差、手动 step、频率校准 | 相当于 hwclock,但比 hwclock 精细到 ns |
hwstamp_ctl | 对网卡开启/关闭 TX/RX 硬件时间戳(一般 ptp4l 自动做) | 底层工具 |
testptp | 内核源码 sample 工具:打 PPS、读 PHC 周期、PTP 外设自检 | 验证驱动用 |
3.1 安装 linuxptp + 确认 /dev/ptp* 存在
# ===== Ubuntu 22.04 / 24.04 / Debian 12 =====
sudo apt update
sudo apt install -y linuxptp ethtool pps-tools
# 验证
which ptp4l phc2sys pmc ts2phc phc_ctl hwstamp_ctl
# → /usr/sbin/ptp4l /usr/sbin/phc2sys ... 齐了
ls -la /dev/ptp*
# → crw-rw-rw- 1 root root 248, 0 Aug 27 09:00 /dev/ptp0 ✅ 这就是网卡 PHC
# 多 PTP 网卡会有 /dev/ptp1 etc.
# ===== RHEL 9 / Rocky 9 =====
sudo dnf install -y linuxptp ethtool pps-tools看哪个网卡对应哪个 /dev/ptpX:
# 方法 A:ethtool -T eth0 里写了 PTP Hardware Clock: 0 → 对应 /dev/ptp0
# 方法 B:sysfs 一条命令搞定
for d in /sys/class/net/*; do
echo "$(basename $d) -> $(readlink -f $d/device/ptp/ptp* 2>/dev/null || echo NONE)"
done五. 最小可跑 Demo:两台机器网线直连 + Default Profile + 硬件时间戳(10 分钟入门)
两台完全一样的机器最好,网卡同型号(路径非对称最小化)。两台都按 §2 做过 ethtool 检查,都有 /dev/ptp0。
| 角色 | IP(二层多播其实不要 IP,只是 SSH 管理用) | 网卡名 |
|---|---|---|
| Master(未来 GM 候选) | 192.168.99.1/24 | enp1s0 |
| Slave | 192.168.99.2/24 | enp1s0 |
4.1 两台都先做:关掉 NetworkManager 的”自动改时间”,网卡 up
sudo systemctl disable --now chrony ntp systemd-timesyncd 2>/dev/null || true
sudo ip link set enp1s0 up
sudo ip addr add 192.168.99.1/24 dev enp1s0 # Master 执行;Slave 写 24.2 两台共用一份 /etc/linuxptp/ptp4l.default.conf(直接抄)
mkdir -p /etc/linuxptp
cat | sudo tee /etc/linuxptp/ptp4l.default.conf > /dev/null <<'EOF'
# ============ Default Profile (Demo 专用,生产要改) ============
[global]
# 我当从机吗? slaveOnly=1 强制当 Slave,不参与 BMCA
# 两台都先设 0(默认参加 BMCA,priority1 小的会被选为 GM)
slaveOnly 0
# GM 选举优先级,越小越优先(Master 写 128,Slave 可以在启动参数覆盖成 255)
priority1 128
priority2 128
# PTP Domain:0~127,两边必须一样!
domainNumber 0
# 传输层:UDP over IPv4 (1);L2 Ethernet 用 0;G.8275.1 必须 L2
network_transport UDPv4
# 延迟测量:E2E(0) = Delay_Req;P2P(1) = Pdelay_Req
delay_mechanism E2E
# 时钟伺服:PI (比例积分,默认) 或 linreg
clock_servo pi
# 一步/两步:0 = 自动检测 follow_up;1 = 强制一步;通常保持 0 自动
twoStepFlag 0
# 首次上线误差 > 200ns 时允许一次性 step(对齐后就用 PI 慢慢拉)
first_step_threshold 0.0000002
# 正常运行中,单次 offset 超过多少才允许 step(0=永远不 step,只 slew)
step_threshold 0
# 频率调整上限(ppb,默认 900M=90%,小网卡晶振差可放宽到接近 1e9=100%)
max_frequency 900000000
# Sync/Announce/Delay_Req 的间隔 (log2 秒):0 = 1s;-3 = 125ms;-4 = 62.5ms
logSyncInterval 0
logAnnounceInterval 1
logMinDelayReqInterval 0
# 宣告超时 = announceReceiptTimeout × 2^logAnnounceInterval = 5 × 2s = 10s,收不到 Announce 就进入 BMCA 重选
announceReceiptTimeout 5
# PI 伺服参数(Default Profile 默认,G.8275.1 有推荐值)
pi_proportional_const 0.700
pi_integral_const 0.010
# TX timestamp 超时:等不到 TX 硬件时间戳回复就丢包重打
tx_timestamp_timeout 10
# 网卡驱动名(DPDK/特殊驱动需要;普通 igb/mlx5_core 留空)
# network_transport ... 上面已写
# 每个接口参数(可覆盖 global)
[enp1s0]
# 单独指定该网卡用 HW timestamp(默认 AUTO);想强制软时间戳做对比可以写 software
# time_stamping hardware
EOF4.3 Master 启动(前台 + verbose,观察现象)
# 开 1 号终端,前台跑(生产后续改 systemd service)
sudo ptp4l -f /etc/linuxptp/ptp4l.default.conf -i enp1s0 -m -q -l 6 \
--priority1=128
# -m = 输出到 stdout;-q = 不打 syslog;-l 6 = INFO;--priority1 覆盖配置文件里的理想输出(30s 后):
ptp4l[12345]: selected /dev/ptp0 as PTP clock
ptp4l[12345]: port 1: INITIALIZING to LISTENING on INIT_COMPLETE
ptp4l[12345]: port 0: INITIALIZING to LISTENING on INIT_COMPLETE
ptp4l[12345]: port 1: new foreign master ...
ptp4l[12345]: selected best master clock 0x001b21.fffe.000001
ptp4l[12345]: assuming the grand master role ✅ 因为 priority1 最小 + 没别人,我当 GM4.4 Slave 启动(另一台机器,前台 verbose)
# Slave 用更大的 priority1=255,让 BMCA 一定选 Master 当 GM
sudo ptp4l -f /etc/linuxptp/ptp4l.default.conf -i enp1s0 -m -q -l 6 \
--priority1=255Slave 端理想输出(~5 秒后开始 lock):
ptp4l[20001]: master offset 123456 s0 freq +890 path delay 450
ptp4l[20001]: master offset 98765 s1 freq +1020 path delay 451
ptp4l[20001]: master offset 42 s2 freq +998 path delay 450 ✅ s2 = 进入 PHASE_LOCKED 了
ptp4l[20001]: master offset -8 s2 freq +991 path delay 450s0 / s1 / s2 三状态:s0 = 初始化没对齐;s1 = 刚 step 过一次/frequency lock;s2 = PHASE LOCKED,offset 稳定到几十 ns,才算成功。
4.5 用 pmc 正式查 offset(比看日志靠谱,可脚本化)
# 本机查(Slave 上执行,读 Current / Parent / TimeStatus 三个数据集)
sudo pmc -u -b 0 -f /etc/linuxptp/ptp4l.default.conf \
'GET CURRENT_DATA_SET' \
'GET PARENT_DATA_SET' \
'GET TIME_STATUS_NP'期望结果关键字段:
offsetFromMaster :: -12.0 ✅ 正负 50ns 内都很好
meanPathDelay :: 448 ✅ 双向时延均值 ns
parentPortIdentity :: 001b21.fffe.000001-1 ✅ 指向 Master MAC 生成的 ID
gmIdentity :: 001b21.fffe.000001 ✅ 和 Master ID 同
gmPresent :: true
gmTimeBaseIndicator :: 0
lastGmPhaseChange :: 0x0000
leap61 / leap59 / currentUtcOffsetValid :: false/false/true
timeTraceable / frequencyTraceable :: true/true4.6 把 PHC 同步给系统时钟(这样 date / CLOCK_REALTIME 读的时间才是对的!)
ptp4l 只调 PHC(/dev/ptp0),不碰系统时钟,一定要再跑一个 phc2sys 做胶水:
# Slave 机器上:以 /dev/ptp0(ptp4l 锁好的网卡硬件时钟)为源,去同步 CLOCK_REALTIME(系统时钟)
sudo phc2sys -s /dev/ptp0 -c CLOCK_REALTIME -w -m -q -O 0 -l 6
# -s = source clock
# -c = destination clock
# -w = 等 ptp4l 进入 s2 PHASE_LOCKED 再干活(安全!不会拿错的 PHC 刷系统时间)
# -O 0 = 不额外加固定 TAI/UTC offset(有闰秒时按 TAI 偏移修正,一般 0 就行)
# -n <domain> 可以通过 domain number 自动找 ptp4l 用的网卡,不用手动写 /dev/ptpXphc2sys 理想输出:
phc2sys[...]: offset -34 s2 freq +1234 delay 12345 ✅ offset < ±1μs 正常⚠️ 反过来 GM 端如果 GPS 给的是系统时钟,要把 CLOCK_REALTIME 刷到 PHC:
phc2sys -s CLOCK_REALTIME -c /dev/ptp0 -w -m。
六. 生产级架构:Grand Master(接 GPS)→ BC 交换机 → 几十台 OC Slave
5.1 GM 机器选型 & GPS 接收机
- 建议 GM 机:Intel i3 N100 小主机 + i210 网卡 + GPS 接收机(串口/USB PPS) 或专业 GPSDO(Trimble Resolution T、u-blox ZED-F9T、Leica 等)
- GM 机需要把 GPS 的 1PPS(秒脉冲物理信号+NMEA RMC/GGA 报文+ToD) 结合,得到一个”准到 20ns 以内的 UTC 时间”喂给 PHC
- linuxptp 自带 ts2phc:把外部 PPS 信号(接另一个 i210 的 SDP 引脚)或串口 10MHz 对齐到 PHC
5.2 ts2phc 把 GPS PPS → 写入 PHC(GM 专用)
典型接线:GPS 接收机的 1PPS TTL 引脚 → i210 的 SDP0(通用引脚,驱动可做外部触发);NMEA 串口 → /dev/ttyS0 用 chrony/NMEA 先粗略对齐。
cat | sudo tee /etc/linuxptp/ts2phc.conf > /dev/null <<'EOF'
[global]
leapfile /usr/share/zoneinfo/leap-seconds.list
# 全局 TS2PHC 模式:每收到一个外部 PPS,用它校正 PHC
ts2phc.pulsewidth 100000000
ts2phc.tty /dev/ttyUSB0 # NMEA 串口,可选
ts2phc.message_id GGA RMC # NMEA 语句
# pin 映射:网卡 enp1s0 (PHC index 0) 的 SDP0 作为 PPS in
[enp1s0:ts2phc]
ts2phc.extts_polarity rising
ts2phc.channel 0
ts2phc.pin 0
EOF
sudo ts2phc -f /etc/linuxptp/ts2phc.conf -c /dev/ptp0 -m -l 6如果嫌接线麻烦,直接让 GM 的 chrony 先通过 PPS refclock 锁到 UTC ±1μs,再 phc2sys 把系统时钟写进 PHC。效果差不多(200ns 内),但简单很多。
5.3 GM / BC 交换机 / Slave 角色各自的典型 ptp4l.conf 片段
GM(priority1=1,强制不 slaveOnly,接 PPS 后就是全局老大):
[global]
slaveOnly 0
priority1 1
priority2 1
domainNumber 24
# 如果是电信 G.8275.1,下面三行必须
# network_transport L2
# delay_mechanism P2P
# G.8275.1 电信 domainNumber=24
# SMPTE 2059-2 一般 domainNumber=128
clockClass 6 # GM 被 GPS 锁到 =6;没锁=248
clockAccuracy 0x21 # 25ns~100ns(u-blox F9T 常见等级)
offsetScaledLogVariance 0x1000
# Announce/Sync 要密集
logSyncInterval -3 # 125ms
logAnnounceInterval 0 # 1s 一条宣告
first_step_threshold 0.0000001
step_threshold 0
tx_timestamp_timeout 20
kernel_leap 1Slave OC(普通业务机):
[global]
slaveOnly 1 # 不参与选举,我就是来当从机的,省 BMCA 计算
priority1 255
domainNumber 24 # 跟 GM 一样!可选 0/24/128
delay_mechanism E2E # 跟链路上 TC 模式一致!可选 E2E/P2P
clock_servo pi
first_step_threshold 0.0000002
step_threshold 0.000000050 # 正常运行中最多 step 50ns,否则只 slew
pi_proportional_const 0.300
pi_integral_const 0.003
sanity_freq_limit 200000 # 频率疯了(>200ppm)就停下来,防止跳时
tx_timestamp_timeout 10
# 用 phc2sys -n <domain> 自动找这个 slave 的 PHC5.4 systemd 持久化(生产一定要写成 service,不要前台跑)
Ubuntu 24.04 带的 linuxptp 包可能已经有模板,我们覆盖成自己的:
# ---- ptp4l@.service:按网卡名启用,比如 systemctl enable --now ptp4l@enp1s0 ----
cat | sudo tee /etc/systemd/system/ptp4l@.service > /dev/null <<'EOF'
[Unit]
Description=Precision Time Protocol (PTP) service for %I
Documentation=man:ptp4l
After=network-online.target
Wants=network-online.target
Requires=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/ptp4l -f /etc/linuxptp/ptp4l.%i.conf -i %I -l 6 -q
Restart=on-failure
RestartSec=3
AmbientCapabilities=CAP_SYS_TIME CAP_NET_ADMIN CAP_NET_RAW
[Install]
WantedBy=multi-user.target
EOF
# ---- phc2sys@.service:按 domain 启用,比如 phc2sys@0 ----
cat | sudo tee /etc/systemd/system/phc2sys@.service > /dev/null <<'EOF'
[Unit]
Description=Synchronize PTP Hardware Clock and System Clock (domain %i)
Documentation=man:phc2sys
After=ptp4l.target
[Service]
Type=simple
ExecStart=/usr/sbin/phc2sys -n %i -w -l 6 -q -O 0
Restart=on-failure
RestartSec=3
AmbientCapabilities=CAP_SYS_TIME
[Install]
WantedBy=multi-user.target
EOF
# ---- ts2phc@.service:GPS 串口转 PTP 硬件时钟 ----
cat | sudo tee /etc/systemd/system/ts2phc@.service > /dev/null <<'EOF'
[Unit]
Description=GPS to PTP Hardware Clock (%i)
Documentation=man:ts2phc
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/sbin/ts2phc -f /etc/linuxptp/ts2phc.%i.conf -l 6 -q
Restart=on-failure
RestartSec=3
AmbientCapabilities=CAP_SYS_TIME
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
# Slave 机启动
sudo systemctl enable --now ptp4l@enp1s0 phc2sys@0
# GM 机(domain 24)
sudo systemctl enable --now ts2phc@enp1s0 ptp4l@enp1s0 phc2sys@245.5 锁状态/监控怎么看(生产脚本化)
# 1. 看 ptp4l 锁状态(0=s0 FAILED, 1=s1 FREQ, 2=s2 PHASE)
sudo pmc -u -b 0 'GET TIME_STATUS_NP' | awk '/phaseState/{print $NF}'
# 脚本健康检查:必须输出 "LOCKED" 或数字 2
# 2. 看 offsetFromMaster 绝对值(必须 < 250ns warning / < 1000ns critical)
sudo pmc -u -b 0 'GET CURRENT_DATA_SET' | awk '/offsetFromMaster/{o=$NF;if(o<0)o=-o;print o}'
# 3. 读两台机 PHC 绝对差(同一台机上没法读两台的,用 phc_ctl 读本台+日志比对;最准的是共用 PPS 进示波器)
sudo phc_ctl /dev/ptp0 get
# 4. phc2sys 日志看"是否在稳定同步"
journalctl -u phc2sys@0 -n 50Prometheus 监控接入(可选,推荐):ptp_exporter 或 linuxptp_exporter,告警:
ptp_phase_state != 2→ criticalabs(ptp_offset_from_master_ns) > 250→ warning;>1000 → criticalptp_mean_path_delay_ns突变 → warning(链路换了/中间 TC 下线)phc2sysabs(offset) > 2000ns→ warning
七. ptp4l.conf 常用字段速查(含 Default / G.8275.1 推荐值)
| 字段 | 含义 | Default 推荐 | G.8275.1 电信推荐 | SMPTE 2059-2 推荐 |
|---|---|---|---|---|
slaveOnly | 1=强制当 Slave 不参选 GM | Slave 机写 1 | OC Slave 写 1 | OC Slave 写 1 |
priority1 / priority2 | GM 选举权重 | GM=128,Slave=255 | GM=1~10 | GM=100 |
domainNumber | 域号 | 0 | 24 | 128 |
network_transport | UDPv4 / UDPv6 / L2 | UDPv4 | L2 | UDPv4 |
delay_mechanism | E2E / P2P | E2E | P2P | E2E 常见 |
twoStepFlag | 一步/两步 | 0 自动 | 0 自动 | 0 自动 |
first_step_threshold | 首次 step 阈值(秒) | 0.0000002 (200ns) | 1μs | 1μs |
step_threshold | 稳态 step 阈值(0=永不 step) | 0 | 0 | 50ns |
max_frequency | 频率调整上限(ppb) | 900M | 200M | 200M |
logSyncInterval | Sync 间隔 log2s | 0 (1s) | -3 (125ms) | -3 ~ -1 |
logAnnounceInterval | Announce 间隔 | 1 (2s) | 0 (1s) | 0 (1s) |
announceReceiptTimeout | 宣告超时 = t × 2^logAnnounceInterval | 5 (10s) | 3 (3s) | 3 (3s) |
clock_servo | pi / linreg / nullf | pi | pi | pi |
pi_proportional_const | 比例项(越大反应越快但容易抖) | 0.700 | 0.300 | 0.150 |
pi_integral_const | 积分项(越大频率修正越重) | 0.010 | 0.003 | 0.0015 |
tx_timestamp_timeout | 等 TX 硬件时间戳的 ms 超时 | 10 | 20 | 10 |
sanity_freq_limit | 频率超了自动停下(ppb) | 0=关 | 200000 (200ppm) | 100000 (100ppm) |
leapfile | 闰秒表路径 | /usr/share/zoneinfo/leap-seconds.list | 同 | 同 |
kernel_leap | 闰秒前要不要给 kernel 发 announce | 1 | 1 | 1 |
uds_address | pmc 通信本地 socket | /var/run/ptp4l | 同 | 同 |
八. 常用命令速查(linuxptp + 调试工具)
| # | 命令 | 作用 | 典型场景 |
|---|---|---|---|
| 1 | ethtool -T eth0 | 查网卡 HW/SW TS 能力 + 对应 /dev/ptpX | 接新机第一条查 |
| 2 | ls /dev/ptp* && cat /sys/class/net/eth0/device/ptp/ptp*/clock_name | 网卡↔PHC 对应 | 多 PTP 网卡 |
| 3 | sudo ptp4l -f conf -i eth0 -m -q -l 6 --priority1=N | 前台调试跑 ptp4l | Demo/排障 |
| 4 | sudo pmc -u -b 0 'GET CURRENT_DATA_SET' | offsetFromMaster / meanPathDelay | 健康检查最常用 |
| 5 | sudo pmc -u -b 0 'GET PARENT_DATA_SET' | gmIdentity / parentStats | 确认跟着谁走 |
| 6 | sudo pmc -u -b 0 'GET TIME_STATUS_NP' | gmPresent / phaseState s0-s2 / leap | 有没有 LOCKED |
| 7 | sudo pmc -u -b 0 'GET PORT_DATA_SET' | portState / tlv / version | 本机角色 |
| 8 | sudo pmc -u -b 0 'GET DEFAULT_DATA_SET' | priority 1/2 / numberPorts / clockIdentity | BMCA 选举调试 |
| 9 | sudo phc2sys -s /dev/ptp0 -c CLOCK_REALTIME -w -m | PHC → 系统时钟同步 | Slave 必跑 |
| 10 | sudo phc2sys -s CLOCK_REALTIME -c /dev/ptp0 -w -m | 系统时钟(GPS 锁好的)→ PHC | GM 配合 GPS chrony PPS |
| 11 | sudo phc2sys -n 0 -w -m | 自动按 domain 找 ptp4l 开的 PHC,同步到系统时钟 | 脚本友好,不用写 /dev/ptpX |
| 12 | sudo ts2phc -f conf -c /dev/ptp0 -m | 外部 PPS/NMEA → PHC | 做 GM |
| 13 | sudo phc_ctl /dev/ptp0 get | 直接读 PHC 时间戳(ns) | 对比两台 GM 的 PHC |
| 14 | sudo phc_ctl /dev/ptp0 cmp | PHC 和 CLOCK_REALTIME 的当前差 | phc2sys 有没有干活 |
| 15 | sudo hwstamp_ctl -i eth0 -r 1 -t 1 | 手动开 RX/TX HW timestamp | ptp4l 没自动开时 |
| 16 | sudo testptp -d /dev/ptp0 -T 10 | 连续读 10 次 PHC 看漂移 | 驱动调试 |
| 17 | ppscheck /dev/pps0 | 看 GPS PPS 引脚是否收到稳定的每秒上升沿 | GM 接 GPS 验收 |
| 18 | journalctl -u ptp4l@enp1s0 -n 50 --no-pager | 看 ptp4l 日志 | 出问题首查 |
| 19 | sudo pmc -u -b 0 -d 24 'GET CURRENT_DATA_SET' | 指定 domain=24 的管理查询 | G.8275.1 |
| 20 | sudo systemctl status ptp4l@enp1s0 phc2sys@0 ts2phc@enp1s0 | 三件套 systemd 状态 | 日常巡检 |
九. 8 个高频坑(按出现率排序,每个都能卡一周)
🐞 1. 以为在跑 PTP,实际只是软件时间戳,精度 10μs~100μs 就上不去
排查:ethtool -T 看 SOF_TIMESTAMPING_TX_HARDWARE / RX_HARDWARE / RAW_HARDWARE 三项有没有 ✅;没有就是 SW。ptp4l 启动日志里也写:
selected /dev/ptp0 as PTP clock ✅ 硬件模式
using clockid CLOCK_REALTIME ❌ 软件模式!拿不到 HW TS修:换网卡 / 更新网卡固件 / 加 time_stamping hardware 到 ptp4l.conf interface 段。
🐞 2. domainNumber / delay_mechanism / network_transport 三边有一边不一致
现象:Slave 永远 s0 INITIALIZING,pmc GET PARENT_DATA_SET gmIdentity = 全 0,meanPathDelay 空。 排查:Master 和 Slave 分别开 -l 7 抓日志看收不收得到 Sync。
- G.8275.1 写 L2,不是 UDPv4
- G.8275.1 写 P2P,不是 E2E
- domain 一边 0 一边 24 → 永远收不到
🐞 3. 普通交换机不支持 TC/BC → 排队延迟把 offset 顶到几 μs~几十 μs
现象:meanPathDelay 每次波动很大(> ±200ns),offsetFromMaster 抖得收不住。 修:
- Demo 两机直连(最省事)
- 生产换写了”IEEE 1588 TC/BC”的交换机,开启 TC 模式;或者整链路 BC
🐞 4. priority1 搞反 GM 乱选
现象:gmIdentity 每隔 30s 换一个机器,每次换 GM offset 会跳一下(BMCA 重新算)。 修:
- GM 的 priority1 写 1 / 2 / 3,绝对不要 128
- 备用 GM priority1 写 10~20,priority2 区分
- 普通 OC slave:
slaveOnly=1(不参选,彻底避免意外当 GM)
🐞 5. BIOS C-state / ASPM / CPU 调频导致 TSC 漂移、PHC 跟着抖
现象:晚上没人用服务器进入 C6 省电,早上唤醒 offset 一跳一跳;或者网卡 RX 延迟一跳一跳 jitter 很大。 修:BIOS 按 §2.3 全关省电;内核加 processor.max_cstate=1 idle=poll intel_idle.max_cstate=0;服务器性能 governor。
🐞 6. phc2sys 没跑 → 程序读的 CLOCK_REALTIME 还是不准
ptp4l 只调 PHC(网卡自己的小钟)。Java / Python / Go / C clock_gettime(CLOCK_REALTIME) 读的全是系统时钟。必须再 phc2sys 把 PHC → 系统时钟! 排查:sudo phc_ctl /dev/ptp0 cmp,两台机 PHC 差 100ns,但 date +%s%N 差几毫秒 → phc2sys 没起或没进 s2。
🐞 7. 两步时钟网卡配置成 One-step → Follow_Up 里根本没有 preciseOriginTimestamp
现象:ptp4l 报 received sync without follow up 警告;offset 永远大。 修:ptp4l.conf 里 twoStepFlag 保持默认 0(自动探测),不要强置 1。
🐞 8. 路径非对称:A→B 延迟 450ns,B→A 延迟 470ns,PTP 取平均就会自带 10ns 偏
场景:光纤主从距离不同 / 光模块型号不一致 / 网卡 TX/RX PHY 延迟有偏差。 修:
- 尽量同型号网卡+同批次光模块+等长布线
- PHC 校准:用示波器/Time Interval Counter 测两台机 PPS 差值,加 phc2sys
-O <offset_ns>手动修 - 使用 G.8275.1 / SMPTE 2059-2 自带”非对称校准 TLV” 的专业交换机/网卡,自动补偿
附录 A:行业 Profile 快速选法
| 你的行业 | 选哪个 Profile | domain 默认 | 示例交换机 BC 配置方向 |
|---|---|---|---|
| 实验室/通用服务器高精度 | Default Profile | 0 | 普通交换机可不配(直连);配 TC 效果更好 |
| 5G / O-RAN / 移动传输 | G.8275.1 + SyncE 频率同步 | 24 | 华为 CE6865 / Nokia 7250 / Cisco NCS 540 开启 T-BC + P2P + telecom profile |
| 广电 SMPTE 2110 / IP 演播室 | SMPTE ST 2059-2 | 128 | Evertz / Imagine / Arista 7130P + SMPTE profile |
| 汽车自动驾驶 / 工业 TSN / 专业音视频 AVB | IEEE 802.1AS (gPTP) | 0 | 车载 TSN 交换机 / NXP S32G 时间网关;必须全链路 gPTP-aware |
附录 B:新部署 PTP 自检 7 条清单
-
ethtool -T eth0三项 HW 打戳 + /dev/ptpX 全有 - 两台直连或链路上 BC/TC 已开(P2P TC 链路 domain / delay_mechanism 全局一致)
- GM
ptp4l日志里assuming the grand master role+priority1 < 10 - Slave
pmc能读到phaseState=LOCKED (s2)连续稳定 5 分钟 - Slave
pmcoffsetFromMaster 稳定 ±250ns 内(100ns 以下为优秀) -
phc2sys已经 systemd enable + 运行;phc_ctl /dev/ptp0 cmp差 < ±500ns - 接入 Prometheus 监控告警:offset > 1μs / phaseState != 2 立即告警
延伸:如果你已经把 PTP 跑到 <100ns,接下来的”极致派”路径是:(1) 把 GPSDO OCXO 换成铷钟 Rb(月漂移 <1e-11,抗短时间 GNSS 遮挡);(2) 全链路 One-step Sync + 对称布线 + 非对称补偿 TLV;(3) 应用层直接读
CLOCK_TAI//dev/ptpXioctl(PTP_SYS_OFFSET_PRECISE)不要走系统时钟的任何中间层,这样金融交易/雷达采集就能跑进 ±20ns 级别。