带宽管理的队列规定

 带宽管理的队列规定

标签:Iptables Netfilter

在Linux中系统中拥有强大的带宽管理功能,甚至比帧中继和ATM 还要优秀。在管理带宽时主要通过tc命令实现。

一、解释队列和队列规定

通过队列,我们可以决定数据被发送的方式。但是,我们 只能对发送数据进行整形,而不能对进入系统的数据进行流量整形
根据 Internet 的工作方式,我们无法直接控制别人向我们发送什么数据。有点象我们家里的信报箱,你不可能控制全世界,联系每一个人,修改别人对你发送邮件的数量。
然而,Internet 主要依靠 TCP/IP,它的一些特性很有用。因为TCP/IP没办法知道两个主机之间的网络容量,所以它会试图越来越快地发送数据(所谓的“慢起技术”)  ,当因为网络容量不够而开始丢失数据时,再放慢速度。实际情况要比这种方法更聪明,我们以后再讨论。
这就象当你尚未读完一半邮件时,希望别人停止给你寄信。与现实世界不同,在Internet 上可以做到这一点。(译注:这个例子并不恰当,TCP/IP的这种机制并不是在网络层实现的,而是要靠传输层的TCP协议)
如果你有一个路由器,并且希望能够防止某些主机下载速度太快,你需要在你路由器的内网卡,也就是向你的网内主机发送数据包的网卡上进行流量整形。
你还要 保证你正在控制的是瓶颈环节。如果你有一个100M 以太网卡,而你的路由器的链路速度是 256k,你必须保证你发送的数据量没有超过路由器的处理能力。否则,就是路由器在控制链路和对带宽进行整形,而不是你。可以说,我们需要 在链路中最慢的位置进行流量整形。幸运的是这很容易。

 二、 简单的无类队列规定

如前所述,利用队列,我们决定了数据被发送的方式。无类队列规定就是那样,能够接受数据和重新编排、延迟或丢弃数据包。
可以用作对于整个网卡的流量进行整形,而不细分各种情况。在我们进一步学习分类的队列规定之前,理解这部分是必不可少的!
最广泛应用的规定是 pfifo_fast 队列规定,因为它是缺省配置。这也解释了为什么其它那些复杂的功能为何如此健壮,因为那些都与缺省配置相似,只不过是其他类型的队列而已。
每种队列都有它们各自的优势和弱点。 
1、 pfifo_fast
这个队列的特点就象它的名字先进先出(FIFO),也就是说没有任何数据包被特殊对待。至少不是非常特殊。这个队列有3个所谓的“频道”。FIFO规则应用于每一个频道。并且: 如果在0频道有数据包等待发送,1频道的包就不会被处理,1 频道和2频道之间的关系也是如此
内核遵照数据包的 TOS标记,把带有“最小延迟”标记的包放进0频道,不要把这个无类的简单队列规定与分类的PRIO相混淆!虽然它们的行为有些类似,但对于无类的 pfifo_fast 而言,你 不能使用 tc 命令向其中添加其它的队列规定
参数与使用:
pfifo_fast 队列规定作为硬性的缺省设置,你不能对它进行配置。它缺省是这样配置的: 
priomap:
内核规定,根据数据包的优先权情况,对应相应的频道
。这个对应是根据数据包的TOS字节进行的。TOS看上去是这样的: 
 
TOS字段的4个bit 是如下定义的:
二进制   十进制  意义
-----------------------------------------
1000     8       最小延迟 (md)
0100     4       最大 throughput (mt)
0010     2       最大可靠性 (mr)
0001     1       最小成本 (mmc)
0000     0       正常服务
因为在这 4bit 的后面还有一个 bit,所以 TOS 字段的实际值是上述值的 2倍。(Tcpdump -v -v 可以让你看到整个TOS 字段的情况,而不仅仅是这4个bit)也就是你在下表的第一列看到的值:
TOS     Bits  意义                     Linux 优先权      频道
------------------------------------------------------------
0x0     0     正常服务                 0 最好效果        1
0x2     1     最小成本(mmc)            1 填充            2
0x4     2     最大可靠性(mr)           0 最好效果        1
0x6     3     mmc+mr                   0 最好效果        1
0x8     4     最大吞吐量(mt)            2 大量传输        2
0xa     5     mmc+mt                   2 大量传输        2
0xc     6     mr+mt                    2 大量传输        2
0xe     7     mmc+mr+mt                2 大量传输        2
0x10    8     最小延迟(md)              6 交互            0
0x12    9     mmc+md                   6 交互            0
0x14    10    mr+md                    6 交互            0
0x16    11    mmc+mr+md                6 交互            0
0x18    12    mt+md                    4 交互+大量传输   1
0x1a    13    mmc+mt+md                4 交互+大量传输   1
0x1c    14    mr+mt+md                 4 交互+大量传输   1
0x1e    15    mmc+mr+mt+md             4 交互+大量传输   1
很多的数字。第二列写着与 4个TOS位相关的数值,接着是它们的意义。
比如,15 表示一个数据包要求最小成本、最大可靠性、最大吞吐量和最
小延迟。我想称之为“人代会车队”。[译者按:原作为‘荷兰数据包’]
第四列写出了 Linux 内核对于 TOS 位的理解,并表明了它们对应哪种优
先权。
最后一列表明缺省的权限图。在命令行里,缺省的权限图应该是:
1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1
也就是说,比如优先权4将被映射到1频道。权限图允许你列出更高的优
先权值(>7),它们不对应 TOS映射,但是有其它的意图。
下表来自RFC 1349,告诉你应用程序可能如何设置它们的 TOS:  
   
txqueuelen:  
队列的长度,来自网卡的配置
,你可以用ifconfig和ip命令修改。如设置队
列长度为10,执行:ifconfig eth0 txqueuelen 10
你不能用tc 命令设置这个。
总结:pfifo_fast是内核系统默认使用的队列,内核按照数据包的TOS将其分配至三个不同的频道中,系统将首先处理完0频道的数据才能再处理1频道的数据,其有priomap和txqueuelen两个参数,且不能通过tc命令进行调节。

2、令牌桶过滤器(TBF)

令牌桶过滤器(TBF)是一个简单的队列规定: 只允许以不超过事先设定的速率到
来的数据包通过,但可能允许短暂突发流量朝过设定值。

TBF 很精确,对于网络和处理器的影响都很小。所以如果您 想对一个网卡限速,
令牌桶过滤器(TBF)是您的第一选择。

TBF的实现在于一个缓冲器(桶),不断地被一些叫做“令牌”的虚拟数据以特定
速率填充着。 (token rate)。桶最重要的参数就是它的大小,也就是它能够存储
令牌的数量(TBF模拟了一个桶,桶中装有令牌,令牌将从数据队列中收集数据包,然后从桶中删除)。
每个到来的令牌从数据队列中收集一个数据包,然后从桶中被删除。这个算法关
联到两个流上,令牌流和数据流,于是我们得到3种情景:
数据流以等于令牌流的速率到达TBF。这种情况下,每个到来的数据包都能对应一个令牌,然后无延迟地通过队列。 
数据流以小于令牌流的速度到达TBF。通过队列的数据包只消耗了一部分令牌,剩下的令牌会在桶里积累下来,直到桶被装满。剩下的令牌可以在需要以高于令牌流速率发送数据流的时候消耗掉,这种情况下会发生突发传输。 
数据流以大于令牌流的速率到达TBF。这意味着桶里的令牌很快就会被耗尽。导致 TBF中断一段时间,称为“越限”。如果数据包持续到来,将发生丢包。

