ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

IEEE 802.1Qav信用整形器CBS原理与交换机调参实战

IEEE 802.1Qav信用整形器CBS原理与交换机调参实战 简介IEEE 802.1Qav-2009 是时间敏感网络TSN协议族中的关键标准文档由 IEEE 局域网/城域网标准委员会制定作为 802.1Q-2005 的第 12 号修正案专门定义 VLAN 桥接设备在转发与队列方面的增强功能以支持时间敏感数据流的可靠传输。它面向工业自动化、音视频流媒体、汽车通信等对实时性与低延迟要求极高的领域适合网络协议研究者、嵌入式与车载网络工程师及 TSN 学习者深入研读。资源包为单一 PDF 文件约 743KB完整收录标准正文、摘要与关键词涵盖公平队列、调度算法、带宽预留、流量整形、精确时钟同步、优先级级联及快速重传恢复等核心机制。目前已有 1023 人学习下载可作为理解 TSN 队列增强原理、对照协议条款与开展科研引用的权威一手资料。1. 从一次交换机调参翻车说起IEEE 802.1Qav-2009.pdf 到底能解决什么去年帮一个做车载以太网的朋友排查音视频卡顿现象很邪门摄像头到域控的链路带宽明明只用了不到 40%但音频偶尔就是会断一下抓包看是几个大的视频帧把队列堵死了。当时第一反应是加带宽结果没用后来翻到 IEEE 802.1Qav-2009.pdf 这份标准才意识到问题不在带宽在转发调度。Qav 定义的是时间敏感流的转发与排队增强Forwarding and Queuing Enhancements for Time-Sensitive Streams核心是给交换机出口队列加一套基于信用的整形器Credit-Based ShaperCBS让音视频这类流不会因为突发帧把低延迟流挤掉。这份 PDF 就是 2009 年发布的原始标准文本属于 TSN 协议族里最早落地、也最容易被忽略的一环——大家张口闭口 802.1Qbv、Qci但真正在量产交换机里跑起来的往往还是 Qav 这套信用整形。它适合谁做车载以太网、工业实时以太网、音视频桥接AVB的固件和驱动工程师以及需要读懂交换机队列行为、给 QoS 配置找理论依据的测试人员。你不需要从头啃完 200 多页但至少得知道 SR class A/B 的信用怎么算、idleSlope 怎么配否则调参就是玄学。2. 信用整形器 CBS 的工作机制从 idleSlope 到队列门控2.1 为什么不是简单的优先级队列很多人第一次接触 Qav会把它和 802.1p 的优先级队列混为一谈。优先级队列只解决“谁先出”不解决“出多少”。一个高优先级队列如果持续有帧进来低优先级队列会被饿死反过来如果高优先级队列突然来一大串突发帧它会把出口带宽瞬间吃满导致其他队列的延迟抖动。Qav 要解决的是后者给每个 SR class 一个信用额度只有信用为正时才能发送发送过程中信用按 sendSlope 下降空闲时按 idleSlope 上升。信用为负时队列被门控关闭必须等信用恢复到 0 以上才能继续发。这样即使高优先级流有突发它也会被强制“还债”给其他流留出确定性的窗口。标准里定义了 SR class A 和 SR class B 两个类别A 的优先级更高、idleSlope 更大通常用于音频和控制B 用于视频。关键参数就三个idleSlope空闲时信用增长速率单位 bits/s、sendSlope发送时信用下降速率等于 idleSlope 减去端口速率、队列最大信用值hiCredit和最小信用值loCredit。hiCredit 决定了突发能有多大loCredit 决定了欠债能欠多少。这些值不是拍脑袋定的标准附录里有计算公式但实际配置时更多依赖流预留协议SRP或静态配置。2.2 信用更新的离散事件模型标准里把信用更新描述成离散事件每当队列从空变非空、从非空变空、或者端口开始/停止发送都会触发一次信用计算。公式看着简单但实现时有个坑信用是连续变化的而交换机芯片通常按固定周期采样。如果采样周期太大信用计算就会失真导致实际整形效果和理论值偏差很大。常见做法是用硬件定时器在每个字节发送完成时更新信用或者用令牌桶近似。下面这段伪代码展示了信用更新的核心逻辑我用 Python 写出来方便理解实际固件里通常是 C 或 Verilog。# 信用整形器核心状态机简化版 # 参数说明 # idle_slope: 空闲时信用增长速率单位 bits/s由 SRP 或静态配置给出 # port_rate: 端口物理速率单位 bits/s比如 100M 就是 100_000_000 # hi_credit: 信用上限单位 bits通常等于 idle_slope * max_frame_size / port_rate # lo_credit: 信用下限单位 bits通常为负的 max_frame_size class CreditShaper: def __init__(self, idle_slope, port_rate, hi_credit, lo_credit): self.idle_slope idle_slope self.send_slope idle_slope - port_rate # 发送时信用下降速率 self.hi_credit hi_credit self.lo_credit lo_credit self.credit 0.0 # 当前信用值单位 bits self.last_update 0.0 # 上次更新时间戳单位秒 self.queue_non_empty False def update_credit(self, now): 根据空闲/发送状态更新信用now 为当前时间戳 elapsed now - self.last_update if self.queue_non_empty: # 队列非空且正在发送信用按 send_slope 下降 self.credit self.send_slope * elapsed else: # 队列空闲信用按 idle_slope 上升 self.credit self.idle_slope * elapsed # 信用钳位不能超过 hi_credit 也不能低于 lo_credit if self.credit self.hi_credit: self.credit self.hi_credit elif self.credit self.lo_credit: self.credit self.lo_credit self.last_update now def can_transmit(self): 信用大于等于 0 才允许发送 return self.credit 0这段代码里最容易被忽略的是send_slope的计算。标准里 sendSlope idleSlope - portRate因为发送时信用既要被 idleSlope 补充又要被端口速率消耗净效果是下降。如果 idleSlope 配得接近 portRatesendSlope 就接近 0信用几乎不降整形就失效了。实际配置时 idleSlope 一般不超过端口速率的 75%留出余量给其他队列。2.3 队列门控与传输选择信用整形器只是决定“能不能发”真正“发哪个”还要靠传输选择算法。Qav 规定的是严格优先级加信用门控高优先级 SR class 先看信用信用够就发信用不够就跳过看下一个 class。非 SR 流比如 best effort只有在所有 SR class 都不可发时才能占用带宽。这里有个细节标准允许实现把多个 SR class 映射到同一个队列但大多数交换机芯片是每个 class 独立队列。配置时要注意队列和 class 的映射关系别把 class A 和 class B 塞进同一个队列否则信用计算会互相干扰。提示如果你在交换机上看到credit相关寄存器先确认它对应的是哪个 SR class再看 idleSlope 的单位是 bits/s 还是 bytes/s单位搞错会导致整形完全失效。3. 把标准落到配置静态参数计算与交换机实操3.1 从流规格反推 idleSlope假设你要传一路 48kHz 采样的音频每包 6 个采样点包间隔 125 微秒包大小 100 字节含开销。这路流的平均速率是 100 字节 / 125 微秒 800,000 字节/秒 6.4 Mbits/s。如果端口是 100 Mbits/s那么 idleSlope 至少要给到 6.4 Mbits/s 才能保证这路流不被饿死。但实际配置不能只给 6.4因为还要考虑突发和多个流叠加。常见做法是 idleSlope 流平均速率 × 1.2 到 1.5 的余量同时不超过端口速率的 75%。对于 class A如果有多路音频要把所有 class A 流的平均速率加起来再乘余量。hiCredit 的计算更讲究。标准里 hiCredit 决定了单次突发的最大信用积累通常取 maxFrameSize × idleSlope / portRate。假设 maxFrameSize 是 1500 字节idleSlope 是 10 Mbits/sportRate 是 100 Mbits/s那么 hiCredit 1500 × 8 × 10 / 100 1200 bits。这个值意味着队列空闲时最多攒 1200 bits 的信用对应 150 字节的突发。如果 hiCredit 设得太大突发会变大延迟抖动增加设得太小信用不够发完整帧会导致帧被无限推迟。loCredit 一般取负的 maxFrameSize允许欠一帧的债。3.2 在 Linux 上用 tc 模拟 Qav 整形如果你手头没有支持 Qav 的硬件交换机可以用 Linux 的tc加cbs队列规则模拟。内核从 4.15 开始支持cbsqdisc参数就是 idleSlope 和 sendSlope。下面这条命令在 eth0 上创建一个 CBS 队列idleSlope 设为 10 Mbits/ssendSlope 设为 -90 Mbits/s注意 sendSlope 是负值因为信用下降。# 创建 CBS 队列规则模拟 802.1Qav 信用整形 # idleSlope: 空闲时信用增长速率单位 bits/s # sendSlope: 发送时信用下降速率单位 bits/s通常为负值 # hiCredit: 信用上限单位 bits # loCredit: 信用下限单位 bits tc qdisc add dev eth0 parent 1:1 handle 10: cbs \ idleSlope 10000000 \ sendSlope -90000000 \ hiCredit 1200 \ loCredit -12000 # 查看队列统计确认信用和丢包情况 tc -s qdisc show dev eth0执行后可以用tc -s看统计重点看credit和dropped。如果 dropped 持续增长说明 hiCredit 太小或者 idleSlope 不够帧在队列里等不到信用。如果 credit 长期为负说明 sendSlope 绝对值太大信用恢复太慢。实际硬件交换机上这些参数通常通过 SRP 协议动态协商或者通过 CLI 静态配置比如set port 1 qav idle-slope 10000000之类的命令具体看厂商文档。3.3 用 iperf 和 ping 验证整形效果配置完不能只看寄存器得用流量验证。我一般会同时跑三路流一路高优先级 UDP 模拟音频一路低优先级 TCP 模拟背景流量再用 ping 测延迟抖动。如果 Qav 生效高优先级流的延迟应该稳定在百微秒级低优先级流会被压制但不会完全断。下面这个脚本用iperf3和ping做对比测试。# 启动 iperf3 服务端在另一台机器上 iperf3 -s # 在配置了 CBS 的机器上跑高优先级 UDP 流模拟音频 # -u 表示 UDP-b 指定带宽-t 指定持续时间 iperf3 -c 192.168.1.100 -u -b 8M -t 30 -l 100 # 同时跑低优先级 TCP 流模拟背景流量 iperf3 -c 192.168.1.100 -t 30 # 用 ping 测延迟抖动-i 0.01 表示每 10ms 发一个包 ping -i 0.01 -c 3000 192.168.1.100 | tail -n 3如果 Qav 配置正确ping 的 mdev抖动应该比没配 Qav 时小很多UDP 流的丢包率也应该接近 0。如果 UDP 丢包严重先检查 hiCredit 是否够大再检查队列映射是否正确。常见错误是把所有流都映射到同一个队列导致信用被背景流量消耗光。4. 避坑与排查Qav 配置里最容易翻车的五个点4.1 现象音频偶尔断一下抓包看没有丢包原因信用整形器把帧推迟了但没丢所以抓包看不到丢包只看到延迟抖动。这种情况通常是 hiCredit 太小突发帧攒不够信用只能等下一轮。解决把 hiCredit 调大或者把 maxFrameSize 对应的信用值算进去。标准里 hiCredit 至少能容纳一个最大帧的信用否则大帧永远发不出去。4.2 现象配置了 idleSlope 但整形完全没效果原因idleSlope 单位搞错了。有些交换机 CLI 用 bytes/s有些用 bits/s差 8 倍。如果按 bits/s 配了 bytes/s 的值idleSlope 会小 8 倍信用恢复极慢队列几乎一直关闭。解决先用show命令确认单位再按流速率反推。另一个可能是 sendSlope 没配默认是 0信用只增不减整形失效。4.3 现象低优先级流被完全饿死原因SR class 的 idleSlope 总和超过了端口速率信用整形器一直在发 SR 流非 SR 流拿不到任何带宽。标准里要求所有 SR class 的 idleSlope 之和不超过端口速率的 75%留 25% 给非 SR 流。解决重新计算各路流的平均速率把 idleSlope 总和压到 75% 以下。如果流太多考虑用 Qbv 做时间片而不是全靠 Qav。4.4 现象交换机重启后 Qav 配置丢失原因静态配置没保存或者 SRP 协商失败。很多交换机默认开启 SRP如果网络里没有 SRP 对端协商会超时然后回退到默认配置。解决要么关掉 SRP 用静态配置要么确保 SRP 域里有正确的宣告。静态配置记得write memory或copy running-config startup-config。4.5 现象不同厂商交换机互联时 Qav 行为不一致原因标准只规定了行为没规定实现细节。比如信用更新周期、队列映射方式、hiCredit 默认值各厂商都不一样。A 厂商的 class A 可能映射到队列 3B 厂商映射到队列 5互联时优先级就乱了。解决互联前先对齐队列映射和参数最好用同一厂商设备。如果必须混用用抓包和延迟测试验证实际行为别只看配置。5. 进阶用 Qav 做确定性延迟的边界与验证方法Qav 能提供有界延迟但不是零延迟。它的确定性来自信用整形器的“还债”机制即使高优先级流突发也会被强制在后续空闲期补偿从而给其他流留出窗口。但这个窗口的大小取决于 idleSlope 和 hiCredit 的比值。我一般用下面这个公式估算最坏情况延迟最坏延迟 ≈ (maxFrameSize × 8) / idleSlope 队列深度 × 帧传输时间。如果算出来超过业务容忍值就得调 idleSlope 或者减少队列深度。验证方法上除了前面说的 iperf 和 ping更严谨的做法是用支持硬件时间戳的网卡抓包看每个帧的入口和出口时间差。Linux 上可以用tcpdump加-j adapter_unsynced或者用SO_TIMESTAMPING套接字。下面这个 Python 脚本用scapy发带时间戳的探测包收端算单向延迟。# 用 scapy 发送带时间戳的 UDP 探测包收端计算单向延迟 # 需要收端也运行 scapy 并记录接收时间 from scapy.all import Ether, IP, UDP, Raw, sendp import time # 构造探测包payload 里放发送时间戳 send_time time.time() payload fQAV_PROBE:{send_time:.9f}.encode() pkt Ether(dst00:11:22:33:44:55) / IP(dst192.168.1.100) / UDP(dport9999) / Raw(payload) # 发送 1000 个包间隔 1ms for i in range(1000): sendp(pkt, ifaceeth0, verboseFalse) time.sleep(0.001)收端解析 payload 里的时间戳和本地接收时间相减就是单向延迟。如果延迟分布集中在某个值附近说明 Qav 整形生效如果长尾很严重说明 hiCredit 太大或者有背景流量干扰。这个测试我每次改完 Qav 参数都会跑一遍比看寄存器直观得多。还有一个容易忽略的点Qav 只管出口队列不管入口。如果入口有突发帧在入口就被丢弃或者排队Qav 也救不回来。所以完整方案通常是入口做流量计量policing出口做 Qav 整形。另外Qav 和 Qbv 可以叠加使用Qbv 做时间片门控Qav 在时间片内做信用整形这样既有确定性窗口又有突发容忍。配置顺序是先配 Qbv 再配 Qav否则 Qbv 的门控会覆盖 Qav 的信用状态。从那以后我每次调 Qav 参数都强制走一遍“算 idleSlope → 配 hiCredit → 跑 iperfping → 抓包看延迟分布”的流程再也不敢直接抄别人的配置。希望帮到你。本文还有配套的精品资源点击获取
返回列表