当前位置:首页>Linux>Linux 网络子系统源码剖析(十):流量控制(QoS)源码解析

Linux 网络子系统源码剖析(十):流量控制(QoS)源码解析

  • 2026-09-26 22:36:22
Linux 网络子系统源码剖析(十):流量控制(QoS)源码解析

从队列规则到流量整形 - 揭秘 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)机制,用于控制网络流量的发送速率、优先级和带宽分配。

四大核心组件:

数据包流向


🔧 Qdisc 框架实现

核心数据结构

源码位置:include/net/sch_generic.h

代码解析:

  1. Qdisc 结构:每个网络设备队列的核心数据结构

  2. enqueue/dequeue:入队和出队操作,由具体 qdisc 实现

  3. 统计信息:bstats(发送统计)、qstats(队列统计)

  4. Qdisc_ops:定义 qdisc 的所有操作函数

  5. Qdisc_class_ops:有类别 qdisc 的类别操作

Qdisc 初始化

源码位置:net/sched/sch_generic.c

初始化流程图:

数据包入队和出队

数据包处理流程图:


🔄 常用 Qdisc 实现

pfifo_fast(默认 Qdisc)

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 (Token Bucket Filter)

TBF 使用令牌桶算法进行流量整形,限制流量速率。

源码位置:net/sched/sch_tbf.c

令牌桶算法原理:

时间轴示例:
t0: tokens = 0, 生成令牌
t1: tokens = 1000, 发送 500 字节包,tokens = 500
t2: tokens = 1500, 发送 1000 字节包,tokens = 500
t3: tokens = 1500, 发送 2000 字节包,令牌不足,等待
令牌生成速率 = rate (字节/秒)
桶容量 = buffer (字节)
允许突发 = buffer (字节)

TBF 配置示例:

HTB (Hierarchical Token Bucket)

HTB 是最常用的分层流量控制 qdisc,支持带宽保证和借用。

核心概念:

关键特性:
1. 保证带宽(rate):每个类别的最小带宽
2. 最大带宽(ceil):每个类别的最大带宽
3. 带宽借用:空闲时可以借用其他类别的带宽
4. 优先级(prio):决定借用顺序

HTB 配置示例:

FQ_CODEL (Fair Queue Controlled Delay)

FQ_CODEL 结合了公平队列和主动队列管理,是现代 Linux 推荐的 qdisc。

核心特性:

FQ_CODEL 配置示例:


🎯 流量分类器

分类器用于将数据包分类到不同的类别或队列。

u32 分类器

u32 是最常用的分类器,支持基于 IP 头字段的匹配。

匹配规则示例:

fw 分类器

fw 分类器使用 iptables 的 mark 进行分类。

配置示例:

cgroup 分类器

cgroup 分类器根据进程所属的 cgroup 进行分类。


💡 实战案例

案例 1:多用户带宽分配

需求:

  • 总带宽 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 "多用户带宽分配配置完成"

案例 2:优先级队列

需求:

  • 高优先级: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 "优先级队列配置完成"


⚡ 性能优化

选择合适的 Qdisc

# 场景 1:简单限速
# 推荐:TBF
tc qdisc add dev eth0 root tbf rate 10mbit burst 10kb latency 50ms
# 场景 2:多用户带宽分配
# 推荐:HTB + SFQ
tc qdisc add dev eth0 root htb
tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit
# ... 添加子类别
# 场景 3:降低延迟
# 推荐:FQ_CODEL 或 CAKE
tc qdisc add dev eth0 root fq_codel
# 场景 4:高性能路由器
# 推荐:pfifo_fast(默认)或 mq + fq_codel
tc qdisc add dev eth0 root mq
tc qdisc add dev eth0 parent :1 fq_codel
tc qdisc add dev eth0 parent :2 fq_codel

减少 CPU 开销

# 1. 使用硬件卸载
ethtool -K eth0 tso on gso on gro on
# 2. 使用多队列
# 查看网卡队列数
ethtool -l eth0
# 设置队列数
ethtool -L eth0 combined 4
# 3. 使用 XPS (Transmit Packet Steering)
# 绑定队列到 CPU
echo  1 > /sys/class/net/eth0/queues/tx-0/xps_cpus
echo  2 > /sys/class/net/eth0/queues/tx-1/xps_cpus
# 4. 使用 BPF 分类器(性能最好)
tc qdisc add dev eth0 clsact
tc 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

最佳实践

  1. 选择合适的 qdisc:根据场景选择(限速、多用户、低延迟)

  2. 使用 HTB + SFQ:灵活的带宽分配

  3. 使用 FQ_CODEL:降低延迟,提升体验

  4. 合理设置优先级:关键业务高优先级

  5. 监控和调优:定期检查统计信息

性能优化建议

  1. 使用硬件卸载(TSO、GSO、GRO)

  2. 使用多队列网卡

  3. 使用 BPF 分类器

  4. 避免过深的类别层次

  5. 合理设置队列长度

下一篇预告

下一篇《Linux 网络子系统源码剖析(十一):套接字层实现》将深入分析:

  • socket层的完整架构设计

  • socket缓冲区的管理策略

  • 零拷贝技术的实现细节

  • I/O多路复用


❓ 常见问题(FAQ)

Q1:如何选择合适的 qdisc?

A:

Q2:HTB 的 rate 和 ceil 有什么区别?

A:

  • rate:保证带宽,即使其他类别空闲也能获得

  • ceil:最大带宽,可以借用其他类别的空闲带宽

  • 示例:

Q3:如何调试 tc 规则不生效?

A:

# 1. 检查规则是否正确添加
tc qdisc show dev eth0
tc class show dev eth0
tc filter show dev eth0
# 2. 查看统计信息
tc -s qdisc show dev eth0
tc -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 tc
man tc-htb
man tc-tbf
man tc-sfq
man 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 内核源代码情景分析》- 毛德操、胡希明


作者:肇中  

最新文章

随机文章