最后一种情景非常重要,因为它可以用来对数据通过过滤器的速率进行整形。
令牌的积累可以导致越限的数据进行短时间的突发传输而不必丢包,但是持续越
限的话会导致传输延迟直至丢包。
请注意,实际的实现是针对数据的字节数进行的,而不是针对数据包进行的。
参数与使用
即使如此,你还是可能需要进行修改,TBF提供了一些可调控的参数。第一个参
数永远可用: 
limit/latency 
limit 确定最多有多少数据(字节数)在队列中等待可用令牌。你也可以通过设置 latency 参数来指定这个参数, latency 参数确定了一个包在 TBF中等待传输的最长等待时间。后者计算决定桶的大小、速率和峰值速率。 
burst/buffer/maxburst 
桶的大小,以字节计。这个 参数指定了最多可以有多少个令牌能够即刻被使用。通常,管理的带宽越大,需要的缓冲器就越大。在 Intel 体系上,10兆bit/s的速率需要至少10k字节的缓冲区才能达到期望的速率。 如果你的缓冲区太小,就会导致到达的令牌没有地方放(桶满了),这会导致潜在的丢包。
mpu
一个零长度的包并不是不耗费带宽。比如以太网,数据帧不会小于 64 字节。Mpu(Minimum Packet Unit,最小分组单位)决定了令牌的最低消耗。
rate: 
速度操纵杆
。参见上面的limits!
如果桶里存在令牌而且允许没有令牌,相当于不限制速率(缺省情况)。If the
bucket contains tokens and is allowed to empty, by default it does so at infinite speed.
如果不希望这样,可以调整入下参数:
peakrate:
如果有可用的令牌,数据包一旦到来就会立刻被发送出去,就象光速一样。那可能并不是你希望的,特别是你有一个比较大的桶的时候。峰值速率可以用来指定令牌以多块的速度被删除。用书面语言来说,就是: 释放一个数据包,但后等待足够的时间后再释放下一个。我们通过计算等待时间来控制峰值速率,然而,由于UNIX定时器的分辨率是 10毫秒, 如果平均包长10k bit,我们的峰值速率被限制在了1Mbps
mtu/minburst :
但是如果你的常规速率比较高,1Mbps 的峰值速率对我们就没有什么价值。要实现更高的峰值速率,可以在一个时钟周期内发送多个数据包。最有效的办法就是:再创建一个令牌桶,这第二个令牌桶缺省情况下为一个单个的数据包,并非一个真正的桶。
要计算峰值速率,用 mtu 乘以 100 就行了。 (应该说是乘以 HZ 数,Intel体系上是100,Alpha 体系上是1024)
配置范例:
这是一个非常简单而实用的例子:  
tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540
tc qdisc del dev ppp0 root tbf
为什么它很实用呢?如果你有一个队列较长的网络设备,比如 DSL modem或者
cable modem 什么的,并通过一个快速设备(如以太网卡)与之相连,你会发现上
 载数据绝对会破坏交互性。
这是因为上载数据会充满 modem 的队列,而这个队列为了改善上载数据的吞吐
量而设置的特别大。但这并不是你需要的,你可能为了提高交互性而需要一个不
太大的队列。也就是说你希望在发送数据的时候干点别的事情。
上面的一行命令并非直接影响了 modem 中的队列,而是通过控制 Linux 中的队
列而放慢了发送数据的速度。
把220kbit修改为你实际的上载速度再减去几个百分点。如果你的 modem确实很
快,就把“burst”值提高一点。

3、随机公平队列(SFQ)

SFQ(Stochastic Fairness Queueing,随机公平队列)是公平队列算法家族中的一个简单实现。它的精确性不如其它的方法,但是它在实现高度公平的同时,需要的计算量却很少。
SFQ 的关键词是“会话”(或称作“流”)  ,主要针对一个 TCP 会话或者 UDP流。流量被分成相当多数量的 FIFO队列中,每个队列对应一个会话。数据按照简单轮转的方式发送, 每个会话都按顺序得到发送机会。
SFQ将TCP和UDP流量平均的分配至多个FIFO队列中,每个队列对应一个或多个会话,然后按照轮询的方法依次从每个FIFO队列中取数据发送。保证数据发送的公平性。 
这种方式非常公平,保证了每一个会话都不会没其它会话所淹没。SFQ之所以被称为“随机”,是因为 它并不是真的为每一个会话创建一个队列,而是使用一个散列算法,把所有的会话映射到有限的几个队列中去。 
因为使用了散列,所以可能多个会话分配在同一个队列里,从而需要共享发包的
机会,也就是共享带宽。为了不让这种效应太明显,SFQ会频繁地改变散列算法,
以便把这种效应控制在几秒钟之内

有很重要的一点需要声明: 只有当你的出口网卡确实已经挤满了的时候,SFQ才
会起作用
!否则在你的Linux机器中根本就不会有队列,SFQ也就不会起作用。
稍后我们会描述如何把 SFQ 与其它的队列规定结合在一起,以保证两种情况下
都比较好的结果。
特别地,在你使用 DSL modem或者 cable modem的以太网卡上设置 SFQ而不进
行任何进一步地流量整形是无谋的!
参数与使用:
SFQ基本上不需要手工调整:
perturb
多少秒后重新配置一次散列算法。如果取消设置,散列算法将永远不会重
新配置(不建议这样做)。10秒应该是一个合适的值。

quantum: 
 一个流至少要传输多少字节后才切换到下一个队列。却省设置为一个最大
包的长度(MTU的大小)。
不要设置这个数值低于MTU!
配置范例:
如果你有一个网卡,它的链路速度与实际可用速率一致,比如一个电话
MODEM,如下配置可以提高公平性:
# tc qdisc add dev ppp0 root sfq perturb 10
# tc -s -d qdisc ls

qdisc sfq 800c: dev ppp0 quantum 1514b limit 128p flows 128/1024 perturb 10sec 
 Sent 4812 bytes 62 pkts (dropped 0, overlimits 0)
“800c:”这个号码是系统自动分配的一个句柄号,“limit”意思是这个队列中可
以有 128 个数据包排队等待。一共可以有 1024 个散列目标可以用于速率审计,
而其中128个可以同时激活。(no more packets fit in the queue!)每隔10秒种散列
算法更换一次。

三、关于什么时候用哪种队列的建议

总之,我们有几种简单的队列,分别使用排序、限速和丢包等手段来进行流量整
形(限速)。
如果想单纯地降低出口速率,使用令牌桶过滤器。调整桶的配置后可用于
控制很高的带宽。
如果你的链路已经塞满了,而你想保证不会有某一个会话独占出口带宽,
使用随机公平队列

如果你有一个很大的骨干带宽,并且了解了相关技术后,可以考虑前向随
机丢包(参见“高级”那一章)。
如果希望 对入口流量进行“整形”(不是转发流量),可使用入口流量策略,
注意
,这不是真正的“整形”。
如果你正在转发数据包,在数据流出的网卡上应用TBF。除非你希望让数
据包从多个网卡流出,也就是说入口网卡起决定性作用的时候,还是使用
入口策略。
如果你 并不希望进行流量整形,只是想看看你的网卡是否有比较高的负载
而需要使用队列,使用 pfifo队列(不是 pfifo_fast)。它缺乏内部频道但是
可以统计backlog

最后,你可以进行所谓的“社交整形”。你不能通过技术手段解决一切问
题。用户的经验技巧永远是不友善的。正确而友好的措辞可能帮助你的正
确地分配带宽。

四、相关术语

关于这些词语的严格定义请参考这个文档。 
[url]http://www.ietf.org/internet-drafts/draft-ietf-diffserv-model-06.txt[/url]
队列规定:
管理设备输入(ingress)或输出(egress)的一个算法。
无类的队列规定: 
一个内部不包含可配置子类的队列规定。 
分类的队列规定 :
一个分类的队列规定内可一包含更多的类。其中每个类又进一步地包含一
个队列规定,这个队列规定可以是分类的,也可以是无类的。根据这个定
义,严格地说pfifo_fast 算是分类的,因为它实际上包含3个频道(实际上
可以认为是子类)。然而从用户的角度来看它是无类的,因为其内部的子
类无法用tc 工具进行配置。 
类:
一个分类的队列规定可以拥有很多类,类内包含队列规定。
分类器: 
每个分类的队列规定都需要决定什么样的包使用什么类进行发送。分类器
就是做这个用的。
过滤器 :
分类是通过过滤器完成的。一个过滤器包含若干的匹配条件,如果符合匹
配条件,就按此过滤器分类。

调度(队列调度):
在分类器的帮助下,一个队列规定可以裁定某些数据包可以排在其他数据
包之前发送。这种处理叫做“调度”,
比如此前提到的pfifo_fast 就是这样
的。调度也可以叫做“重排序”,但这样容易混乱。
整形(流量整形/限速):
在一个数据包发送之前进行适当的延迟,以免超过事先规定好的最大速
率,这种处理叫做“整形”。
整形在 egress 处进行。习惯上,通过丢包来
降速也经常被称为整形。 
策略 :
通过延迟或是丢弃数据包来保证流量不超过事先规定的带宽
。在 Linux,
里,策略总是规定丢弃数据包而不是延迟。即,不存在ingress 队列。
Work-Conserving :
对于一个 work-conserving 队列规定, 如果得到一个数据包,它总是立刻
对它进行分发
。换句话说,只要网卡(egress 队列规定)允许,它就不会延
迟数据包的发送。
non-Work-Conserving 
有些队列,比如令牌桶过滤器, 可能需要暂时停止发包以实现限制
带宽
。也就是说它们有时候即使有数据包需要处理,也可能拒绝发送。
现在我们简单了解了一些术语,让我们看看他们的位置: 
              
