引言
Frame Preemption(帧抢占)机制,主要用于 TSN(Time-Sensitive Networking):让高优先级帧不必等一个正在发送的低优先级大帧发完。
为什么需要 Qbu?
普通以太网一旦开始发送一个帧,通常不能中途停下来。
假设端口是 100 Mbps,正在发送一个 1500B 的低优先级帧,此时一个高优先级控制报文到达:
时间 --------------------------------->
低优先级:
|========== 1500B Frame ==========|
高优先级:
↑ 到达
|--------------------------> 必须等待1500B 在 100 Mbps 链路上的序列化时间大约:
1500 × 8 / 100 Mbps ≈ 120 μs再加上,帧前导码(Preamble),帧间隙(InterFrameGap,IFG), 实际阻塞时间还会略高。
对于工业控制、车载网络等 TSN 场景,100 多 μs 的额外延迟可能很重要。
802.1Qbu 的核心思想就是:允许高优先级帧“打断”低优先级帧。
铺垫
帧前导码
理解 Ethernet 的前导码(Preamble),对后面理解 802.1Qbu/802.3br 的 SMD 和分片很重要。
标准以太网帧在链路上传输时,MAC 帧前面实际上还有 Preamble + SFD:
┌──────────────┬─────┬─────────────┬─────────┬──────┐
│ Preamble 7B │ SFD │ Ethernet │ Payload │ FCS │
│ │ 1B │ Header 14B │ │ 4B │
└──────────────┴─────┴─────────────┴─────────┴──────┘通常说的 Ethernet Frame 是从 Destination MAC 开始的,因此 Preamble 和 SFD 一般不算 Ethernet MAC Frame 本身的一部分。
Preamble 有 7 字节,经典表示是:
55 55 55 55 55 55 55二进制看起来就是不断交替的 0 和 1:
01010101 01010101 01010101 ...它的主要作用是让接收端知道: 有帧要来了 。早期 Ethernet 接收电路需要利用这一串规律变化的信号进行同步。现代 PHY 实现细节更复杂,但协议层面仍保留了这个结构。
Preamble 后面紧跟一个 SFD(Start Frame Delimiter,帧起始定界符)。 SFD 的意义可以理解为: “同步阶段结束,真正的 MAC 帧马上开始。” 经典表示: D5
所以经常看到:
55 55 55 55 55 55 55 D5
└──── Preamble ─────┘ ↑
SFD帧间隙
了解 Qbu,不需要了解帧间隙。只是它和帧前导比较”近”,所以这里顺带介绍下。现实世界可能复杂的多,我也不知道。
帧间隙(Inter-Packet Gap / Interframe Gap,IPG/IFG)是以太网上两个连续发送的帧之间必须保留的一段空闲时间。
它和刚才讲的 Preamble/SFD 连起来看:
时间方向 ─────────────────────────────────────────────→
Frame 1 Frame 2
┌────────┬─────┬───────────┬─────┐ ┌────────┬─────┬──────
│Preamble│ SFD │ MAC Frame │ FCS │ │Preamble│ SFD │ ...
└────────┴─────┴───────────┴─────┘ └────────┴─────┴──────
│ │
└────┘
↑
IPG也就是说,一个帧发送完 FCS 后,发送端不能马上发送下一个 Preamble,中间需要留出 IPG。
经典 Ethernet 的标称帧间隙是 96 bit times,也就是发送 12 个字节的时间。 最初的 Ethernet 设计里,这段时间给接收端提供从上一帧恢复、准备接收下一帧的机会。从现在理解协议的角度,更重要的是把它看成 Ethernet 链路的固定开销之一。
人也一样。连续的工作间,需要插入 Gap。Gap 是生活的一部分。
IPv4 分片
从分片角度来说, 802.1Qbu 是在二层分片, IP Fragmentation 是在三层分片。虽然分片的目的不同,但是它们还有有些相似之处的。所以,本节也简单介绍下 IPv4。
网络分层处理。底层的每一种物理网络,能够承载的数据单元大小却不一样。三层不关心底层情况,只需要知道最多能承载多大的 IP 包就行。MTU 来描述这种能力边界。
一个 IP 数据包太大,超过 MTU 时,需要把它拆成多个较小的 IP 包传输,到目的端再重新拼起来。
目的端重组的时候,怎么知道这些分片来自同一个 IP 数据包呢?(后面我们看 Qbu 分片的时候,要始终考虑这个问题)
IPv4 Header 中有几个关键字段:
- Identification用于关联分片。同一个原始 IP 包拆出来的分片通常具有相同的 Identification。
- MF(More Fragments)表示后面还有没有分片
- Fragment Offset(片偏移)表示当前分片的数据原本位于原始 IP 数据的什么位置。一个容易踩坑的地方是:它的单位不是 1 字节,而是 8 字节。
- DF Flags = Don’t Fragment(禁止分片)
所以判断规则非常简单:
| MF | Fragment Offset | 含义 |
|---|---|---|
| 0 | 0 | 没有分片 |
| 1 | 0 | 第一个分片 |
| 1 | > 0 | 中间分片 |
| 0 | > 0 | 最后一个分片 |
只要 MF = 1 或者 Fragment Offset != 0,这个包就是一个分片。
IPv6 的分片规则和 IPv4 又不同了,这里不再赘述。
Qbu 介绍
802.1Qbu 主要从桥接/TSN 的角度定义 Frame Preemption,例如哪些 Traffic Class 可以配置成 Express、哪些可以配置成 Preemptable。
真正涉及 MAC 层如何把帧暂停、形成 fragment、发送以及重新组合的机制,则和 IEEE 802.3br 密切相关。
下面两节,分别介绍这两部分。
Express(不可被抢占) 和 Preemptable(可被抢占) 队列
我们前面提到,高优先级的帧,可以打断低优先级的帧发送。
内部实现中,将高优先级的帧放入 Express 队列,将低优先级的帧放入 Preemptable 队列。Express 的包,可以抢占 Preemtable 包发送。
帧的优先级
首先,怎么判断一个帧的优先级?
一个帧的优先级,通常不是根据帧的内容、长度来判断,而是看二层帧中有没有携带 QoS 优先级字段。以最常见的以太网来说,核心就是 IEEE 802.1Q VLAN Tag 里的 PCP 字段。
| Dst MAC | Src MAC | 802.1Q Tag | EtherType | Payload | FCS |
│
▼
┌──────────────────┐
│ PCP │ DEI │ VID │
│ 3bit│ 1bit│12bit │
└──────────────────┘其中 PCP(Priority Code Point) 就是帧的二层优先级标记。因为 PCP 有 3 bit,所以范围是:
PCP = 0 ~ 7一般来说数值越高,代表的业务优先级越高,但有一个重要细节:PCP 值并不简单等于交换机实际转发优先级。交换机通常会把 PCP 映射到内部 Traffic Class / Queue,再根据队列调度策略决定谁先发。
例如可能配置成:
PCP 0 ──→ Queue 1
PCP 1 ──→ Queue 0
PCP 2 ──→ Queue 0
PCP 3 ──→ Queue 2
PCP 4 ──→ Queue 3
PCP 5 ──→ Queue 4
PCP 6 ──→ Queue 5
PCP 7 ──→ Queue 5所以更准确地说:PCP 是帧携带的“优先级标记”;真正决定帧何时发送的是设备的 QoS 分类、队列映射和调度机制。
除了在二层,标记包的优先级,我们还可以使用 IP 的 DSCP 标记优先级。
Express 和 Preemptable 队列
支持 Qbu 的设备中,网卡队列分为两类,Express 队列和 preemptable 队列。Express 队列包到了时,可以打断 preemptable 队列中的包发送。
收到 Ethernet Frame
│
▼
分类 / 优先级识别
│
例如读取 VLAN PCP
│
▼
映射到 Traffic Class
│
▼
TX Queue
│
┌────────┴────────┐
▼ ▼
Express Preemptable
│ │
└──────┬──────────┘
▼
MAC Merge Sublayer
│
▼
PHYMAC Merge Sublayer(MAC Merge 子层)可以理解为 802.3br / 802.1Qbu 帧抢占机制中的“交通调度 + 分片/重组层”。它位于 MAC 与 PHY 之间附近的逻辑位置,核心任务是把两路流量——Express 和 Preemptable 合并到同一条物理链路上发送。
Preemptable Frame(可抢占帧) 和 fragment(分段)
Preemptable Frame 与普通帧的区别
Preemptable queue 中发出的包,经过 MAC Merge Sublayer 是 Preemptable Frame。
Preemptable Frame 可以转换成一个或多个 mPacket / fragment。
Preemptable Frame 和 普通的 Frame 是不同的,即使它从头到尾都没有被抢占。但是,因为 Preemptable Frame 不知道什么时候被抢占,所以它必须和普通的 Frame 不同。否则,被抢占前,已经发出去的内容,就没有办法区分是一个帧损坏了,还是要给帧的分段。它和 IP 分片的不同是,Preemptable Frame 是随时可能被抢占而分片,但是 IP 分片是发送前就根据 MTU 决定是否要分片了。
和普通帧最核心的区别,在于开头的 SMD(Start mPacket Delimiter)。
可以理解为:
普通 / Express Frame:
Preamble SFD MAC Frame
│ │
▼ ▼
┌─────────┬─────────┬──────────────────────────────┐
│ 7 Byte │ 1 Byte │ DA | SA | ... | Payload|FCS │
└─────────┴─────────┴──────────────────────────────┘而一个没有被抢占的 Preemptable Frame,经过 MAC Merge 后:
Preemptable Frame(未发生抢占):
Preamble SMD-S MAC Frame
│ │
▼ ▼
┌─────────┬─────────┬──────────────────────────────┐
│ 6 Byte │ 2 Byte │ DA | SA | ... | Payload|FCS │
└─────────┴─────────┴──────────────────────────────┘至于 SMD-S 这个字节的具体内容是什么,见下一节。
SMD-S 和 SMD-C
SMD = Start mPacket Delimiter
其中有两大类:
| 类型 | 含义 | 用途 |
|---|---|---|
| SMD-S | Start mPacket Delimiter - Start | 表示一个新的 Preemptable Frame 的第一个 mPacket 开始 |
| SMD-C | Start mPacket Delimiter - Continue | 表示之前被抢占的 Preemptable Frame 的后续 mPacket 开始 |
假设一个 Preemptable Frame 被抢占 两次:
原始 Preemptable Frame A:
┌─────────────────────────────────────────────────────┐
│ Frame A │
└─────────────────────────────────────────────────────┘MAC Merge 后可能变成:
时间 ───────────────────────────────────────────────→
SMD-S
↓
┌──────┬──────────────┐
│SMD-S │ Fragment #1 │
└──────┴──────────────┘
│
│ 被抢占
▼
┌───────────────┐
│ Express Frame │
└───────────────┘
SMD-C
↓
┌──────┬──────────────┐
│SMD-C │ Fragment #2 │
└──────┴──────────────┘
│
│ 再次被抢占
▼
┌───────────────┐
│ Express Frame │
└───────────────┘
SMD-C
↓
┌──────┬──────────────┐
│SMD-C │ Fragment #3 │
└──────┴──────────────┘和 IPv4 分片一样,我们需要考虑这几个问题:1) Preemptable Frame 有没有分片;2) 分片的话,该分片在原始报文的什么位置; 3) 考虑不同分片是不是同一个帧的,不同帧的分片不能重组在一起;
这些问题,都是通过 SMD-S 和 SMD-C 编码的值决定的。
SMD-S 和 SMD-C 用了 八位帧前导的最后两位。
核心规律是:报文编号 S0→S1→S2→S3;同一报文的 continuation 使用对应的 C0→C1→C2→C3;F 始终按 F0→F1→F2→F3→F0 循环。
第一个报文:S0 / C0
| SMD-S0 | SMD-C0 + F0 | SMD-C0 + F1 | SMD-C0 + F2 | SMD-C0 + F3 | SMD-C0 + F0 | |
|---|---|---|---|---|---|---|
| Byte7 | 0x55 | 0x61 | 0x61 | 0x61 | 0x61 | 0x61 |
| Byte8 | 0xE6 | 0xE6 | 0x4C | 0x7F | 0xB3 | 0xE6 |
第二个报文:S1 / C1
| SMD-S1 | SMD-C1 + F0 | SMD-C1 + F1 | SMD-C1 + F2 | SMD-C1 + F3 | SMD-C1 + F0 | |
|---|---|---|---|---|---|---|
| Byte7 | 0x55 | 0x52 | 0x52 | 0x52 | 0x52 | 0x52 |
| Byte8 | 0x4C | 0xE6 | 0x4C | 0x7F | 0xB3 | 0xE6 |
第三个报文:S2 / C2
| SMD-S2 | SMD-C2 + F0 | SMD-C2 + F1 | SMD-C2 + F2 | SMD-C2 + F3 | SMD-C2 + F0 | |
|---|---|---|---|---|---|---|
| Byte7 | 0x55 | 0x9E | 0x9E | 0x9E | 0x9E | 0x9E |
| Byte8 | 0x7F | 0xE6 | 0x4C | 0x7F | 0xB3 | 0xE6 |
第四个报文:S3 / C3
| SMD-S3 | SMD-C3 + F0 | SMD-C3 + F1 | SMD-C3 + F2 | SMD-C3 + F3 | SMD-C3 + F0 | |
|---|---|---|---|---|---|---|
| Byte7 | 0x55 | 0x2A | 0x2A | 0x2A | 0x2A | 0x2A |
| Byte8 | 0xB3 | 0xE6 | 0x4C | 0x7F | 0xB3 | 0xE6 |
比如:SMD-C2 + F1 —— 第三个报文的 continuation,并且 Fragment Count = 1。
SMD-S0、S1、S2、S3 的选择由一个 modulo-4 的 frame_count 决定。 IEEE 802.3br 明确说明,Preemptable frame 的 SMD 携带 frame count,而 frame count 是模 4 计数。所以,我们可以看到 S 和 F 都是以 4 为周期进行重复的。
分片帧的校验和
接收端要检查这一段传输有没有出错,所以每个 fragment 都需要检验和。(我怎么感觉屁事多呢,直接重组完,用最后的 FCS 检查就好了,校验失败,drop 就是了)
MAC Merge 给非最后 fragment加一个 mCRC:
First Fragment:
Preamble | SMD-S | DA | SA | Type | A | B | mCRC
4BExpress Frame 发完之后:
Continuation / Final Fragment:
Preamble | SMD-C | Frag Count | C | D | E | FCS
↑
原始帧 FCS之前,我了解了下 Ethernet-FCS 的校验和。
但是,mCRC 怎么计算的,不想查了,费劲。