1. Linux流量控制介绍
Linux提供了一系列可用于修改和调整网络数据包的工具。Linux社区对修改数据包、应用防火墙策略(如netfilter
或更古老的ipchains)以及在Linux上运行着的各种各样的网络服务已经非常熟悉。然而,Linux社区内部却很少有
人知道Linux的流量控制子系统已经非常成熟,并且有着强大的功能。在Linux社区外,Linux流量控制的功能更是
鲜有人知。
本指南将会介绍 流量控制的思想、 方法以及Linux流量控制的组件。同时会提供一些一般性的指导。 . 这份指南是
笔者根据LARTC HOWTO、对各种软件项目的研究,以及在LARTC mailing list上的一些重要主题整理而成的。
对于急于实施Linux流量控制的人,建议直接阅读 Traffic Control using tcng and HTB HOWTO何LARTC HOWTO
这两份资料。
2. Linux流量控制思想简介
本节将会简要介绍 Linux流量控制并说明为什么要实施流量控制,同时指出实施流量控制的一些优点 和缺点,最后
再介绍Linux流量控制思想中的关键要素。
2.1. 什么是流量控制
流量控制就是在路由器上通过一系列队列,对数据包进行排序以控制它们的发送顺序,并通过一系列策略控制收到
的和发送的数据包是否应该被丢弃,同时还要对数据包的发送速率进行控制。
在大部分情况下,流量控制只有一个队列,接收收到的数据包(译者注:这里的接收到的数据包指的是由本机应用
程序产生的数据包,而不是指从网络上接收到的数据包),把它们放入队列,并以网络硬件所能支持的最大速度发
送出去。这种类型的队列叫做FIFO。 注意:Linux默认的排队规则是pfifo_fast,这是一种比FIFO稍微复杂一点的
排队规则。
不同的应用有不同类型的队列。队列就是一种用于组织未能立即开始的任务或数据流的方法。网络链路通常要求数
据包以一定的顺序发送,因此我们需要在本机网络出口上使用队列来管理数据包。
如果一台桌面电脑和一台流量较大的网站服务器共用一条网络链路,那就有可能发生带宽争夺。由于网站服务器的
上行流量很大,超过了链路速度,从而路由器上的队列被来自网站服务器的数据包挤满。路由上的队列被挤满后,
路由器就会开始丢包,这会导致桌面电脑的丢包率上升,数据延时增大。过大的延时会让桌面用户抓狂的。如果我
们把这条链路分成两部分,分别给桌面电脑和网站服务器使用,就能较好地分配带宽资源,让两者都能比较正常地
工作。
在Linux下,用户可以通过一系列工具在网络端口上应用不同类型的队列和策略来实施流量控制。这些工具提供的
功能都很强大,同时也很复杂。但是,虽然实施流量控制可以改善网络质量,但提高带宽所得到的效果总是优于实
施流量控制的。
QoS(Quality of Service,服务质量)通常也被当做是一种实施流量控制的方法。
2.2. 为什么要实施流量控制
分组交换网络和电路交换网络的一个重要不同之处是,分组交换网络是无状态的,而电路交换网络是有状态的(如
电话线路)。分组交换网络被设计成和IP网络一样,是无状态的网络,无状态属性保证了包交换网络的健壮性。
无状态网络的缺点是无法很好地区分网络上的数据流类型。而通过实施流量控制,我们可以根据数据包的类型来决
定其发送的方法和顺序,这就可以在无状态网络上模拟出一个有状态网络。
流量控制还可以用于很多网络环境下。下面列出了一些例子,在这些情况下,实施流量控制通常可以解决问题,至
少也能把糟糕的情况改善一些
流量控制可用于很多情况,下面的例子并没有完全包括所有的情况。但列出这些例子可以帮助读者了解,在何种情
况下实施流量控制可以收到不错的效果。
常见的流量控制实践
使用TBF和HTB把带宽限制在一个数值之下。
使用HTB和classifying,并配合使用filter,限制某个用户、某个服务或某个程序所能使用的带宽。
在非对称线路(如ADSL)上最大化TCP协议的吞吐量。这可以通过提高ACK数据包的优先级来实现。
为某个用户或某个应用保留一定的带宽。这可以通过HTB和classifying来实现。
提高延时敏感性型用的性能。这要通过在HTB内使用PRIO来实现。
合理分配多余的带宽。可以通过HTB的租借机制来实现。
在无限制的网络上实现公平分配资源。这可以通过HTB的租借机制来实现。
丢弃某种类型的数据包。这可以通过 policer 和 filter 来实现。
2.3. 实施流量控制的好处
若能正确地实施流量控制,那就能让网络得到更充分地使用,减少网络上的竞争。大流量的下载不会破坏实时程序
的交互性,同时也能让低优先级的数据传输(如电子邮件)正常进行。
更广义上,如果流量控制的策略能很好地符合与用户约定好的网络使用规则,那么用户也就能更合理地使用有限的
网络资源。
2.4. 实施流量控制的缺点
复杂性是实施流量控制的一个主要缺点。虽然可以借助一系列工具的帮助来降低实施流量控制的复杂性,但是要想
从已经配置好的流量控制方案中找出不恰当的配置也依旧不是一件容易的事情。
正确实施流量控制可以更公平地分配网络资源,但若配置不恰当,可能反而会恶化网络环境,使网络资源的分配更
加不公平。
流量控制的规则越多,路由器就要使用更多的处理器资源来处理这些流量控制规则,我们要保证路由器有足够的能
力来处理我们所设定的流量控制规则。幸运的是,路由器不需要消耗太多的计算资源就能处理比较复杂的规则。从
另一方面来说,我们更应把注意力放在如何保证如此复杂的流量控制规则不会出现错误。
对于个人用户来说,实施流量控制几乎是零成本的。但对于企业来说,实施流量控制所付出的成本可能要比购买更
大的带宽还要多,
2.5. 队列
队列是调度的实现。一个队列中会有有限多个对象,这些对象将在队列中排队等待以便被处理。在网络中,队列中
的对象就是数据包,数据包在队列中排队等待网卡将它们发送出去。最简单的一种情况是,队列中的数据包按照先
进先出规则进行排队,最先进入队列的数据包将会被最先传输出去,而最后进入队列的数据包将被最后传输出去。
[2]. 在计算机网络(或计算机科学)中,这种排队方式就叫做 FIFO.
如果不使用其他组件,单纯的队列无法提供流量控制的功能。队列只有两种操作,当数据包到来时执行入队操作,
当有数据包可以发送时执行出队操作。
当队列配合其他组件使用时,就能实现很多的功能。我们可以同时使用多个队列,对数据包进行重排、丢弃、改变
优先级等操作。
对运行在高层的应用来说,数据包只要能被发送出去就可以,至于发送顺序如何倒不是很重要。因此,对高层应用
来说,流量控制系统就只是一条单一的队列。 [3] 只有对于流量控制本层来说,流量控制结构才是可见的和有意义
的。
2.6. 数据流
一个数据流就是两台主机间建立的一条连接。任何经由这条连接发送的一系列数据包都可以看成这两台主机间的一
条数据流。在TCP中,源IP和端口及目的IP和端口唯一决定了一条数据流。在UDP中也类似如此。
流量控制可以将网络流量分割成不同类型的数据流,这些数据流可以作为一个整体进行传输(参考DiffServ)。不
同的流量控制结构可以将网络流量平均地分割成不同的数据流。
当网络中的数据流发生冲突时,对数据流进行处理就显得非常重要。这种情况常见于一个应用程序产生了大量的数
据流时。
2.7. 令牌桶
在流量整形中,令牌桶算法是一种很常用的整形方法。
为了控制队列中数据包出队的速率,就需要精确计算单位时间内出队的数据包数或数据包总大小,而这通常是很复
杂的。为了进行简化,现在一般都使用另一种机制:系统以一定的速率产生令牌,每个数据包(或一个字节)对应
一个令牌,只有当令牌充足的时候数据包才能出队。
打个比方来说,在游乐园里有很多游客在排队等待乘坐过山车。让我们假设过山车按照固定的时间到来。游客必须
等待一列过山车到来后才能乘坐。在这里,过山车上的位置就相当于令牌,而游客就相等于数据包。这就是网络速
率限制,或者称为shaping。在单位时间内,只能有固定数量的游客乘上过山车(出队)。
继续这个过山车的比喻,假设某个时刻没有游客排队,而车站里有很多车子,这时候来了一大波游客,那么这些游
客的大多数人(甚至是全部)都能立刻乘上过山车(因为此时车站里有很多空车)。车站里所能停放的过山车数量
就是令牌桶中“桶”的大小。桶中积攒起来的令牌能在突然出现大量数据包时全部使用出去。
让我们完成这个比喻。游乐园里的过山车以恒定速率到来,如果没有游客排队的话,就停放在等待区里,直到等待
区里停满了车子。在令牌桶模型中,令牌以恒定的速率填充到桶中,直到桶满了为止。使用令牌桶模型进行流量整
形能应付诸如HTTP应用之类会产生突发数据传输的情形。
TBF 排队规则是一个经典的流量整形实现(在TBF小节中有一张图表能帮助读者理解令牌桶)。TBF以rate的速率产
生令牌,并在桶中有令牌的时候才发送数据包。
当队列中没有数据包的时候,就暂时不需要使用令牌,这时令牌就会在桶中积累起来。如果桶的容量是无限制的,
那就会失去流量整形的意义,因此,桶的容量必须是有限的。因为在桶中积累了一定数量的令牌,此时若队列中突
然出现大量数据包需要出队,我们也有足够的令牌保证数据包能够顺畅出队。
这就是说,一个装满的令牌桶能在一定时间内应付任何类型的流量。比较恒定的网络流量适合使用较小的令牌桶。
经常有突发数据传输的网络则比较适合使用大的令牌桶,除非流量整形的目的是为了限制突发数据传输。
概括来说,系统会以恒定的速率产生令牌,直到令牌桶满了为止。令牌桶能够在保证较长一段时间内网络流量在限
制值以下,又能处理大速率的突发数据传输。
令牌桶模型也被应用于TBF(一种classless qdiscs)和HTB(一种classful qdiscs)中。在tcng语言中,two- and
three-color meters也是一种令牌桶模型的应用。
2.8. 数据包和帧
一块数据被称为什么取决于它位于哪一层网络栈中。虽然这里写出了数据包和帧两个名词,但在整个文档中将不会
刻意区分这两个词在技术上的区别(这通常是错误的做法!)。
帧通常被用于描述位于第二层(数据链路层)的数据块。以太网接口、PPP接口和T1接口都把位于第二层的数据块
称为帧。在这种情况下,帧就是流量控制所要操作的对象。
在其他情况下,位于第三层(网络层)的数据块被称为数据包。虽然不太精确,但本文将会使用数据包这一名词进
行说明。
3. 流量控制中的概念
3.1. 整形
整形就是流量控制,把数据包的发送速率控制在一个固定的水平以下。
整形会控制队列中数据包的发送速率,使其保持在一个固定的值以下。这是最常用的流量控制手段。由于整形通过
延迟数据包的发送来控制数据包发送速率,故整形机制是非工作保存的。“非工作保存”可以理解为:系统必须进行
一些操作来延迟数据包的发送。
反过来说,一种非工作保存的队列是可以进行流量整形的,而工作保存的队列(参考 PRIO)不能进行流量整形,
因为工作保存队列无法延迟发送数据包。
流量整形能将网络流量限制在一个固定的速率下,通常以位/秒或字节/秒作为衡量单位。但这也有副作用,那就是
突发的数据传输也会被限制在一个较低的速率下。 [4]. 流量整形的好处之一是可以控制数据包的延迟时间。在流量
整形中,通常会使用令牌桶机制来实现整形。 查看 第 2.7 节 “令牌桶” 以了解更多有关令牌桶的信息.
3.2. 调度
一个调度器会对将要发送的数据包顺序进行排列或重排。
对队列中对数据包顺序进行排列或重排就叫做调度。最常见的调度器是FIFO(先入先出队列)。由于数据包必须按
顺序出队,因此队列实际上就是一个调度器。
对于不同的网络环境,我们可以使用不同的调度器。一个公平队列算法(参考SFQ)能防止一个客户端或一个数据
流占用过多的带宽。一个循环算法(参考WRR)可以让各个客户端或数据流都有平等的使用网络的机会。还有一些
更复杂的算法可用于防止骨干网流量过载,或者是对一些常见算法的改进。
3.3. 分类
分类器能把不同类型的网络流量划分到不同的队列中去。
把数据包按照不同类型进行划分叫做分类。通常我们只对上行流量进行分类。在路由器接收、路由并转发一个数据
包的时候,网络设备可以以几种不同的方式给数据包进行分类。其中一种方式是标记数据包。标记数据包的操作可
以在一个网络中由管理员进行设置,也有可能在数据包经过每一跳时发生。
Linux允许数据包通过一系列的流量控制结构,期间允许用户使用决策器对数据包进行分类(参考第 4.3 节 “ 过滤
器 ”和第 4.5 节 “决策器”)。
3.4. 策略
决策器能计算并限制某个特定队列的流量。
在流量控制中使用决策器来控制流量是非常简单的。决策器通常会应用于网络边界上,某个节点使用的流量不会超
过分配给它的流量。当网络流量在预设值以下时,决策器什么都不会做。但当网络流量超过预设值时,决策器就开
始发挥作用,它能将流量速率控制在一个固定的水平之下。最不近人情的操作是即使在数据包能够继续分类的情况
下依旧直接将其丢弃。
决策器只会区别对待两种情况,分别是入队数据包速率高于或低于预定速率。当入队速率低于预设值时,决策器就
会允许数据包入队。当入队速率高于预设值时,决策器就执行其他操作(丢包或重新分类)。虽然决策器内部使用
令牌桶来计算速率,但它并不像shaping那样会延迟数据包的发送。
3.5. 丢弃
丢弃一个数据包,一个数据流或一个分类下的数据包,都可以叫做丢弃。
丢弃一个数据包就叫做丢包。
3.6. 标记
标记是一种对数据包进行一些修改的操作。
流量控制中的标记操作会给数据包加上一个DSCP,接下来在由一个管理员控制的一个网络下的其他路由器上将会
使用这个标记。
4. Linux流量控制中的组件
表 1. 流量控制的概念与Linux中流量控制组件的对应关系
流量控制概念
Linux流量控制组件
shaping(整形)
class 提供流量整形功能。
scheduling(调
度器)
classifying(分
类)
qdisc 就是一个调度器。调度器有比较简单的FIFO,也有比较复杂的如HTB之类的调度
器。。
filter 会附着在 classifier 上进行分类工作。 严格来说,分类器必须依靠过滤器才
能正常工作。
policing(策略)
policer 可以看成是 filter 的一个子功能。
dropping(丢弃)
将 filter 和 policer 配合使用,并将 policer 的动作设为“丢包”,就可以丢弃指定
的数据流。
marking(标记)
dsmark qdisc 用于给数据包打上标记。
4.1.
简单来说,qdisc其实就是一个调度器(第 3.2 节 “调度”)。每个网络接口都会有一个调度器,默认的调度器是
FIFO。qdiscs会根据调度器的规则重新排列数据包进入队列的顺序。
qdisc 是 Linux 流量控制系统的核心。qdisc 也被称为排队规则。
classful qdiscs 可以包含多个 class ,数据包根据 filter 的设置被分发到特定的 classful qdiscs 上。分类排
队规则可以没有子类,但这样做通常会导致系统资源被白白浪费。
classless qdiscs 是没有分类的。由于非分类的排队规则没有子对象,所以 classifying 中也没有针对其的操作。因
此,非分类的排队规则无法和过滤器相关联。
许多人常常弄不清楚 root qdisc 和 ingress qdisc 有何区别。这两者都不是真正的排队规则,他们只是一个供
我们建立自己的流量控制结构的地方。出站数据包规则可以附加到 egress 上,而入站数据包可以附加到 ingress
上。
egress 和 ingress 存在于每一个网络接口上,在流量控制中最常用的是 egress,也就是我们所熟知的 root
qdisc。 root qdisc 能与任何类型的排队规则相关联,形成流量控制结构。每一个出站的数据包都要经过
egress,或者说都要经过 root qdisc 排队规则。
网络接口上收到的数据包都将经过 ingress qdisc。 ingress qdisc 下无法创建任何分类,且只能与一个
filter 相关联。一般情况下, ingress qdisc 仅仅被当成是一个用于附加 policer 来控制入站流量的对象。
简而言之,egress 要比 ingress 更强大,更像是一个真正的排队规则。ingress 只能和决策器配合使用。本文也将
重点介绍 root qdisc,除非特别说明,否则所有的操作都是默认针对 ingress 进行的。
4.2. 分类
类仅存在于可分类的 qdisc 之下(如 HTB CBQ 等)。理论上,类能无限扩展,一个类可以仅包含一个排队规
则,也可以包含多个各自排队规则的子类。 [5]. 一个类之下可以包含多个分类的排队规则。这就给 Linux 流量控制
系统予以了极大的可扩展性和灵活性。
每一个类都有一个数字编号, filter 将根据这个编号与指定的类相关联。对数据包进行重分类或丢弃操作时也
需要用这个数字编号来指定相应的分类器对象。
排队规则上的最末尾的分类成为叶子分类。叶子分类默认包含一个 FIFO 排队规则,且不会包含任何子分类。任何
包含子分类的分类都不是叶子分类。
4.3. 过滤器
过滤器是 Linux 流量控制系统中最复杂的对象,它是连接各个流量控制核心组件的纽带。过滤器最简单和最常见的
用法就是对数据包进行分类。Linux允许用户使用一个或多个过滤器将出站数据包分类并送到不同的出站队列上
(参考第 3.3 节 “分类”)。
一个过滤器必须有一个 classifier 。
一个过滤器可以有一个 policer 。
过滤器能与 qdisc 相关联,也可以和 class 相关联。所有的出站数据包首先会通过 root qdisc,接着被与
root qdisc 关联的过滤器进行分类,进入子分类,并被子分类下的过滤器继续进行分类。
q
d
i
s
c
4.4. 分类器
过滤器可以使用 tc 命令来进行操作,根据实际需求可以选择使用不同的分类器。最常用的分类器是 u32 分类
器,它可以根据数据包中的属性对数据包进行分类。
分类器可以根据数据包的元数据对数据包进行分类,识别不同类型的数据包。在 Linux 流量控制中,分类器的使用
是最基本的东西。
4.5. 决策器
决策器只能配合 filter 使用。决策器只有两种操作,当流量高于用户指定值时执行一种操作,反之执行另一种
操作。灵活使用决策器可以模拟出三色计(three-color meter)。请参考第 10 节 “图表”。
policing 和 shaping 都是 Linux 流量控制中的基本组件,两者都可以对带宽进行限制。但不同的是, shaping 能
保存并延迟发送数据包,而 policing 只会直接丢弃数据包。请参考 例 5 “tc filter ”。
4.6. 丢包
丢包操作只能在 policer 中使用。任何已经与 filter 关联的 policer 都可以使用丢包操作。
注意:在 Linux 流量控制系统中,想要丢弃数据包只能依靠决策器;。决策器可以把进入队列的数据包流速限定在
一个指定值之下。另外,它也可以配合分类器,丢弃特定类型的数据包。[6].
然而,丢包也会造成副作用。比如, a packet will be dropped if the scheduler employed uses this method to
control flows as the GRED does.
另外,当网络流量很大时,整形器或调度器可能会用光分配给它们的内存,这时候整形器和调度器也会开始丢弃数
据包。
4.7. 句柄
每个 class 和分类的 qdisc (参考 第 7 节 “分类排队规则(分类 qdisc )”)都需要一个唯一的标识符。标识符
就是句柄。标识符由一个主编号和一个子编号组成,编号必须是数字。用户可以自由指定编号,但必须遵守以下规
则: [7]
分类器和排队规则句柄编号规则
这个编号对内核来说没有意义。用户可以随意指定一个数字。但是,在流量控制结构中,一个对象下所有子对象的
主编号必须相同。另外,按照惯例, root qdisc 的主编号通常应指定为1。
排队规则的子编号必须为0,而分类器的子编号必须是非0值。一个对象下的所有分类器必须有相同的子编号。
主
编
号
子
编
号
特别地,
qdisc 的编号总是 ffff:0。
The handle is used as the target in classid and 在使用 tc filter 时,句柄将会作为 classid 和 flowid 的参数
值,以指定操作对象。句柄仅供用户使用,内核会使用自己的一套编号来管理流量控制组件对象。
[5] 一个排队规则只能包含与其类型相同的类。举例来说,HTB 排队规则只能包含 HTB 分类,CBQ 排队规则不能
将 HTB 分类作为自己的类。
[6] 想要实现这种功能,你需要一个 filter 和一个 classifier ,使用分类器匹配特定类型的数据包,最后关
联上一个 policer ,并将决策器的操作指定为“丢包”,就像下面这样:police rate 1bps burst 1 action
drop/drop。
[7]我不知道编号数字的范围是多大,但我估计其范围应该是一个无符号32位整数(编号以十六进制表示)。
4.7. 句柄
每个 class 和分类的 qdisc (参考 第 7 节 “分类排队规则(分类 qdisc )”)都需要一个唯一的标识符。标识符
就是句柄。标识符由一个主编号和一个子编号组成,编号必须是数字。用户可以自由指定编号,但必须遵守以下规
则:
5. 软件和工具
5.1 内核版本要求
大多数发行版的内核都带有 Linux 流量控制系统(即 QoS ),一些发行版将其编译为内核模块,令一些发行版则
是直接将其静态编译到内核内。用户自己编译或一些自定义的内核可能没有包含流量控制模块。若要内核支持流量
控制,需要在编译内核的时候选择下文列出的编译选项。
建议没有内核编译经验的用户先阅读 Kernel HOWTO。对于有经验的用户,在对流量控制系统有一定了解的情况
下,可以根据自己的需求自行判断以下列出的选项哪些是自己所需要的。
例 1. 内核编译选项
#
# QoS and/or fair queueing
# 服务质量和公平队列
# 译注:下面的选项中的缩写的意义如下:
# SCH,SCHED:调度
# CLS:分类器
# CBQ:类基队列
# HTB:分层令牌桶
# CSZ:Clark-Shenker-Zhang 调度算法,这是一种比较复杂的调度算法,以这三位开发者的名字命名。
# 个人用户一般用不到,本文也没有详细介绍
# PRIO:优先级队列
# RED:早期随机丢包
# SFQ:随机公平队列
# TBF:令牌桶
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=m
CONFIG_NET_SCH_HTB=m
CONFIG_NET_SCH_CSZ=m
i
n
g
r
e
s
s