整个大方框表示内核。最左面的箭头表示从网络上进入机器的数据包。它们进入
Ingress 队列规定,并有可能被某些过滤器丢弃。即所谓策略。
这些是很早就发生的(在进入内核更深的部分之前)。这样早地丢弃数据有利于
节省CPU时间。
数据包顺利通过的话,如果它是发往本地进程的,就会进入 IP 协议栈处理并提
交给该进程。如果它需要转发而不是进入本地进程,就会发往egress。本地进程
也可以发送数据,交给 Egress 分类器。
然后经过审查,并放入若干队列规定中的一个进行排队。这个过程叫做“入队”。
在不进行任何配置的情况下,只有一个egress队列规定!a!apfifo_fa!!总是接
收数据包。
数据包进入队列后,就等待内核处理并通过某网卡发送。这个过程叫做“出队”。 
这张图仅仅表示了机器上只有一块网卡的情况,图中的箭头不能代表所有情况。
每块网卡都有它自己的ingress 和egress。

五、分类的队列规定

如果你有多种数据流需要进行区别对待,分类的队列规定就非常有用了。众多分
类的队列规定中的一种,CBQ(Class Based Queuei,基于类的队列),经常
被提起,以至于造成大家认为CBQ就是鉴别队列是否分类的标准,这是不对的。
CBQ 不过是家族中最大的孩子而已,同时也是最复杂的。它并不能为你做所有
的事情。对于某些人而言这有些不可思议,因为他们受“sendmail 效应”影响较
深,总是认为只要是复杂的并且没有文档的技术肯定是最好的。
分类的队列规定及其类中的数据流向:
一旦数据包进入一个分类的队列规定,它就得被送到某一个类中,也就是需要
分类。 对数据包进行分类的工具是过滤器。一定要记住:“分类器”是从队列规
定内部调用的,而不是从别处。
过滤器会返回一个决定,队列规定就根据这个决定把数据包送入相应的类进行排
队。每个子类都可以再次使用它们的过滤器进行进一步的分类。直到不需要进一
步分类时,数据包才进入该类包含的队列规定排队。
除了能够包含其它队列规定之外,绝大多数分类的队列规定能够流量整形。这
对于需要同时进行调度(如使用SFQ)和流量控制的场合非常有用。如果你用一个
高速网卡(比如以太网卡)连接一个低速设备(比如cable modem或者ADSL modem)
时,也可以应用。
如果你仅仅使用SFQ,那什么用也没有。因为数据包进、出路由器时没有任何延
迟。虽然你的输出网卡远远快于实际连接速率,但路由器中却没有队列可以调度。
队列规定家族:根、句柄、兄弟和父辈:
每块网卡都有一个出口“根队列规定”,缺省情况下是前面提到的pfifo_fast 队列规定。每个队列规定都指定一个句柄,以便以后的配置语句能够引用这个队列规定。除了出口队列规定之外,每块网卡还有一个入口,以便policies进入的数据流, 队列规定的句柄有两个部分:一个主号码和一个次号码。习惯上把根队列规定称为“1:”,等价于“1:0”。队列规定的次号码永远是0, 类的主号码必须与它们父辈的主号码一致。 
如何用过滤器进行分类:
下图给出一个典型的分层关系:  
 
不要误解这张图!你千万不要想象内核处在树的顶点而下面部分是网络。数据包
是在根队列规定处入队和出队的,而内核只同根打交道。 
一个数据包可能是按照下面这个链状流程进行分类的:
1: -> 1:1 -> 12: -> 12:2
数据包现在应该处于12:2下属的某个队列规定中的某个队列中。在这个例子中,
树的每个节点都附带着一个过滤器,用来选择下一步进入哪个分支。这样比较直
。然而,这样也是允许的:
1: -> 12:2
也就是说, 根所附带的一个过滤器要求把数据包直接交给12:2
数据包如何出队并交给硬件:
当内核决定把一个数据包发给网卡的时候,根队列规定1:会得到一个出队请求,
然后把它传给1:1,然后依次传给10:、11:和12:,which each query their siblings,
然后试图从它们中进行 dequeue()操作。也就是说,内和需要遍历整颗树,因为
只有12:2中才有这个数据包。 
换句话说,类及其兄弟仅仅与其“父队列规定”进行交谈,而不会与网卡进行交
谈。只有根队列规定才能由内核进行出队操作!
更进一步,任何类的出队操作都不会比它们的父类更快。这恰恰是你所需要的:
我们可以把 SFQ 作为一个子类,放到一个可以进行流量整形的父类中,从而能
够同时得到SFQ的调度功能和其父类的流量整形功能。

一、 PRIO队列规定

PRIO 队列规定并不进行整形,它仅仅根据你配置的过滤器把流量进一步细分。
你可以认为 PRIO 队列规定是 pfifo_fast 的一种衍生物,区别在每个频道都是一
个单独的类,而非简单的FIFO。

当数据包进入PRIO队列规定后,将根据你给定的过滤器设置选择一个类。缺省
情况下有三个类,这些类仅包含纯FIFO队列规定而没有更多的内部结构。你可
以把它们替换成你需要的任何队列规定。

每当有一个数据包需要出队时,首先处理:1类。只有当标号更小的类中没有需要
处理的包时,才会标号大的类。
 
当你希望不仅仅依靠包的 TOS,而是想使用 tc 所提供的更强大的功能来进行数
据包的优先权划分时,可以使用这个队列规定
。它也可以包含更多的队列规定,
而pfifo_fast 却只能包含简单的fifo队列规定。
因为它不进行整形,所以使用时与 SFQ 有相同的考虑:要么确保这个网卡的带
宽确实已经占满,要么把它包含在一个能够整形的分类的队列规定的内部
。后者
几乎涵盖了所有cable modems 和DSL设备。
严格地说,PRIO队列规定是一种Work-Conserving 调度。

1、PRIO的参数与使用

tc 识别下列参数: 
bands:
创建频道的数目
。每个频道实际上就是一个类。如果你修改了这个数值,你必须同时修改:
priomap:
如果你不给 tc 提供任何过滤器,PRIO 队列规定将参考 TC_PRIO 的优先级来决定如何给数据包入队
,它的行为就像前面提到过的pfifo_fast 队列规定,关于细节参考前面章节。
频道是类,缺省情况下命名为主标号:1 到主标号:3。如果你的 PRIO 队列规定是12:,把数据包过滤到 12:1将得到最高优先级。
注意:0频道的次标号是1,1频道的次标号是2,以此类推。

2、 配置范例

现在想创建下图中的类树:  
    
创建三个类1:1, 1:2, 1:3分别对应频道0,1,2(这里PRIO有一个并不严重的BUG,即类是从1开始的,而频道却是从0开始的,不是很对应)。
大批量数据使用30:,交互数据使用20:或10:。
命令如下:
# tc qdisc add dev eth0 root handle 1: prio 
## 这个命令立即创建了类: 1:1, 1:2, 1:3
  
# tc qdisc add dev eth0 parent 1:1 handle 10: sfq
# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000
# tc qdisc add dev eth0 parent 1:3 handle 30: sfq     
注: limit 确定最多有多少数据(字节数)在队列中等待可用令牌。buffer定义桶的大小,即指定了最多可以有多少个令牌能够即刻被使用 。handle参数用于定义句柄,意思尚不清楚。
以上配置指明内核将TOS最好的流量放入类1(频道0),并调用sfq作排队公平传输,次之的数据放入类型2并调用tbf将速度限制在20kbit,交互式数据放入类型3中并调用sfq作排队公平传输。                       
现在,我们看看结果如何: 
# tc -s qdisc ls dev eth0 
qdisc sfq 30: quantum 1514b 
Sent 0 bytes 0 pkts (dropped 0, overlimits 0)  
 
qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms 
 Sent 0 bytes 0 pkts (dropped 0, overlimits 0)  
 
qdisc sfq 10: quantum 1514b 
Sent 132 bytes 2 pkts (dropped 0, overlimits 0)  
 
qdisc prio 1: bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Sent 174 bytes 3 pkts (dropped 0, overlimits 0) 
如你所见,0频道已经有了一些流量,运行这个命令之后发送了一个包!
现在我们来点大批量数据传输(使用能够正确设置TOS标记的工具):
# scp tc ahu@10.0.0.11:./
ahu@10.0.0.11's password: 
tc                   100% |*****************************|   353 KB    00:00    
# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b 
 Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) 
 
 qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms 
 Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) 
 
 qdisc sfq 10: quantum 1514b 
 Sent 2230 bytes 31 pkts (dropped 0, overlimits 0) 
 
 qdisc prio 1: bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 389140 bytes 326 pkts (dropped 0, overlimits 0) 
