上一篇画了地图:gPTP 是 TSN 的地基,全网必须对齐到同一个时间源——GM(Grandmaster,主时钟)。
问题来了:一个网络里可能有一堆带时钟的节点(域控、交换机、ECU),凭什么你说的算?GM 是怎么选出来的?选错了怎么办?主挂了谁接班?
答案就是 BMCA(Best Master Clock Algorithm,最佳主时钟算法)。这篇把它讲透。
一、先认识时钟家族里的三种角色
gPTP 网络里的设备,按时钟能力分三类:
| 角色 | 全称 | 干什么 | 车载典型设备 |
|---|---|---|---|
| OC | Ordinary Clock(普通时钟) | 单端口设备,要么当 GM,要么同步别人 | ECU、域控、摄像头 |
| BC | Boundary Clock(边界时钟) | 多端口交换机,每个端口独立参与 BMCA,向上游同步、向下游转发时间 | 车载交换机、中央网关 |
| TC | Transparent Clock(透明时钟) | 只测量并补偿报文在自己这里的驻留时间,不参与"选王" | 早期 1588 方案里常见 |
车载网络几乎只用 BC,不用 TC。 为什么?
TC 的玩法是"报文本机转发,我只在 correctionField 里记一笔自己花了多久"——听起来高效,但它对报文敏感:每转一跳,驻留时间的测量误差、报文的串行化延迟都会往 correctionField 里累加,级联几跳后噪声就上来了。
BC 则是"中转站"模式:交换机在自己的 GM 口同步上游时间,然后在其他端口以主时钟的身份重新发出 Sync。下游设备看到的是一个"新的、干净的"主时钟,驻留时间在 BC 内部就被吸收掉了,不往报文里累加误差。级联十跳,误差也不叠加。
这也是 802.1AS 相对 1588 的一个明确取向:大规模级联网络,BC 是标配。
二、BMCA 比什么:七个字段的"选王积分榜"
每个时钟节点都有一份"成绩单",学名叫 defaultDS(数据集),核心字段:
| 字段 | 含义 | 设计建议 |
|---|---|---|
| priority1 | 一级优先级,手工配置 | 想手动指定谁是 GM,改它最直接 |
| clockClass | 时钟等级,描述时钟源的"血统" | 6 = 同步到主参考(GPS 等),187 = 默认自由运行 |
| clockAccuracy | 声称的时间精度等级 | 按时钟源真实能力填,别虚报 |
| offsetScaledLogVariance | 时钟稳定度(方差的对数) | 由时钟硬件质量决定,一般自动 |
| priority2 | 二级优先级,手工配置 | 同一血统内区分主备 |
| clockIdentity | 时钟唯一标识,8 字节 EUI-64 | 最终 tiebreaker,比大小,纯看"运气" |
| stepsRemoved | 经过了多少个 BC | Announce 报文每过一跳 +1 |
BMCA 的规则很朴素:字段依次往下比,分出胜负就停。顺序是:
priority1 (越小越优先)
↓ 打平
clockClass (越小越优先,血统越正)
↓ 打平
clockAccuracy (越小越优先)
↓ 打平
offsetScaledLogVariance
↓ 打平
priority2 (越小越优先)
↓ 还打平
clockIdentity (纯比大小,谁小谁赢)
stepsRemoved 不参与"比优劣",它只在路径选择时起作用:当一个端口同时收到多个 Announce,选 stepsRemoved 最小的那条路径;一样大才回到上面的字段比较。
注意全是"越小越优先"。priority 填 0 是最想当主,填 255 是最不想。别搞反。
三、BMCA 怎么跑起来:Announce 报文的分级传播
光有成绩单不够,还得让所有节点都看见彼此的成绩。BMCA 靠 Announce 报文完成,整个过程分两级:
3.1 每个端口本地选"最优外来时钟"
设备每个端口都会收邻居发来的 Announce,在端口本地做个比较:收到的外来时钟 vs 自己心里记的"当前推荐时钟",用上面那套规则比。赢了的留下,输出不变。
3.2 设备级选 GM,定端口的角色
把所有端口选出的最优外来时钟,再和设备自己比一次:
- 自己赢 → 本设备就是 GM,宣告"我就是王"
- 外来时钟赢 → 本设备当从时钟,收到胜出 Announce 的那个端口变成 slave port(从端口),其他端口继续对外发 Announce——但发的不是原创,而是转发学到的"王"的成绩单(stepsRemoved +1)
于是整网形成一条 GM → 各级 BC → 末端 OC 的同步树,像这样:
┌─────┐
│ GM │ ← priority1=0,clockClass=6(GPS 参考)
└──┬──┘
│ Announce 传播
┌────┴────┐
│ BC-1 │ 域控内交换机,slave 口朝上,GM 口朝下
└──┬──┬──┘
│ │
┌──┴┐ ┌┴───┐
│OC │ │OC │ ← 摄像头 / 雷达 ECU,全部同步到 GM 时间
└───┘ └────┘
每个 OC 眼里只有一个"王"——GM。它不知道中间过了几个 BC,那些是 BC 该操心的事。
3.3 GM 挂了怎么办
GM 失联后,slave 口的 Announce 会超时(gPTP 里 Announce 接收超时典型 3 个间隔)。超时后该端口立刻重新参与本地比较——次优的那个时钟自动上位。
假设 GM 平时 priority1=0、备用域控 priority1=8:平时域控安安分分当 BC,GM 一挂,域控的成绩单秒杀全场,自动继位。不需要人工干预,不需要上层协议栈介入,这就是 BMCA 设计的漂亮之处。
故障恢复还有个细节:旧 GM 恢复后不会立刻抢回王位,要看 priority——如果它 priority1=0 还是最高,那全网会重新收敛到它。所以备时钟的 priority 一定要设得比主高,否则抢主权打得不可开交。
四、一个实例:把七个字段过一遍
假设域控里两个候选:
| 字段 | 域控 A | 域控 B |
|---|---|---|
| priority1 | 128 | 128 |
| clockClass | 6 | 6 |
| clockAccuracy | 0x20 | 0x20 |
| offsetScaledLogVariance | 0x4E5D | 0x4361 |
| priority2 | 128 | 128 |
比到 offsetScaledLogVariance:0x4361 < 0x4E5D,B 赢,B 当 GM。
假设 B 挂了一段时间又回来,而 A 已经当了两年"王"——没关系,BMCA 是状态机不是历史问题。B 的 Announce 一到,A 发现 B 的成绩单更好,乖乖交出王位,全网重新收敛。收敛时间取决于 Announce 间隔,几百毫秒量级。
再假设两个域控成绩单一模一样,连 clockIdentity 都是同厂同型号撞了——比 clockIdentity 大小,小赢。纯运气,但保证了全网的确定性:不会出现"一半设备跟 A、一半跟 B"的分裂局面。
工程教训:clockIdentity 默认常由 MAC 地址派生。同型号板子的 MAC 段址相近,比大小结果基本随机。想控制谁当 GM,老老实实配 priority,别赌 clockIdentity。
五、工程配置的三条经验
- GM 必须挂在最好的时钟源上。GM 是全网的"发源地",它自己歪了,后面全歪。域控有 GPS/GNSS 参考的,priority1 设 0;没有外部参考的节点,clockClass 老老实实填 187(自由运行),别虚报 6——虚报血统,GM 挂了它接班,全网时间开始漂,查起来要人命
- priority 分层设计:主 GM(GPS)→ 备 GM(域控本地时钟)→ 交换机(不当王)→ 末端 ECU。一个清晰的主备链,故障场景行为可预测
- 时钟源要监控:BMCA 只管选,不管"选出来的王健康不健康"。建议在域控里加看护:slave 口 offset 长期超出阈值就告警,clockClass 异常跳变就告警——BMCA 不会告诉你"王中风了",它只会忠实地跟着王一起歪
六、结语
BMCA 的本质就一句话:一套确定性的优先级规则 + Announce 报文的分级传播,让全网在谁都可以当主的环境里,稳定收敛到唯一的最优时钟。
它没有投票、没有协商、没有"我觉得",全是查表比大小——所以它快(收敛以 Announce 间隔为单位)、且行为完全可预测。对车载这种"宁可确定性宕,不要概率性飘"的场景,这就是正确的气质。
下一篇讲 gPTP 的对时数学:Sync、Follow_Up、Delay_Req、Delay_Resp 四个报文怎么把 offset 和链路延迟算出来。那是 gPTP 最"硬"也最值得琢磨的部分。
你们网络里的主备时钟是怎么配的?有没有遇到过"王位"抢来抢去的问题?评论区见。