从队列规则到流量整形 - 揭秘 Linux 网络 QoS 的实现机制
系列:Linux 网络子系统源码剖析篇号:第 10 篇 内核版本:Linux 5.10 LTS重点模块:Qdisc 框架、HTB、FQ_CODEL、流量整形、分类器
Linux 流量控制(TC)框架的完整实现
Qdisc(队列规则)的核心机制
常用 Qdisc 算法(pfifo_fast、HTB、TBF、FQ_CODEL)
流量整形和流量分类的实现原理
分类器(u32、fw、cgroup)的使用
实战案例:多用户带宽分配、优先级控制
性能优化技巧和监控方法
已阅读第 1-9 篇文章
了解网络协议栈基础
理解队列和调度算法
熟悉 tc 命令基本用法
约 90-100 分钟
Linux 流量控制(Traffic Control,简称 TC)是内核提供的网络 QoS(Quality of Service)机制,用于控制网络流量的发送速率、优先级和带宽分配。
四大核心组件:
源码位置:include/net/sch_generic.h
代码解析:
Qdisc 结构:每个网络设备队列的核心数据结构
enqueue/dequeue:入队和出队操作,由具体 qdisc 实现
统计信息:bstats(发送统计)、qstats(队列统计)
Qdisc_ops:定义 qdisc 的所有操作函数
Qdisc_class_ops:有类别 qdisc 的类别操作
源码位置:net/sched/sch_generic.c
初始化流程图:
数据包处理流程图:
pfifo_fast 是 Linux 的默认 qdisc,提供三个优先级队列(band 0、1、2)。
源码位置:net/sched/sch_generic.c
/*** pfifo_fast 结构** 三个优先级队列* band 0: 最高优先级(交互式流量)* band 1: 中等优先级(普通流量)* band 2: 最低优先级(批量传输)*/struct pfifo_fast_priv {struct skb_array q[PFIFO_FAST_BANDS]; /* 三个队列 */};#define PFIFO_FAST_BANDS 3/*** 优先级映射表** 根据 skb->priority 映射到 band*/static const u8 prio2band[TC_PRIO_MAX+1] = {1, 2, 2, 2, 1, 2, 0, 0, /* 0-7 */1, 1, 1, 1, 1, 1, 1, 1/* 8-15 */};/** 优先级说明:* TC_PRIO_BESTEFFORT (0) -> band 1 (中等)* TC_PRIO_FILLER (1) -> band 2 (低)* TC_PRIO_BULK (2) -> band 2 (低)* TC_PRIO_INTERACTIVE_BULK(4) -> band 1 (中等)* TC_PRIO_INTERACTIVE (6) -> band 0 (高)* TC_PRIO_CONTROL (7) -> band 0 (高)*//*** pfifo_fast_enqueue - 入队* @skb: 数据包* @sch: qdisc* @to_free: 释放列表** 根据 TOS 将数据包放入不同优先级队列*//*** pfifo_fast_dequeue - 出队* @sch: qdisc** 按优先级从队列取出数据包*/
pfifo_fast 结构图:
使用示例:
TBF 使用令牌桶算法进行流量整形,限制流量速率。
源码位置:net/sched/sch_tbf.c
令牌桶算法原理:
时间轴示例:t0: tokens = 0, 生成令牌t1: tokens = 1000, 发送 500 字节包,tokens = 500t2: tokens = 1500, 发送 1000 字节包,tokens = 500t3: tokens = 1500, 发送 2000 字节包,令牌不足,等待令牌生成速率 = rate (字节/秒)桶容量 = buffer (字节)允许突发 = buffer (字节)
TBF 配置示例:
HTB 是最常用的分层流量控制 qdisc,支持带宽保证和借用。
核心概念:
关键特性:1. 保证带宽(rate):每个类别的最小带宽2. 最大带宽(ceil):每个类别的最大带宽3. 带宽借用:空闲时可以借用其他类别的带宽4. 优先级(prio):决定借用顺序
HTB 配置示例:
FQ_CODEL 结合了公平队列和主动队列管理,是现代 Linux 推荐的 qdisc。
核心特性:
FQ_CODEL 配置示例:
分类器用于将数据包分类到不同的类别或队列。
u32 是最常用的分类器,支持基于 IP 头字段的匹配。
匹配规则示例:
fw 分类器使用 iptables 的 mark 进行分类。
配置示例:
cgroup 分类器根据进程所属的 cgroup 进行分类。
需求:
总带宽 10Mbps
用户 A:保证 3Mbps,最大 6Mbps
用户 B:保证 3Mbps,最大 6Mbps
用户 C:保证 4Mbps,最大 10Mbps
#!/bin/bash
DEV=eth0
TOTAL=10mbit
USER_A_IP=192.168.1.10
USER_B_IP=192.168.1.20
USER_C_IP=192.168.1.30
# 1. 删除现有 qdisc
tc qdisc del dev $DEV root 2>/dev/null
# 2. 创建 HTB qdisc
tc qdisc add dev $DEV root handle 1: htb default 30
# 3. 创建根类别
tc class add dev $DEV parent 1: classid 1:1 htb rate $TOTAL
# 4. 创建用户类别
# 用户 A
tc class add dev $DEV parent 1:1 classid 1:10 htb \
rate 3mbit ceil 6mbit prio 1
tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10
# 用户 B
tc class add dev $DEV parent 1:1 classid 1:20 htb \
rate 3mbit ceil 6mbit prio 1
tc qdisc add dev $DEV parent 1:20 handle 20: sfq perturb 10
# 用户 C
tc class add dev $DEV parent 1:1 classid 1:30 htb \
rate 4mbit ceil 10mbit prio 2
tc qdisc add dev $DEV parent 1:30 handle 30: sfq perturb 10
# 5. 添加过滤器
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dst $USER_A_IP flowid 1:10
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dst $USER_B_IP flowid 1:20
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dst $USER_C_IP flowid 1:30
echo "多用户带宽分配配置完成"
需求:
高优先级:VoIP、视频会议(延迟敏感)
中优先级:HTTP、HTTPS(交互式)
低优先级:下载、备份(批量传输)
#!/bin/bash
DEV=eth0
# 1. 创建 PRIO qdisc(优先级队列)
tc qdisc add dev $DEV root handle 1: prio bands 3 \
priomap 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
# 2. 为每个优先级添加 qdisc
# band 0: 高优先级(VoIP)
tc qdisc add dev $DEV parent 1:1 handle 10: sfq perturb 10
# band 1: 中优先级(HTTP)
tc qdisc add dev $DEV parent 1:2 handle 20: sfq perturb 10
# band 2: 低优先级(下载)
tc qdisc add dev $DEV parent 1:3 handle 30: sfq perturb 10
# 3. 添加过滤器
# VoIP (SIP, RTP)
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dport 5060 0xffff flowid 1:1 # SIP
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dport 10000 0xf000 flowid 1:1 # RTP (10000-20000)
# HTTP/HTTPS
tc filter add dev $DEV parent 1: protocol ip prio 2 u32 \
match ip dport 80 0xffff flowid 1:2
tc filter add dev $DEV parent 1: protocol ip prio 2 u32 \
match ip dport 443 0xffff flowid 1:2
# FTP (低优先级)
tc filter add dev $DEV parent 1: protocol ip prio 3 u32 \
match ip dport 21 0xffff flowid 1:3
echo "优先级队列配置完成"
# 场景 1:简单限速# 推荐:TBFtc qdisc add dev eth0 root tbf rate 10mbit burst 10kb latency 50ms# 场景 2:多用户带宽分配# 推荐:HTB + SFQtc qdisc add dev eth0 root htbtc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit# ... 添加子类别# 场景 3:降低延迟# 推荐:FQ_CODEL 或 CAKEtc qdisc add dev eth0 root fq_codel# 场景 4:高性能路由器# 推荐:pfifo_fast(默认)或 mq + fq_codeltc qdisc add dev eth0 root mqtc qdisc add dev eth0 parent :1 fq_codeltc qdisc add dev eth0 parent :2 fq_codel
# 1. 使用硬件卸载ethtool -K eth0 tso on gso on gro on# 2. 使用多队列# 查看网卡队列数ethtool -l eth0# 设置队列数ethtool -L eth0 combined 4# 3. 使用 XPS (Transmit Packet Steering)# 绑定队列到 CPUecho 1 > /sys/class/net/eth0/queues/tx-0/xps_cpusecho 2 > /sys/class/net/eth0/queues/tx-1/xps_cpus# 4. 使用 BPF 分类器(性能最好)tc qdisc add dev eth0 clsacttc filter add dev eth0 egress bpf da obj my_filter.o sec classifier
本文深入剖析了 Linux 流量控制(QoS)的实现机制,涵盖了从 Qdisc 框架到具体算法的完整实现。
流量控制框架:
Qdisc:队列规则,控制数据包排队和发送
Class:类别,支持层次化带宽分配
Filter:过滤器,将数据包分类到不同类别
数据包流向:应用 → TCP/UDP → IP → qdisc → 网卡
常用 Qdisc:
pfifo_fast:默认 qdisc,三个优先级队列
TBF:令牌桶,简单限速
HTB:分层令牌桶,支持带宽保证和借用
SFQ:公平队列,每个流公平分配带宽
FQ_CODEL:公平队列 + 主动队列管理,推荐使用
分类器:
u32:基于 IP 头字段匹配
fw:使用 iptables mark
cgroup:基于进程 cgroup
选择合适的 qdisc:根据场景选择(限速、多用户、低延迟)
使用 HTB + SFQ:灵活的带宽分配
使用 FQ_CODEL:降低延迟,提升体验
合理设置优先级:关键业务高优先级
监控和调优:定期检查统计信息
使用硬件卸载(TSO、GSO、GRO)
使用多队列网卡
使用 BPF 分类器
避免过深的类别层次
合理设置队列长度
下一篇《Linux 网络子系统源码剖析(十一):套接字层实现》将深入分析:
socket层的完整架构设计
socket缓冲区的管理策略
零拷贝技术的实现细节
I/O多路复用
Q1:如何选择合适的 qdisc?
A:
Q2:HTB 的 rate 和 ceil 有什么区别?
A:
rate:保证带宽,即使其他类别空闲也能获得
ceil:最大带宽,可以借用其他类别的空闲带宽
示例:
Q3:如何调试 tc 规则不生效?
A:
# 1. 检查规则是否正确添加tc qdisc show dev eth0tc class show dev eth0tc filter show dev eth0# 2. 查看统计信息tc -s qdisc show dev eth0tc -s class show dev eth0# 3. 检查数据包是否匹配tc -s filter show dev eth0# 查看 "Sent" 字段是否增加# 4. 使用 tcpdump 验证tcpdump -i eth0 -nn 'dst host 192.168.1.100'# 5. 检查接口方向# 注意:tc 只作用于出站流量(egress)# 入站流量需要使用 IFB 或 ingress qdisc
Q4:如何限制入站流量?
A:
Q5:FQ_CODEL 和 SFQ 有什么区别?
A:
Q6:如何监控 QoS 效果?
A:
Q7:大量 overlimits 是什么意思?
A:overlimits 表示数据包因为速率限制而被延迟发送的次数。这是正常的,说明流量整形正在工作。如果 dropped 很多,说明队列满了,可能需要:
增加队列长度(limit)
增加带宽限制(rate/ceil)
检查是否有流量突发
Q8:TIME_WAIT 连接会占用 QoS 带宽吗?
A:不会。TIME_WAIT 状态的连接不会发送数据,因此不会占用 QoS 带宽。QoS 只对实际发送的数据包进行控制。
Q9:容器环境如何使用 QoS?
A:
Q10:如何测试 QoS 配置是否生效?
A:
net/sched/sch_generic.c - qdisc 核心框架
net/sched/sch_api.c - qdisc API
net/sched/sch_htb.c - HTB 实现
net/sched/sch_tbf.c - TBF 实现
net/sched/sch_sfq.c - SFQ 实现
net/sched/sch_fq_codel.c - FQ_CODEL 实现
net/sched/sch_prio.c - PRIO 实现
net/sched/cls_u32.c - u32 分类器
net/sched/cls_fw.c - fw 分类器
net/core/dev.c - 设备层 qdisc 处理
# tc 工具apt-get install iproute2# 监控工具apt-get install iftop nload bmon# 测试工具apt-get install iperf3 netperf# tc 手册man tcman tc-htbman tc-tbfman tc-sfqman tc-fq_codel
Linux Advanced Routing & Traffic Control HOWTO:http://lartc.org/
tc 命令详解:https://man7.org/linux/man-pages/man8/tc.8.html
HTB 手册:http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm
CAKE 文档:https://www.bufferbloat.net/projects/codel/wiki/Cake/
《Linux 高级路由和流量控制》
《深入理解 Linux 网络技术内幕》- Christian Benvenuti
《Linux 内核源代码情景分析》- 毛德操、胡希明
作者:肇中