如你所见,所有的流量都是经过 30:处理的,优先权最低。现在我们验证一下交
互数据传输经过更高优先级的频道,我们生成一些交互数据传输: 
# tc -s qdisc ls dev eth0
qdisc sfq 30: quantum 1514b 
 Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) 
 
 qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms 
 Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) 
 
 qdisc sfq 10: quantum 1514b 
 Sent 14926 bytes 193 pkts (dropped 0, overlimits 0) 
 
 qdisc prio 1: bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 401836 bytes 488 pkts (dropped 0, overlimits 0) 
正常,所有额外的流量都是经 10:这个更高优先级的队列规定处理的。与先前
的整个 scp 不同,没有数据经过最低优先级的队列规定。
总结:FRIO与pfifo_fast工作机制类似,只不过FRIO是分类队列,可以通过tc命令进行设置(如创建类(频道)数目,使用何种过滤器等),而pfifo_fast则为无类队列,不能通过tc命令进行配置,且为默认队列,可以利用FRIO的分类机制对流量进行细分,然后则不同类型的流量放入不同的类中,流量将按照类的小大顺序进行传输,另外,其不能够进行流量整形,所以经常在每一个子类中加入整形队列。
 
 
二、著名的 CBQ队列规定

如前所述,CBQ 是最复杂、最琐碎、最难以理解、最刁钻的队列规定。这并不
是因为其作者的恶毒或者不称职,而是因为 CBQ 算法本身的不精确,而且与
Linux的内在机制不协调造成的。 
除了可以分类之外,CBQ 也是一个整形器,但是从表面上看来工作得并不好。
它应该是这样的:如果你试图把一个10Mbps 的连接整形成1Mbps 的速率,就应
该让链路 90%的时间处于闲置状态,必要的话我们就强制,以保证 90%的闲置
时间。
但闲置时间的测量非常困难,所以 CBQ 就采用了它一个近似值,来自硬件层
的两个传输请求之间的毫秒数来代替它。这个参数可以近似地表征这个链路
的繁忙程度。
这样做相当慎重,而且不一定能够得到正确的结论。比如,由于驱动程序方面或
者其它原因造成一块网卡的实际传输速率不能够达到它的标称速率,该怎么办?
由于总线设计的原因,PCMCIA网卡永远也不会达到 100Mbps。那么我们该怎么
计算闲置时间呢?
如果我们引入非物理网卡像 PPPoE、PPTP!a情况会变得更糟糕。因为相
当一部分有效带宽耗费在了链路维护上。
那些实现了测量的人们都发现 CBQ 总不是非常精确甚至完全失去了其本来意
义。
但是,在很多场合下它还是能够很好地工作。根据下面的文档,你应该能够较好
地配置CBQ来解决答多数问题。

1、CBQ整形的细节

如前所述,CBQ 的工作机制是确认链路的闲置时间足够长,以达到降低链路实
际带宽的目的。为此,它要计算两个数据包的平均发送间隔。 
操作期间,有效闲置时间的测量使用EWMA(exponential weighted moving average,
指数加权移动均值)算法,也就是说最近处理的数据包的权值比以前的数据包按
指数增加。UNIX的平均负载也是这样算出来的。
计算出来的平均时间值减去 EWMA测量值,得出的结果叫做“avgidle”。 最佳的
链路负载情况下,这个值应当是0:数据包严格按照计算出来的时间间隔到来。 
在一个过载的链路上,avgidle 值应当是负的。如果这个负值太严重,CBQ 就会
暂时禁止发包,称为“overlimit”(越限)。
相反地,一个闲置的链路应该有很大的 avgidle 值,这样闲置几个小时后,会造成
链路允许非常大的带宽通过。为了避免这种局面,我们用maxidle 来限制avgidle
的值不能太大。
理论上讲,如果发生越限,CBQ 就会禁止发包一段时间(长度就是事先计算出来
的传输数据包之间的时间间隔),然后通过一个数据包后再次禁止发包。但是最
好参照一下下面的 minburst 参数。
 
2、配置参数

下面是配置整形时需要指定的一些参数:
avpkt :
平均包大小,以字节计。计算 maxidle 时需要,maxidle从maxburst 得出。 
bandwidth :
网卡的物理带宽,用来计算闲置时间。
cell :
一个数据包被发送出去的时间可以是基于包长度而阶梯增长的。一个800
字节的包和一个806字节的包可以认为耗费相同的时间。也就是说它设置
时间粒度。通常设置为8,必须是2的整数次幂。
maxburst:
这个参数的值决定了计算 maxidle 所使用的数据包的个数。在 avgidle 跌
落到0之前,这么多的数据包可以突发传输出去。这个值越高,越能够容
纳突发传输
。你无法直接设置 maxidle 的值,必须通过这个参数来控制。
minburst:
如前所述,发生越限时 CBQ 会禁止发包。实现这个的理想方案是根据事
先计算出的闲置时间进行延迟之后,发一个数据包。然而,UNIX的内核
一般来说都有一个固定的调度周期(一般不大于10ms),所以最好是这样:
禁止发包的时间稍长一些,然后突发性地传输 minburst 个数据包,而不是
一个一个地传输。等待的时间叫做offtime
。从大的时间尺度上说,minburst 值越大,整形越精确。但是,从毫秒级的时间尺度上说,就会有越多的突发传输。
minidle:
如果 avgidle 值降到0,也就是发生了越限,就需要等待,直到avgidle 的值足够大才发送数据包。为避免因关闭链路太久而引起的以外突发传输,在avgidle 的值太低的时候会被强制设置为minidle 的值,参数 minidle 的值是以负微秒记的。所以 10 代表 avgidle 被限制在-10us上。
mpu :
最小包尺寸,因为即使是 0长度的数据包,在以太网上也要生成封装成64 字节的帧,而需要一定时间去传输。为了精确计算闲置时间,CBQ 需要知道这个值。
rate :
期望中的传输速率。在 CBQ 的内部由很多的微调参数。比如,那些已知队列中没有数据的类就不参加计算、越限的类将被惩罚性地降低优先级等等。都非常巧妙和复杂。
除了使用上述idletime近似值进行整形之外,CBQ还可以象 PRIO 队列那样,把
各种类赋予不同的优先级,优先权数值小的类会比优先权值大的类被优先处理。
每当网卡请求把数据包发送到网络上时,都会开始一个 WRR(weighted round
robin,加权轮转)过程,从优先权值小的类开始。
那些队列中有数据的类就会被分组并被请求出队。在一个类收到允许若干字节数
据出队的请求之后,再尝试下一个相同优先权值的类。
下面是控制WRR过程的一些参数: 
allot:
当从外部请求一个 CBQ 发包的时候,它就会按照“priority”参数指定的
顺序轮流尝试其内部的每一个类的队列规定。 当轮到一个类发数据时,它
只能发送一定量的数据。“allot”参数就是这个量的基值
。更多细节请参
照“weight”参数。
prio:
CBQ可以象 PRIO设备那样工作。其中“prio”值较低的类只要有数据就
必须先服务,其他类要延后处理

weight 
“weight”参数控制 WRR 过程。每个类都轮流取得发包的机会。如果其
中一个类要求的带宽显著地高于其他的类,就应该让它每次比其他的类发
送更多的数据。
CBQ 会把一个类下面所有的 weight 值加起来后归一化,所以数值可以任
意定,只要保持比例合适就可以。人们常把“速率/10”作为参数的值来
使用,实际工作得很好。归一化值后的值乘以“allot”参数后,决定了每
次传输多少数据。
请注意,在一个CBQ内部所有的类都必须使用一致的主号码!

决定链路的共享和借用的 CBQ参数
除了纯粹地对某种数据流进行限速之外,CBQ 还可以指定哪些类可以向其它哪
些类借用或者出借一部分带宽。
Isolated/sharing: 
凡是使用“isolated”选项配置的类,就不会向其兄弟类出借带宽。如果你
的链路上同时存在着竞争对手或者不友好的其它人,你就可以使用这个选
项。 选项“sharing”是“isolated”的反义选项
bounded/borrow :
一个类也可以用“bounded”选项配置,意味着它不会向其兄弟类借用带
宽。选项“borrow”是“bounded”的反义选项。
 
 一个典型的情况就是你的一个链路上有多个客户都设置成了“isolated”和
