4. 无类qdisc实战
无类qdisc(classless qdisc)是Linux TC中最容易上手的一类队列规则:它们不提供class子节点,不能通过tc class add继续细分,而是直接挂在某个网络接口的根位置(root)或作为某个类的叶子qdisc使用。正因为结构简单,无类qdisc非常适合用来做“单点行为控制”——要么控制发送顺序,要么控制发送速率,要么控制发送时机。本节围绕五个最常用的无类qdisc展开:pfifo_fast、fq_codel、sfq、tbf、netem,重点讲清楚它们各自“对包做了什么”,以及如何用命令观察这些行为。
一、先建立观察基线:默认qdisc与统计字段
在动手之前,先确认接口当前的qdisc。以eth0为例:
tc qdisc show dev eth0
# 典型输出:
# qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
大多数物理网卡的默认根qdisc就是pfifo_fast。它内部有3个band(0、1、2),band 0优先级最高,band 2最低。内核根据skb的priority字段(0~15)查priomap决定入哪个band。默认priomap把priority 6、7映射到band 0,priority 4、5映射到band 1,其余映射到band 2。发送时严格按band 0→1→2的顺序取包,同一band内是FIFO。这意味着:只要band 0有包,band 1和band 2就会被“饿死”。
观察统计用tc -s qdisc show dev eth0,关键字段包括sent(已发送包数/字节)、dropped(丢弃)、overlimits(超限次数)、backlog(当前排队包数/字节)。后续所有qdisc的行为判断,都围绕这几个字段展开。
二、fq_codel:用“流公平+主动队列管理”压低缓冲膨胀
fq_codel(Fair Queueing with Controlled Delay)把进入的包按五元组哈希成多个流队列,每个流独立排队,再以轮询方式公平出队;同时每个流内部运行CoDel算法,当包在队列中的停留时间(sojourn time)持续超过目标值(默认5ms)时,主动丢弃或标记包,从而避免bufferbloat。
tc qdisc replace dev eth0 root fq_codel limit 10240 flows 1024 \
target 5ms interval 100ms quantum 1514 ecn
tc -s qdisc show dev eth0
# qdisc fq_codel 0: root refcnt 2 limit 10240p flows 1024 quantum 1514
# target 5ms interval 100ms ecn
# Sent 1234567 bytes 8901 pkt (dropped 12, overlimits 0 requeues 0)
# backlog 0b 0p requeues 0
# maxpacket 1514 drop_overlimit 0 new_flow_count 320 ecn_mark 0
# new_flows_len 0 old_flows_len 0
观察要点:new_flow_count反映新建流数量,old_flows_len反映当前活跃的“老流”队列数,ecn_mark在启用ecn时统计被标记的包。适用场景:家庭网关、Wi-Fi AP、上行带宽受限的服务器——凡是“多流并发且怕延迟抖动”的地方,fq_codel通常是首选。
三、sfq:随机公平队列,抑制单流霸占
sfq(Stochastic Fairness Queueing)同样按哈希分流,但它的哈希会周期性扰动(perturb),避免固定哈希被恶意流碰撞利用。它不主动丢包,只在队列满时丢弃新到包。
tc qdisc replace dev eth0 root sfq perturb 10 quantum 1514 \
limit 127 divisor 1024
tc -s qdisc show dev eth0
# qdisc sfq 8001: root refcnt 2 limit 127p quantum 1514b
# depth 127 divisor 1024 perturb 10sec
# Sent 987654 bytes 7000 pkt (dropped 0, overlimits 0 requeues 0)
# backlog 0b 0p requeues 0
与fq_codel相比,sfq没有延迟控制机制,它解决的是“公平性”而非“延迟”。适用场景:多用户共享出口、P2P流量与交互流量混跑、需要防止某条TCP流独占带宽的场合。如果同时关心延迟,应优先选fq_codel。
四、tbf:令牌桶限速,把“平均速率”钉死
tbf(Token Bucket Filter)是经典的整形器。它维护一个令牌桶,令牌以rate速率生成,桶容量为buffer(或burst)。只有拿到令牌的包才能发送,否则排队等待;队列满则丢弃。
tc qdisc replace dev eth0 root tbf rate 10mbit burst 32kbit \
latency 400ms
tc -s qdisc show dev eth0
# qdisc tbf 8002: root refcnt 2 rate 10Mbit burst 4Kb/8 mpu 0b
# lat 400.0ms linklayer ethernet
# Sent 5000000 bytes 4000 pkt (dropped 25, overlimits 1200 requeues 0)
# backlog 0b 0p requeues 0
关键参数关系:burst至少应能容纳一个最大包(如1514字节≈12kbit),否则小包也会被误丢;latency决定最大排队时延,它和rate共同约束了队列深度。观察overlimits:它表示因令牌不足而进入等待的次数,是限速正在生效的直接证据。适用场景:给某台主机或某个接口做硬性带宽上限,例如把测试机出口限制在10Mbit。
五、netem:人为制造延迟、抖动、丢包与乱序
netem(Network Emulator)不用于“优化”,而用于“模拟恶劣网络”。它可以在发送路径上注入延迟、抖动、丢包、重复、乱序和损坏。
tc qdisc replace dev eth0 root netem delay 100ms 20ms \
distribution normal loss 2% duplicate 1% reorder 25% 50%
tc -s qdisc show dev eth0
# qdisc netem 8003: root refcnt 2 limit 1000
# delay 100.0ms 20.0ms distribution normal loss 2% 1%
# duplicate 1% reorder 25% 50% gap 2
# Sent 300000 bytes 200 pkt (dropped 4, overlimits 0 requeues 0)
# backlog 0b 0p requeues 0
参数含义:delay 100ms 20ms表示基础延迟100ms、抖动±20ms;loss 2%表示随机丢包2%;reorder 25% 50%表示25%的包有50%概率被乱序发送。观察dropped可确认丢包是否生效。适用场景:弱网测试、音视频抗抖动验证、TCP拥塞控制算法对比实验。
六、组合使用与验证方法
无类qdisc可以嵌套:例如先挂tbf做限速,再在其下挂netem做延迟模拟,形成“限速+弱网”的复合场景。命令如下:
tc qdisc add dev eth0 root handle 1: tbf rate 5mbit burst 16kbit latency 200ms
tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 50ms loss 1%
tc -s qdisc show dev eth0
验证时建议用ping观察RTT变化、用iperf3观察吞吐是否被限制到目标速率、用tc -s观察dropped和overlimits是否随流量增长。判断标准很直接:如果iperf3稳定在5Mbit附近且overlimits持续增长,说明tbf限速生效;如果ping的RTT从0.2ms跳到50ms左右且抖动明显,说明netem延迟生效。
tc qdisc del dev eth0 root即可恢复默认。实验时建议在独立网络命名空间(ip netns)或虚拟接口(veth)中进行,避免影响生产流量。七、常见误区与排障要点
- 把tbf的burst设得过小:小于一个MTU时,大包永远拿不到足够令牌,表现为“限速远低于rate”。建议burst至少为
rate/100与MTU的较大者。 - 在ingress方向直接挂这些qdisc:无类qdisc默认作用于egress。若要控入向,需要配合
ingressqdisc +ifb重定向。 - fq_codel的target设得过大:例如设成100ms,CoDel几乎不触发,延迟控制失效。默认5ms对多数场景已足够。
- netem与tbf顺序颠倒:先netem后tbf会导致延迟被整形队列放大,建议先tbf后netem,让延迟作用在已整形的流上。
掌握这五个无类qdisc后,你已经能覆盖“优先级、公平、限速、弱网”四类最常见的单点QoS需求。下一阶段再引入有类qdisc(HTB、CBQ)时,就可以把它们作为叶子挂到类下面,形成更精细的分层控制体系。