摘取自 Gitbook的流量控制和Linux TC(Traffic Control)框架原理解析
流量控制(Traffic Control)是Linux内核提供的流量限速、整形和策略控制机制。
递归控制(分层次控制)
很多资料介绍TC是对队列组织中的数据包进行延迟,丢包等处理。但很少有人在介绍队列组织前介绍什么是递归控制,所谓的递归控制是分层次地控制,而对于每个层次,控制方式都是一致的。熟悉CFS调度的都知道,对于组调度和task调度都采用了完全相同的调度方式,然而显然组和task是属于不同层次的。
不光是控制逻辑的组织,就连Linux在实现UNIX进程模型时,也采用了这种树形的递归控制逻辑,每一个层次都是一个两层的树。
递归控制是分形的,如果能用立体的图展示会更好些,对于上图而言,除了叶子节点之外的每一个节点都是一颗独立的小树,不管是大树还是小树,对于控制逻辑或者组织逻辑而言,其性质是完全一样的。
递归的控制便于控制逻辑的任意叠加,在协议栈的设计中看到过,比如X over Y,简称XoY,比如PPPoE,IP over UDP(tun模式的OpenVPN),TCP over IP(原生的TCP/IP栈)...对于TC而言,考虑下面一个需求:
1.将整个带宽按照2:3的比例分给TCP和UDP;
2.在TCP流量中,按照源IP地址段将其划分为不同的优先级;
3.在相同的优先级队列中,按照2:8的比例将带宽分给HTTP应用和其它;
这是一个递归控制的需求,其中1和3均使用了带宽比例分配,但是显而易见,这是属于不同层次的。整个架构看起来应该是下面这个样子:
但是事情远非想象的那个单纯,虽然上面的图已经让你看出了TC框架的端倪,然而对于实现它却没有一点帮助。几个典型的问题摆在那里,怎么甄别数据包到不同的队列,图中的非叶子节点要呈现成什么数据结构,既然不是真正的队列却又要有队列的行为,那么如何表达它们?
队列组织
Linux在实现TC的时候,对“队列”进行了抽象,维护了两个回调函数指针,一个是enqueue入队操作,一个是dequeue出队操作。不管是enqueue还是dequeue,都不一定真正将数据包排入队列,而仅仅是“执行一系列的操作”。这个“执行一系列的操作”可以是:
1.对于叶子节点,真正排入一个真实的队列或者从真正的队列拉出一个数据包;
2.递归调用其它抽象队列的enqueue/ dequeue。
注意上面的第2点,提到了“其它抽象队列”,那么如何来定位这个抽象队列呢?这就需要一个抉择,也就是一个选择器,根据数据包的特征来将数据包归入一个抽象队列,这个时候,TC的设计框图可以用下图来表达:
上图以用一种递归控制的形式来定义TC框架。下图以经典的“队列规程,类别,过滤器”三元组套在这幅图上。
从上图中看到,tc由qdisc、fitler和class三部分组成:
1) qdisc通过队列将数据包缓存起来,用来控制网络收发的速度
2) class用来表示控制策略
3) filter用来将数据包划分到具体的控制策略中
qdisc
qdisc通过队列将数据包缓存起来,用来控制网络收发的速度。实际上,每个网卡都有一个关联的qdisc。它包括以下几种:
无分类qdisc(只能应用于root队列)
[p|b]fifo:简单先进先出
pfifo_fast:根据数据包的tos将队列划分到3个band,每个band内部先进先出
red:Random Early Detection,带带宽接近限制时随机丢包,适合高带宽应用
sfq:Stochastic Fairness Queueing,按照会话对流量排序并循环发送每个会话的数据包
tbf:Token Bucket Filter,只允许以不超过事先设定的速率到来的数据包通过 , 但可能允许短暂突发流量朝过设定值
有分类qdisc(可以包括多个队列)
cbq:Class Based Queueing,借助EWMA(exponential weighted moving average, 指数加权移动均值 ) 算法确认链路的闲置时间足够长 , 以达到降低链路实际带宽的目的。如果发生越限 ,CBQ 就会禁止发包一段时间。
htb:Hierarchy Token Bucket,在tbf的基础上增加了分层
prio:分类优先算法并不进行整形 , 它仅仅根据你配置的过滤器把流量进一步细分。缺省会自动创建三个FIFO类。
注意,一般说到qdisc都是指egress出向 qdisc。每块网卡实际上还可以添加一个ingress qdisc,不过它有诸多的限制
1) ingress qdisc不能包含子类,而只能作过滤
2) ingress qdisc只能用于简单的整形
如果相对ingress方向作流量控制的话,可以借助ifb( Intermediate Functional Block)内核模块。因为流入网络接口的流量是无法直接控制的,那么就需要把流入的包导入(通过 tc action)到一个中间的队列,该队列在 ifb 设备上,然后让这些包重走 tc 层,最后流入的包再重新入栈,流出的包重新出栈。
filter
filter用来将数据包划分到具体的控制策略中,包括以下几种:
u32:根据协议、IP、端口等过滤数据包
fwmark:根据iptables MARK来过滤数据包
tos:根据tos字段过滤数据包
class
class用来表示控制策略,只用于有分类的qdisc上。每个class要么包含多个子类,要么只包含一个子qdisc。当然,每个class还包括一些列的filter,控制数据包流向不同的子类,或者是直接丢掉。
网友评论