“bounded”,那就是说他们都被限制在其要求的速率之下,且互相之间不会借用
带宽。 
在这样的一个类的内部的子类之间是可以互相借用带宽的。

3、 配置范例

这个配置把 WEB服务器的流量控制为 5Mbps、SMTP流量控制在3Mbps 上。而
且二者一共不得超过 6Mbps,互相之间允许借用带宽。我们的网卡是 100Mbps
的。
# tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit  avpkt 1000 cell 8
# tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit rate 6Mbit  \
weight 0.6Mbit prio 8 allot 1514 cell 8 maxburst 20  avpkt 1000 bounded
这部分按惯例设置了根为 1:0,并且绑定了类 1:1。也就是说整个带宽不能超过
6Mbps。
如前所述,CBQ 需要调整很多的参数。其实所有的参数上面都解释过了。相应
的HTB配置则要简明得多。
# tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit rate 5Mbit \
weight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20  avpkt 1000                       
# tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit rate 3Mbit \
weight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20  avpkt 1000
我们建立了 2 个类。注意我们如何 根据带宽来调整 weight 参数的。两个类都没有配置成“bounded”,但它们都连接到了类 1:1 上,而 1:1 设置了“bounded”。所以两个类的总带宽不会超过 6Mbps。别忘了,同一个 CBQ 下面的子类的主号码都必须与CBQ自己的号码相一致!
# tc qdisc add dev eth0 parent 1:3 handle 30: sfq
# tc qdisc add dev eth0 parent 1:4 handle 40: sfq

缺省情况下,两个类都有一个FIFO队列规定。但是我们把它换成SFQ队列,以保证每个数据流都公平对待。
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 80 0xffff flowid 1:3
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 25 0xffff flowid 1:4

这些命令规定了根上的过滤器,保证数据流被送到正确的队列规定中去。注意:我们先 使用了“tc class add” 在一个队列规定中创建了类,然后使用“tc qdisc add”在类中创建队列规定,使用tc filter  add创建过滤器
你可能想知道, 那些没有被那两条规则分类的数据流怎样处理了呢?从这个例子来说,它们被1:0直接处理,没有限制
如果 SMTP+web 的总带宽需求大于 6Mbps,那么这 6M 带宽将按照两个类的weight 参数的比例情况进行分割:WEB 服务器得到 5/8的带宽,SMTP 得到 3/8
的带宽。
从这个例子来说,你也可以这么认为: WEB 数据流总是会得到
5/8*6Mbps=3.75Mbps 的带宽。

4、其它 CBQ参数:split和 defmap

如前所述, 一个分类的队列规定需要调用过滤器来决定一个数据包应该发往哪个类去排队。除了调用过滤器,CBQ 还提供了其他方式,defmap 和 split。很难掌握,但好在无关大局。但是现在是解释defmap 和split 的最佳时机,我会尽力解释。
因为你经常是仅仅需要根据 TOS 来进行分类,所以提供了一种特殊的语法。当
CBQ需要决定了数据包要在哪里入队时,要检查这个节点是否为“split 节点”。
如果是,子队列规定中的一个应该指出它接收所有带有某种优先权值的数据包,
权值可以来自TOS字段或者应用程序设置的套接字选项。

数据包的优先权位与 defmap 字段的值进行“或”运算来决定是否存在这样的匹
。换句话说, 这是一个可以快捷创建仅仅匹配某种优先权值数据包的过滤器的
方法。如果 defmap 等于 0xff,就会匹配所有包,0 则是不匹配
。这个简单的配
置可以帮助理解:
# tc qdisc add dev eth1 root handle 1: cbq bandwidth 10Mbit allot 1514 cell 8 avpkt \
1000 mpu 64 
# tc class add dev eth1 parent 1:0 classid 1:1 cbq bandwidth 10Mbit  rate 10Mbit allot\  1514 cell 8 weight 1Mbit prio 8 maxburst 20  avpkt 1000
一个标准的CBQ前导。
Defmap参照TC_PRIO位(我从来不直接使用数字!):
TC_PRIO..                     Num  对应 TOS
-------------------------------------------------
BESTEFFORT                  0    最高可靠性        
FILLER                              1    最低成本          
BULK                                2    最大吞吐量(0x8)  
INTERACTIVE_BULK   4                        
INTERACTIVE                6    最小延迟(0x10)      
CONTROL                        7                        
TC_PRIO..的数值对应它右面的 bit。关于 TOS 位如何换算成优先权值的细节可
以参照pfifo_fast 有关章节。
然后是交互和大吞吐量的类:
# tc class add dev eth1 parent 1:1 classid 1:2 cbq bandwidth 10Mbit  rate 1Mbit allot \ 1514 cell 8 weight 100Kbit prio 3 maxburst 20 avpkt 1000 split 1:0 defmap c0
 
# tc class add dev eth1 parent 1:1 classid 1:3 cbq bandwidth 10Mbit rate 8Mbit allot \ 1514 cell 8 weight 800Kbit prio 7 maxburst 20 avpkt 1000 split 1:0 defmap 3f 

“split 队列规定”是1:0,也就是做出选择的地方。c0是二进制的11000000,3F
是00111111,所以它们共同匹配所有的数据包。第一个类匹配第 7和第6位,也
就是负责“交互”和“控制”的数据包。第二个类匹配其余的数据包。
节点1:0现在应该有了这样一个表格:
priority  send to
0  1:3
1  1:3
2  1:3
3  1:3
4  1:3
5  1:3
6  1:2
7  1:2
为了更有趣,你还可以传递一个“change 掩码”,确切地指出你想改变哪个优先
权值。你只有在使用了“tc class change”的时候才需要。比如,往1:2中添加best
effort数据流,应该执行:
# tc class change dev eth1 classid 1:2 cbq defmap 01/01
现在,1:0上的优先权分布应该是:
priority  send to
0  1:2
1  1:3
2  1:3
3  1:3
4  1:3
5  1:3
6  1:2
7  1:2
求助: 尚未测试过“tc class change”,资料上这么写的。
总结:通过split和defmap将不同优先级的数据发送到相应的队列中从而替代过滤器的使用。通过split调来队列规定,即在什么地方为数据做选择,defmap用于匹配流量优先级(总共8位,优先级为0-7),用二个16进制来表示,转化为二进制后,1表示匹配,0表示不匹配,如0xff表示匹配所有流量,c0表示匹配第六、七位。两个参数在tc class  add处使用,从而替代tc filter  add命令。

五、 HTB(Hierarchical Token Bucket, 分层的令牌桶)

Martin Devera (<devik>)正确地意识到CBQ太复杂,而且并没有按照多数常见情
况进行优化。他的 Hierarchical 能够很好地满足这样一种情况:你有一个固定速
率的链路,希望分割给多种不同的用途使用。为每种用途做出带宽承诺并实现定
量的带宽借用。
HTB就象 CBQ一样工作,但是并不靠计算闲置时间来整形。它是一个分类的令
能够找到很好的文档。
牌桶过滤器。它只有很少的参数,并且在它的网站
随着你的HTB配置越来越复杂,你的配置工作也会变得复杂。但是使用 CBQ 的
话,即使在很简单的情况下配置也会非常复杂!HTB3 (关于它的版本情况,请参
阅它的网站)已经成了官方内核的一部分(2.4.20-pre1、2.5.31 及其后)。然而,你
可能仍然要为你的tc命令打上HTB3 支持补丁,否则你的 tc 命令不理解HTB3。
如果你已经有了一个新版内核或者已经打了补丁,请尽量考虑使用HTB。 
 
1、配置范例

环境与要求与上述CBQ的例子一样。
# tc qdisc add dev eth0 root handle 1: htb default 30
 
# tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k
 
# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k
# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k
# tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k
作者建议2在那些类的下方放置SFQ:
# tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
# tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
# tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10
添加过滤器,直接把流量导向相应的类:
# U32="tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32"
# $U32 match ip dport 80 0xffff flowid 1:10
# $U32 match ip sport 25 0xffff flowid 1:20
这就完了!a没有没见过的或者没解释过的数字,没有不明意义的参数。
HTB 完成得相当不错!a如果 10:和 20:都得到了保证的速率,剩下的就是分割
了,它们借用的比率是5:3,正如你其网的那样。
未被分类的流量被送到了 30:,仅有一点点带宽,但是却可以任意借用剩下的带
宽。因为我们内部使用了SFQ,而可以公平发包。
 
 
英文原版:
http://blog.chinaunix.net/article.php?articleId=18225&blogId=224
HTB官方网站:
http://luxik.cdi.cz/~devik/qos/htb/

