第四章 最佳主时钟算法(BMCA)

说实话,我刚开始搞时钟同步那会儿,觉得 BMCA 挺神秘的。后来自己调了几个项目,发现说白了它就是一套“选老大”的规则——所有节点坐下来比比谁更靠谱,然后大家跟着它走。本章我把原理、选举流程、状态机拆开讲,你跟着走一遍,基本就能上手了。

4.1 为什么要选主时钟?

IEEE 1588 的域模型里,不可能每个设备都当“主”。你想想看,如果每个节点都发同步报文,网络就乱套了。所以必须有一个 最佳主时钟(Grandmaster, GM) 来统领全局。BMCA 就是干这个的——它运行在每个端口上,通过比较自身和邻居的 数据集,决定谁当主、谁当从。

我的经验: 有一次在工厂调试,某个交换机的优先级设得太低,结果一个普通设备当了 GM,整个同步精度掉了好几个微秒。后来我养成习惯——关键链路节点手动指定高优先级,避免 BMCA 选错。

4.2 选举过程:比什么?怎么比?

BMCA 的选举说白了就三步:收集 → 比较 → 决策。每个端口周期性发送 Announce 报文,里面带着源端口的数据集。收到 Announce 后,本端口把自己和对方的数据逐项比较。比较顺序如下:

  1. 优先级 1(priority1):数值越小优先级越高。管理员可以手动配,范围 0–255。
  2. 时钟 Class(clockClass):表示时钟的质量等级。比如原子钟 Class 低(好),普通晶振 Class 高(差)。
  3. 时钟 Accuracy(clockAccuracy):偏差指标,越小越准。
  4. 时钟 Variance(offsetScaledLogVariance):抖动指标,越小越好。
  5. 端口号(portIdentity.portNumber):如果前面全部一样,端口号小的胜出。

比较是逐级进行的——只要某一项分出高低,后面就不比了。听起来是不是跟体育比赛积分榜有点像?

比较项 说明 推荐设置
priority1 用户可配,0 最高 GM 设为 128,普通设备 255
clockClass 6–248,越小质量越好 原子钟 6,TCXO 52,OCXO 45
clockAccuracy 0x00–0xFF,0xFE 表示未知 GPS 授时 0x21,网口同步 0xFE
offsetScaledLogVariance 数值越小越稳定 典型值 0x4E5D(约 0.1ns)
portIdentity.portNumber 设备内端口号,唯一 硬件自动分配
核心口诀: “先看 priority1,再看 Class,然后 Accuracy,接着 Variance,最后拼端口”——我们团队内部都这么记。

4.3 BMCA 状态机:节点到底在干嘛?

BMCA 给每个端口定义了若干状态。我记得第一次看状态机图时一脸懵,后来发现只要理解三个核心状态就够了:

  • MASTER:我是老大,我来发同步报文。
  • SLAVE:我跟你同步,我收报文、做跟随、调本地时钟。
  • PASSIVE:我不当主也不当从,但监听 Announce,随时准备切换。

除此之外还有 LISTENING(初始/重启后等待)、PRE_MASTER(刚当选主但还没开始发报文,先做个自我检查)。完整状态迁移如下图:

BMCA 状态机简图 BMCA 端口状态机 LISTENING PRE_MASTER MASTER SLAVE PASSIVE 被选为 GM 检测到更优主时钟 热备配置 自检通过 遭遇更优主时钟 同步超时 说明: 实线箭头表示正常状态迁移; 虚线箭头表示异常/配置触发迁移。 PASSIVE 通常用于备端或边界时钟。

4.4 状态迁移的典型场景

光看状态图你可能觉得抽象,我举几个实际工作中的例子:

  • 上电启动:所有端口进入 LISTENING,等待 2–3 个 Announce 周期(默认 2–3 秒)。如果没收到任何 Announce,该端口自己就成为 MASTER——这就是为什么普通交换机上电后也会发 Sync。
  • 主时钟掉线:SLAVE 端口连续收不到 Sync,超时后回到 LISTENING,重新参与选举。这时如果其他设备更优,可能选出一个新 GM。
  • 人工干预:我曾经在一个测试中把 GM 的 priority1 改成 1(比原来低),结果其他节点立刻检测到,整个网络 2 秒内完成主时钟切换——BMCA 没有你想象的那么慢。
注意: 不要频繁调整 priority1 或硬重启 GM 节点。我曾经见过有人批量修改配置后,设备同时进入 LISTENING 和 PRE_MASTER 震荡,花了 30 秒才收敛。如果你需要热切换,建议通过 Announce 间隔 和 priority2 来做软切换。

4.5 BMCA 的“避坑”指南

我结合自己踩过的坑,列几个要点:

  1. Announce 超时设置别太短。 默认 3 倍 Announce 周期(比如 2s × 3 = 6s)。太短会导致误判,太长切换延迟大。建议对网络稳定的场景保留默认,无线或卫星跳变场景可以适当延长。
  2. Priority 不要都设成 128。 所有设备 priority1=128 时,BMCA 会继续比拼 clockClass。如果 clockClass 也一样,就拼 Accuracy……最后可能因为端口号不同选出个意料之外的 GM。建议至少把“预期 GM”的 priority1 设成 127 或更低。
  3. 边界时钟和透明时钟的 BMCA 行为不同。 普通端口的 BMCA 是全功能;但透明时钟(TC)默认不参与选举,只转发 Announce。我们组有一次差点把 TC 的端口也配成 BC,导致 BMCA 选出了两个 GM。记住:TC 不能主动当 GM。
总结: BMCA 不是什么玄学。你只要抓住“逐级比较 → 状态机 → 收敛时间”这个链路,就能看懂任何日志。我习惯在调试时先用 ethtool -P 确认端口,再用 ptpd --show-dataset 看 Announce 内容——直接对比就能判断 BMCA 决策是否合理。