LINUX HTB队列规定是LINUX QOS 内容的部分, 主要是配合TC工具进行流量控制的一种算法, 和CBQ 比HTB有它自身的特点, 有关CBQ的资料相对比较多一些. 这是HTB网站上的一篇用户手册;

LINUX HTB队列规定用户指南

HTB Linux queuing discipline manual - user guide
Martin Devera aka devik (devik@cdi.cz)
Manual: devik and Don Cohen
Last updated: 5.5.2002
译者:龚关 gongguan008@163.com
1. Introduction 介绍
2. Link sharing 链路共享
3. Sharing hierarchy 共享层次
4. Rate ceiling 速率限度
5. Burst 突发
6. Priorizing bandwidth share 带宽分配优先
7. Understanding statistics 查看统计
8. Making, debugging and sending error reports 开发,调试和错误报告

1. Introduction 介绍

HTB 意味着是一个更好理解更容易掌握的可以快速替换LINUX CBQ 队列规定的队列, CBQ和HTB都可以帮助你限制你的链路上的出口带宽;他们允许你把一条物理链路模拟成几条更慢的链路或者是把发出的不同类型的流量模拟成不同的连接,在他们的实际应用中, 你必须指定怎么分配物理链路给各种不同的带宽应用并且如何判断每种不同的应用的数据包是怎么样被发送的;

这篇文章将告诉你怎么使用 HTB . 这里有很多的例子和分析图以及一些特殊问题的讨论.
这个HTB的发布版本已经有更多的升级, 详细情况请看HTB 的主页.
请先查阅: TC 工具 (不仅仅是HTB ) 有关速率的单位换算: kbps = kilo bytes kbit = kilo bits


2. Link sharing 链路共享

案例: 我们有两不同的用户A和B, 都通过网卡 eth0 连接到 internet ,我们想分配 60 kbps的带宽给B 和 40 kbps的带宽给A, 接下来我们想对A用户的带宽再做一个分配, 给30 kbps的带宽给WWW服务, 10 kbps的带宽给其他用途; 任何没有用的带宽可以分配和其他需要带宽类 (在分配的总带宽允许的范围内的部分)

HTB 可以保障提供给每个类带宽的数量是它所需求的最小需求或者等于分配给它的数量.当一个类需要的带宽少于分配的带宽时,剩余的带宽被分配给其他需要服务的类.

注: 这里这种情况被称为”借用”剩余带宽, 我们以后将用这个术语, 但无论如何,好像很不好因为这个”借用”是没有义务偿还的.



上面所提到的不同类型的流量在HTB里表现为类, 上面是一个简单的分布图.

我们来看看所用到的命令:

tc qdisc add dev eth0 root handle 1: htb default 12
这条命令分配了HTB队列规定给 eth0 并且指定了一个名称为(handle" 1:) 句柄 1: , 这个名称用于标识它下面的子类, default 12 的意思是没有被分类的流量被分配到类 1:12

注: 一般 (不仅仅是HTB其他所有TC的队列和类),句柄都被写成 X:Y 这里X是队列规定的整数型的标识符,Y是这个队列规定的类的整数型标识符,队列规定的句柄标识符的Y必须是0,而句柄的类的标识符的数值必须是一个非零的整数. "1:" 等同于 "1:0".

tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps

第一行在队列1:下创建了一个根类1:1,并且定义了HTB队列规定作为这个根类的父类,一个根类可以像队列规定下其他类一样允许它的子类相互借用带宽,但是根类之间不能相互借用带宽,我们可以在HTB队列下直接创建三个类,但是其中一个类的剩余带宽不能够借用给其他需要的类,在这种情况下我们想允许带宽借用,所以我们为根类创建扩展类并且让这些类在根类的范围内发送数据,我们定义了下面的三行, ceil参数我们在以后将讲述.

注: 有时候人们会问我为什么他们必须重复dev eth0 描述在他们已经定义并使用了handle 或者parent ,原因是本地接口比如eth0 和eth1他们各自都可能会有类的句柄表示为1:1.

我们还必须描述那些数据包属于那个类, 有关详细内容请查看有关TC 过虑器的相关文档. 命令看起来像下面这样:

tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 flowid 1:11
(We identify A by its IP address which we imagine here to be 1.2.3.4.)

(我们根据它的IP地址进行分类识别,这里我们假设为1.2.3.4)

注:U32过虑器有一个非文档化的设计会导致在你使用U32分类器”tc filter show”命令时会显示出不同的prio的值.


你可能会注意到我们没有为类1:12创建过虑, 很明显,这个例子中被用作缺省, 就是没有被上面两条分类规则定义的任何数据包 (任何原地址非1.2.3.4)将被发送到类1:12

现在我们可以很方便的为队列规定分配子类, 如果没有指定缺省是pfifo

tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 5
tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 5
tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10



这是我们所需要的全部命令, 让我们来看看有什么事情发如果我们给每个类发送90kbps的数据然后停止发送其中一个类在某一时刻.在图的底部标有"0:90k". 标签中央水平位置 (在9附近.同时标有红色1)现实了数据流量随时间的变化情况.冒号之前是类的标识符;(0表示类1:10, 1 表示类 1:11, 2 表示类 1:12)冒号之后是新的速率在那个时间有标注. 比如类0在时间0的时候速率改变为90K ;在时间3为0K (0= 0k) ,在时间6时返回90K;

最初所有的类共同流量为90kb. 以后以被指定更高的速度传输, 每一个类都被限制在其被指定的速率, 在时间3的时候我们停止传送类0的数据, 分配给类0的速率被分配给其他两个类.如图所示1至6内的类1和类2 (很难看出类1的增长因为它只有4kbps.) 同样在时间9时类1流量停止它的带宽被分配到另外两个类(类0的增长同样很难看出), 在时间15类2被分配给类0和类1, 在时间18 类1和类2 同时停止,所以类0得到它所需要的所有90 kbps.带宽.

现在是个接触quantums概念的很好的时机.实际上当一些想借用带宽的类服务于其他竞争的类之前相互给定的一定数量的字节, 这个数量被称为quantums . 你应该明白如果一些竞争的类是可以从它的父类那里得到所需的quantums; 精确的指定quantums的数量尽可能的小并其大于MTU是很重要的.

一般你不需要手工指定一个quantums因为HTB会根据计算选择数值.计算类的quantum相对于用r2q参数分配; 它的缺省的值是10因为典型的MTU是1500,缺省值很适合速率为15 kBps (120 kbit).当你创建队列最小的速率指定r2q 1, 比较适合速率为12 kbit;如果你需要手工指定quantum 当你添加或者更改类,如果预想计算的值是不适合的你可以清除日志里的警告. 当你用命令行指定了quantum 类的r2q将被忽略.

如果A和B是不同的客户这个解决方案看起来很好, 但是如果A 付了40kbps 他可能更希望他不用的WWW的带宽可以用在自己的其他服务上而并不想分享个B. 这种需求是可以通过HTB的类的层次得到解决的.


3. Sharing hierarchy 共享层次

前面章节的问题在这一节里通过类层次结构来得到解决, 用户A 可以很清楚的表达自己的类; 回到前面我们说过提供给每个类带宽的数量是它所需求的最小需求或者等于分配给它的数量. 这样可以用在其他非父类的HTB类里. 我们叫他们为子类, 对于HTB的父类来说我们称为内部类, 规则是使用服务的总量最小而且总量等于他的子类所请求的总和.这里我们分配40kbps给用户A ,这就意味着如果A的需求少于分配的WWW带宽, 那么剩下的将被用来服务于A的其他应用.(如果有需要), 至少总和为40kbps.



注:数据包的分类规则可以分配给内部节点, 也可以有分配其他的过虑器给内部节点,最终应该到达子类或者是特定的类class 1:0 ;父类提供的速率应该是它所有子类的总和.

现在的命令如下:
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:2 htb rate 40kbps ceil 100kbps
tc class add dev eth0 parent 1:2 classid 1:10 htb rate 30kbps ceil 100kbps
tc class add dev eth0 parent 1:2 classid 1:11 htb rate 10kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps

我们现在来看看流量图显示的我们用层次结构的解决方案. 当A的WWW流量停止, 它所分配的带宽被二次分配到它的其他流量上, 所以A的总带宽仍然为40kbps.如果A的总请求带宽小于40kbps.那么剩余的流量将被分配给B.




4. Rate ceiling 速率限度

参数ceil指定了一个类可以用的最大带宽, 用来限制类可以借用多少带宽.缺省的ceil是和速率一样.(也是我们为什么必须在上面的例子里指定它用来显示借用带宽的上限)我们改变前面例子里的类1:2 (A) 和1:11 (A's other) ceil 100kbps 分别为ceil 60kbps 和ceil 20kbps.



和前面的图显示不同, 在时间3 (WWW 流量停止) 因为A 的其他流量限制在20kbps. 所以用户A得到仅仅总带宽20kbps没有用的20kbps被分配给了B .

第二个不同是在时间15时B停止, 因为没有ceil, 所有它的带宽被给了A , 但是现在A 仅仅允许使用60kbps,所以剩余的40kbps 闲置.

这个特性对于ISP是很有用的, 因为他们一般限制被服务的用户的总量即使其他用户没有请求服务.(ISPS 很想用户付更多的钱得到更好的服务) ,注根类是不允许被借用的, 所以没有指定ceil

注: ceil的数值应该至少和它所在的类的速率一样高, 也就是说ceil应该至少和它的任何一个子类一样高



5. Burst 突发

网络硬件只能在一个时间发送一个包这仅仅取决于一个硬件的速率. 链路共享软件可以利用这个能力动态产生多个连接运行在不同的速度. 所以速率和ceil不是一个即时度量只是一个在一个时间里发送包的平均值. 实际的情况是怎样使一个流量很小的类在某个时间类以最大的速率提供给其他类. burst 和cburst 参数控制多少数据可以以硬件最大的速度不费力的发送给需要的其他类.

如果cburst 小于一个理论上的数据包他形成的突发不会超过ceil 速率, 同样的方法TBF的最高速率也是这样.

你可能会问, 为什么需要bursts . 因为它可以很容易的提高向应速度在一个很拥挤的链路上. 比如WWW 流量是突发的. 你访问主页. 突发的获得并阅读. 在空闲的时间burst将再"charge"一次.

注: burst 和cburst至少要和其子类的值一样大.




如图, 接着前一章的例子,我改变burst给红和黄(agency A)类20 kb但cburst仍然为缺省(cca 2 kb).绿色的峰出现在时间13由于在SMTP类设置了burst;A类在限度下自从时间9并且聚集了20 kb的突发流量,峰高于20 kbps(被ceil参数限制因为它的cburst接近包的尺寸),聪明的读者可能会问为什么在时间7处没有红色和黄色的峰;因为黄色已经接近ceil所以没有空间用于突发;有一个不必要的人为的低谷在时间4, 那是因为我忘记添加burst 给根连接到(1:1)类;峰从时间1并且当时间4蓝色的类想借用黄色的速率被拒绝并且自己补偿;

局限性: 当你在计算机上用一个小的时间片操作一个高速链路 你需要为所有的类设置很小的burst 和cburst . 在i386系统上是10ms 在Alphas.系统是1ms ;最小的burst可以max_rate*timer 被估算出来; 所以10Mbit的速率在i386 系统上 burst为 12kb

如果你设置太小的burst 你可能会得到比你设置更小的速率, 后来TC 工具在你没有指定burst.时将估计并且设置一个最小且可能的burst.


6. Priorizing bandwidth share 带宽分配优先

带宽分配的优先级包括两个方面, 首先它影响到子类剩余带宽的分配, 到现在我们已经知道剩余带宽按照速率比例来分配, 我以第三节的配置为例(没有设置ceiling 和 bursts的层次结构 )除了SMTP (green)的优先权改为0(更高) 其他所以类都改为1



从视图我们可以看到类得到了所有剩余带宽, 规则是优先权越高的类越优先得到剩余带宽., 但必须是在rate 和 ceil得到保障的前提下.

另一个方面的问题,包的延时, 在以太网里的延时度量是很困难的,但有一个简单的办法.我们添加一个带宽小于100 kbps的HTB类.第二个类(我们测量的)作为一个子类,然后我们模仿成更慢并且延时比较大的链路.

出于简单的原因, 我们用两个有关联的类;

# qdisc for delay simulation
tc qdisc add dev eth0 root handle 100: htb
tc class add dev eth0 parent 100: classid 100:1 htb rate 90kbps

# real measured qdisc
tc qdisc add dev eth0 parent 100:1 handle 1: htb
AC="tc class add dev eth0 parent"
$AC 1: classid 1:1 htb rate 100kbps
$AC 1:2 classid 1:10 htb rate 50kbps ceil 100kbps prio 1
$AC 1:2 classid 1:11 htb rate 50kbps ceil 100kbps prio 1
tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 2
tc qdisc add dev eth0 parent 1:11 handle 21: pfifo limit 2

注: 另一个HTB的子类和同一个HTB内的子类是不一样的, 因为HTB的类发送数据和硬件发送能力一样的, 所以在限度以内的数据发送仅仅取决于设备速度而不是上级类; HTB下的HTB类输出是模拟一个逻辑硬件; (大的延时)

假定速率同为50 kbps的两个类在时间3S 时执行命令如下:

tc class change dev eth0 parent 1:2 classid 1:10 htb rate 50kbps ceil 100kbps burst 2k prio 0



你可以看到WWW的延时趋近于0而SMTP的延时增大. 当你的优先级别更高而其他类的延时就更大.稍后在7S 时,模仿WWW以60 kbps和SMTP 以40 kbps.发送数据.你可以看到另一个有趣的现象.当WWW越限之后HTB首先是限制带宽.

什么样的类需要优先权? 一般需要延时低的类; 比如视频和音频流; (你必须使用正确的流量速率防止流量相互淹没. ) 或者是交互性(TELNET .SSH )流量正常突发并且不影响其他的流量.

提高ICMP的优先权可以得到一个很好的PING的延时返回, 但这是一个假相, 因为从技术角度来说在测试联通性时这种情况并不是你想要的.



7. Understanding statistics 查看统计

TC 工具允许你对LINUX队列规定进行统计; 不幸的是统计结果作者没有解释所以你不能经常用到他们; 这里我尽力解释来帮助理解HTB的状态; 首先是HTB的整体状态. 下面是第三节里的一个片段;

# tc -s -d qdisc show dev eth0
qdisc pfifo 22: limit 5p
Sent 0 bytes 0 pkts (dropped 0, overlimits 0)

qdisc pfifo 21: limit 5p
Sent 2891500 bytes 5783 pkts (dropped 820, overlimits 0)

qdisc pfifo 20: limit 5p
Sent 1760000 bytes 3520 pkts (dropped 3320, overlimits 0)

qdisc htb 1: r2q 10 default 1 direct_packets_stat 0
Sent 4651500 bytes 9303 pkts (dropped 4140, overlimits 34251)

前三个规定是HTB的子队列, 我们跳过他们因为PFIFO的状态是自我解释的. Overlimit告诉你有多少次队列延时了数据包; direct_packets_stat 告诉你有多少包直接通过队列被送出; 其他的解释是自我解释型的, 让我们看看类的状态;

tc -s -d class show dev eth0
class htb 1:1 root prio 0 rate 800Kbit ceil 800Kbit burst 2Kb/8 mpu 0b
cburst 2Kb/8 mpu 0b quantum 10240 level 3
Sent 5914000 bytes 11828 pkts (dropped 0, overlimits 0)
rate 70196bps 141pps
lended: 6872 borrowed: 0 giants: 0

class htb 1:2 parent 1:1 prio 0 rate 320Kbit ceil 4000Kbit burst 2Kb/8 mpu 0b
cburst 2Kb/8 mpu 0b quantum 4096 level 2
Sent 5914000 bytes 11828 pkts (dropped 0, overlimits 0)
rate 70196bps 141pps
lended: 1017 borrowed: 6872 giants: 0

class htb 1:10 parent 1:2 leaf 20: prio 1 rate 224Kbit ceil 800Kbit burst 2Kb/8 mpu 0b
cburst 2Kb/8 mpu 0b quantum 2867 level 0
Sent 2269000 bytes 4538 pkts (dropped 4400, overlimits 36358)
rate 14635bps 29pps
lended: 2939 borrowed: 1599 giants: 0


我删除了类1:11 和 1:12 以便输出更简单扼要; 可以看到有我们设置的参数, 还有level 和 DRR quantum 信息;

overlimits 显示了有多少次类要求被发送数据而被rate/ceil 限制不能发送;(现在显示的仅仅是子类)

rate, pps 告诉你通过类实际的速率(10秒的平均值) 他和你选择的速率是一样的.

Lended 这个类借出的包 ; borrowed 则是被借入的数据包从父类;

Giants 显示了大于TC命令设置的MTU的数据包的数量;HTB和其协调工作,否则将会影响速率的精确性,, 添加 MTU 到你的TC命令里 (缺省是1600 bytes)

8. Making, debugging and sending error reports
开发, 调试和发送错误报告
如果你拥有kernel 2.4.20或者更新的内核你就不必要打补丁, 但你需要更新TC 工具从HTB 3.6 包里得到并且使用它;

为了和老内核工作协调, 下载内核源码, 用patch -p1 -i htb3_2.X.X.diff装载补丁程序, 然后用 make menuconfig;make bzImage; 编译内核时不要忘了选中QoS 和 HTB.

我将非常感激如果你认为在使用发现了HTB错误. 我需要详细的输出; 在队列规定出现异常行为时请在tc qdisc add .... htb.后面加上参数debug 3333333 ; 它将会在系统日志里记录一些调试记录,为此你可能需要在你的/etc/syslog.conf. 文件里加上kern.debug -/var/log/debug这一行. 通过压缩包电邮给我,并且付上问题描述和时间.

六、使用过滤器对数据包进行分类

为了决定用哪个类处理数据包,必须调用所谓的“分类器链”  进行选择。这个链中包含了这个分类队列规定所需的所有过滤器。重复前面那棵树:

当一个数据包入队的时候,每一个分支处都会咨询过滤器链如何进行下一步。 典型的配置是在1:1处有一个过滤器把数据包交给12:,然后12:处的过滤器在把包交给12:2。你可以把后一个过滤器同时放在1:1处,可因为?having more specific tests lower
in the chain.?而得到效率的提高。 
另外,你不能用过滤器把数据包向“上”送。而且, 使用 HTB 的时候应该把所有的规则放到根上
再次强调:数据包只能向“下”进行入队操作!只有处队的时候才会上到网卡所
在的位置来。他们不会落到树的最底层后送到网卡!

1、过滤器的一些简单范例

就象在“分类器”那章所解释的, 借助一些复杂的语法你可以详细地匹配任何事情。下面我们就开始,从简单地匹配一些比较明显的特征开始。
比方说,我们有一个 PRIO 队列规定,叫做“10:”,包含 3 个类,我们希望把去
往22口的数据流发送到最优先的频道中去。应该这样设置过滤器:
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32  match ip dport 22 0xffff flowid 10:1
 
# tc filter add dev eth0 protocol ip parent 10: prio 1 u32  match ip sport 80 0xffff flowid 10:1
 
# tc filter add dev eth0 protocol ip parent 10: prio 2 flowid 10:2
 
什么意思呢?是说:
向eth0 上的10:节点添加一个u32过滤规则,它的优先权是1:凡是去往22口(精确匹配)的IP数据包,发送到频道10:1。
向eth0 上的10:节点添加一个u32过滤规则,它的优先权是1:凡是来自80口(精确匹配)的IP数据包,发送到频道10:1。
向eth0 上的10:节点添加一个过滤规则,它的优先权是2:凡是上面未匹配的IP
数据包,发送到频道 10:2。
别忘了添加“dev eth0”(你的网卡或许叫别的名字),因为每个网卡的句柄都有
完全相同的命名空间。
 
想通过 IP地址进行筛选的话,这么敲: 
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 match ip dst 4.3.2.1/32 flowid 10:1
 
# tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 match ip src 1.2.3.4/32 flowid 10:1
 
# tc filter add dev eth0 protocol ip parent 10: prio 2    flowid 10:2
这个例子把去往 4.3.2.1 和来自 1.2.3.4 的数据包送到了最高优先的队列,其它的
则送到次高权限的队列。
你可以连续使用match,想匹配来自1.2.3.4的80口的数据包的话,就这么敲: 
  # tc filter add dev eth0 parent 10:0 protocol ip prio 1 u32 match ip src 4.3.2.1/32 match ip
sport 80 0xffff flowid 10:1

2、常用到的过滤命令一览

这里列出的绝大多数命令都根据这个命令改编而来:
# tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 ??
这些是所谓的“u32”匹配,可以匹配数据包的任意部分。
根据源/目的地址 :
源地址段  'match ip src 1.2.3.0/24'
目的地址段  'match ip dst 4.3.2.0/24'
单个IP地址使用“/32”作为掩码即可。

根据源/目的端口,所有IP协议: 
源  'match ip sport 80 0xffff'
目的 'match ip dport 80 0xffff'
根据IP协议 (tcp, udp, icmp, gre, ipsec)  
使用/etc/protocols所指定的数字。
比如: icmp是1:'match ip protocol 1 0xff'. 。

根据fwmark: 
你可以使用 ipchains/iptables 给数据包做上标记,并且这个标记会在穿过网卡的路由过程中保留下来。如果你希望对来自 eth0 并从 eth1 发出的数据包做整形,这就很有用了。语法是这样的:
tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 6 fw flowid 1:1
注意,这不是一个u32匹配!
你可以象这样给数据包打标记:  
# iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6
数字6是可以任意指定的,如果你不想去学习所有的 tc 语法,就可以与 iptables 结合,仅仅学习按fwmark匹配就行了。

按TOS字段: 
选择交互和最小延迟的数据流:
#tc filter add dev ppp0 parent 1:0 protocol ip prio 10 u32 match ip tos 0x10 0xff flowid 1:4
想匹配大量传输的话,使用“0x08 0xff”。

七、IMQ(Intermediate queueing device,中介队列设备)

中介队列设备不是一个队列规定,但它的使用与队列规定是紧密相连的。就Linux
而言,队列规定是附带在网卡上的,所有在这个网卡上排队的数据都排进这个队
列规定。根据这个概念,出现了两个局限:
1. 只能进行出口整形(虽然也存在入口队列规定,但在上面实现分类的队列规定
的可能性非常小)。
2. 一个队列规定只能处理一块网卡的流量,无法设置全局的限速。
IMQ 就是用来解决上述两个局限的。简单地说,你可以往一个队列规定中放任
何东西。被打了特定标记的数据包在 netfilter 的 NF_IP_PRE_ROUTING  和
NF_IP_POST_ROUTING两个钩子函数处被拦截,并被送到一个队列规定中,该
队列规定附加到一个IMQ设备上。对数据包打标记要用到iptables的一种处理方
法。
这样你就可以对刚刚进入网卡的数据包打上标记进行入口整形,或者把网卡们当
成一个个的类来看待而进行全局整形设置。你还可以做很多事情,比如:把http
流量放到一个队列规定中去、把新的连接请求放到一个队列规定中去、??
9.7.1. 配置范例
我们首先想到的是进行入口整形,以便让你自己得到高保证的带宽。就象配置
其它网卡一样:
tc qdisc add dev imq0 root handle 1: htb default 20
 
tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k
 
tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit
tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit
 
tc qdisc add dev imq0 parent 1:10 handle 10: pfifo
tc qdisc add dev imq0 parent 1:20 handle 20: sfq
 
tc filter add dev imq0 parent 10:0 protocol ip prio 1 u32 match \
ip dst 10.0.0.230/32 flowid 1:10
在这个例子中,使用了u32进行分类。其它的分类器应该也能实现。然后,被打
上标记的包被送到imq0排队。
iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0
 
58

 
ip link set imq0 up
iptables的IMQ处理方法只能用在PREROUTING和POSTROUTING链的mangle
表中。语法是:
IMQ [ --todev n ]
n: imq设备的编号
注:ip6tables 也提供了这种处理方法。
请注意,如果数据流是事后才匹配到 IMQ 处理方法上的,数据就不会入队。数
据流进入 imq 的确切位置取决于这个数据流究竟是流进的还是流出的。下面是
netfilter(也就是iptables)在内核中预先定义优先级:
enum nf_ip_hook_priorities {
        NF_IP_PRI_FIRST = INT_MIN,
        NF_IP_PRI_CONNTRACK = -200,
        NF_IP_PRI_MANGLE = -150,
        NF_IP_PRI_NAT_DST = -100,
        NF_IP_PRI_FILTER = 0,
        NF_IP_PRI_NAT_SRC = 100,
        NF_IP_PRI_LAST = INT_MAX,
};
对于流入的包,imq 把自己注册为优先权等于 NF_IP_PRI_MANGLE+1,也就是
说数据包在经过了 PREROUTING链的mangle 表之后才进入imq设备。
对于流出的包,imq使用优先权等于 NF_IP_PRI_LAST,也就是说不会白白处理
本应该被filter 表丢弃的数据包。
关于补丁和更多的文档请参阅imq网站。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值