# !/bin/sh
touch /var/lock/subsys/local
echo 1 > /proc/sys/net/ipv4/ip_forward
route add default gw 10.0.0.0 (这是加入电信网关,如果你已设了不用这条)
DOWNLOAD=640Kbit (640/8 =80K ,我这里限制下载最高速度只能80K)
UPLOAD=640Kbit (640/8 =80K,上传速度也限制在80K)
INET=192.168.0. (设置网段,根据你的情况填)
IPS=1 (这个意思是从192.168.0.1开始)
IPE=200 (我这设置是从IP为192.168.0.1-200这个网段限速,根据自已的需要改)
ServerIP=253 (网关IP)
IDEV=eth0
ODEV=eth1
/sbin/tc qdisc del dev $IDEV root handle 10:
/sbin/tc qdisc del dev $ODEV root handle 20:
/sbin/tc qdisc add dev $IDEV root handle 10: cbq bandwidth 100Mbit avpkt 1000
/sbin/tc qdisc add dev $ODEV root handle 20: cbq bandwidth 1Mbit avpkt 1000
/sbin/tc class add dev $IDEV parent 10:0 classid 10:1 cbq bandwidth 100Mbit rate 100Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000
/sbin/tc class add dev $ODEV parent 20:0 classid 20:1 cbq bandwidth 1Mbit rate 1Mbit allot 1514 weitht 10Kbit prio 8 maxburst 20 avpkt 1000
COUNTER=$IPS
while [ $COUNTER -le $IPE ]
do
/sbin/tc class add dev $IDEV parent 10:1 classid 10:1$COUNTER cbq banwidth 100Mbit rate
$DOWNLOAD allot 1514 weight 20Kbit prio 5 maxburst 20 avpkt 1000 bounded
/sbin/tc qdisc add dev $IDEV parent 10:1$COUNTER sfq quantum 1514b perturb15
/sbin/tc filter add dev $IDEV parent 10:0 protocol ip prio 100 u32 match ipdst $INET$COUNTER flowid 10:1$COUNTER
COUNTER=` expr $COUNTER + 1 `
done
iptables -t nat -A POSTROUTING -o eth1 -s 192.168.0.0/24 -J MASQUERADE
HTB官方网站:http://luxik.cdi.cz/~devik/qos/htb/
LINUX HTB队列规定用户指南
HTB Linux queuing discipline manual - user guide
Martin Devera aka devik ([email protected])
Manual: devik and Don Cohen
Last updated: 5.5.2002
译者:龚关 [email protected]
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 3635
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这一行. 通过压缩包电邮给我,并且付上问题描述和时间.
名称:tc - 显示/维护流量控制设置
命令格式:
tc qdisc [ add | change | replace | link ] dev DEV [ parent qdisc-id | root ] [ handle qdisc-id ] qdisc [ qdisc specific parameters ]
tc class [ add | change | replace ] dev DEV parent qdisc-id [ classid class-id ] qdisc [ qdisc specific parameters ]
tc filter [ add | change | replace ] dev DEV [ parent qdisc-id | root ] protocol protocol prio priority filtertype [ filtertype specific parameters ] flowid flow-id
tc [-s | -d ] qdisc show [ dev DEV ]
tc [-s | -d ] class show dev DEV tc filter show dev DEV
TC用途简介:
Tc用于Linux内核的流量控制。
流量控制包括以下几种方式:
SHAPING(限制)
当流量被限制,它的传输速率就被控制在某个值以下。限制值可以大大小于有效带宽,这样可以平滑突发数据流量,使网络更为稳定。shaping(限制)只适用于向外的流量。
SCHEDULING(调度)
通过调度数据包的传输,可以在带宽范围内,按照优先级分配带宽。SCHEDULING(调度)也只适于向外的流量。
POLICING(策略)
SHAPING用于处理向外的流量,而POLICIING(策略)用于处理接收到的数据。
DROPPING(丢弃)
如果流量超过某个设定的带宽,就丢弃数据包,不管是向内还是向外。
流量的处理由三种对象控制,它们是:qdisc(排队规则)、class(类别)和filter(过滤器)。
QDISC(排队规则)
QDisc(排队规则)是queueing discipline的简写,它是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。然后,内核会尽可能多地从qdisc里面取出数据包,把它们交给网络适配器驱动模块。
最简单的QDisc是pfifo它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。不过,它会保存网络接口一时无法处理的数据包。
CLASS(类)
某些QDisc(排队规则)可以包含一些类别,不同的类别中可以包含更深入的QDisc(排队规则),通过这些细分的QDisc还可以为进入的队列的数据包排队。通过设置各种类别数据包的离队次序,QDisc可以为设置网络数据流量的优先级。
FILTER(过滤器)
filter(过滤器)用于为数据包分类,决定它们按照何种QDisc进入队列。无论何时数据包进入一个划分子类的类别中,都需要进行分类。分类的方法可以有多种,使用fileter(过滤器)就是其中之一。使用filter(过滤器)分类时,内核会调用附属于这个类(class)的所有过滤器,直到返回一个判决。如果没有判决返回,就作进一步的处理,而处理方式和QDISC有关。
需要注意的是,filter(过滤器)是在QDisc内部,它们不能作为主体。
CLASSLESS QDisc(不可分类QDisc)
无类别QDISC包括:
[p|b]fifo
使用最简单的qdisc,纯粹的先进先出。只有一个参数:limit,用来设置队列的长度,pfifo是以数据包的个数为单位;bfifo是以字节数为单位。
pfifo_fast
在编译内核时,如果打开了高级路由器(Advanced Router)编译选项,pfifo_fast就是系统的标准QDISC。它的队列包括三个波段(band)。在每个波段里面,使用先进先出规则。而三个波段(band)的优先级也不相同,band 0的优先级最高,band 2的最低。如果band里面有数据包,系统就不会处理band 1里面的数据包,band 1和band 2之间也是一样。数据包是按照服务类型(Type of Service,TOS)被分配多三个波段(band)里面的。
red
red是Random Early Detection(随机早期探测)的简写。如果使用这种QDISC,当带宽的占用接近于规定的带宽时,系统会随机地丢弃一些数据包。它非常适合高带宽应用。
sfq
sfq是Stochastic Fairness Queueing(随机公平队列)的简写。是公平队列算法家族中的一个简单实现.它的精确性不如其它的方法,但是它在实现高度公平的同时,需要的计算量却很少.
它按照会话(session--对应于每个TCP连接或者UDP流)为流量进行排序,然后循环发送每个会话的数据包。
SFQ的关键词是"会话"(或称作"流") ,主要针对一个TCP会话或者UDP流.流量被分成相当多数量的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
tbf是Token Bucket Filter(令牌桶过滤器)的简写,适合于把流速降低到某个值。令牌桶过滤器(TBF)是一个简单的队列规定:只允许以不超过事先设定的速率到来的数据包通过,但可能允许短暂突发流量朝过设定值.
TBF很精确,对于网络和处理器的影响都很小.所以如果您想对一个网卡限速,它应该成为您的第一选择!
TBF的实现在于一个缓冲器(桶),不断地被一些叫做"令牌"的虚拟数据以特定速率填充着. (token rate).桶最重要的参数就是它的大小,也就是它能够存储令牌的数量.
每个到来的令牌从数据队列中收集一个数据包,然后从桶中被删除.这个算法关联到两个流上——令牌流和数据流,于是我们得到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
为什么它很实用呢?如果你有一个队列较长的网络设备,比如DSL modem或者cable modem什么的,并通过一个快速设备(如以太网卡)与之相连,你会发现上载数据绝对会破坏交互性.
这是因为上载数据会充满modem的队列,而这个队列为了改善上载数据的吞吐量而设置的特别大.但这并不是你需要的,你可能为了提高交互性而需要一个不太大的队列.也就是说你希望在发送数据的时候干点别的事情.
上面的一行命令并非直接影响了modem中的队列,而是通过控制Linux中的队列而放慢了发送数据的速度.
把220kbit修改为你实际的上载速度再减去几个百分点.如果你的modem确实很快,就把"burst"值提高一点.
关于什么时候用哪种队列的建议
总之,我们有几种简单的队列,分别使用排序,限速和丢包等手段来进行流量整形.
下列提示可以帮你决定使用哪一种队列.涉及到了第14章 所描述的的一些队列规定:
如果想单纯地降低出口速率,使用令牌桶过滤器.调整桶的配置后可用于控制很高的带宽.
如果你的链路已经塞满了,而你想保证不会有某一个会话独占出口带宽,使用随机公平队列.
如果你有一个很大的骨干带宽,并且了解了相关技术后,可以考虑前向随机丢包(参见"高级"那一章).
如果希望对入口流量进行"整形"(不是转发流量),可使用入口流量策略,注意,这不是真正的"整形".
如果你正在转发数据包,在数据流出的网卡上应用TBF.除非你希望让数据包从多个网卡流出,也就是说入口网卡起决定性作用的时候,还是使用入口策略.
如果你并不希望进行流量整形,只是想看看你的网卡是否有比较高的负载而需要使用队列,使用pfifo队列(不是pfifo_fast).它缺乏内部频道但是可以统计backlog.
最后,你可以进行所谓的"社交整形".你不能通过技术手段解决一切问题.用户的经验技巧永远是不友善的.正确而友好的措辞可能帮助你的正确地分配带宽!
不可分类QDisc的配置
如果没有可分类QDisc,不可分类QDisc只能附属于设备的根。它们的用法如下:
tc qdisc add dev DEV root QDISC QDISC-PARAMETERS
要删除一个不可分类QDisc,需要使用如下命令:
tc qdisc del dev DEV root
一个网络接口上如果没有设置QDisc,pfifo_fast就作为缺省的QDisc。
CLASSFUL QDISC(分类QDisc)
可分类的QDisc包括:
CBQ
CBQ是Class Based Queueing(基于类别排队)的缩写。它实现了一个丰富的连接共享类别结构,既有限制(shaping)带宽的能力,也具有带宽优先级管理的能力。带宽限制是通过计算连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的频率和下层连接(数据链路层)的带宽。
HTB
HTB是Hierarchy Token Bucket的缩写。通过在实践基础上的改进,它实现了一个丰富的连接共享类别体系。使用HTB可以很容易地保证每个类别的带宽,虽然它也允许特定的类可以突破带宽上限,占用别的类的带宽。HTB可以通过TBF(Token Bucket Filter)实现带宽限制,也能够划分类别的优先级。
PRIO
PRIO QDisc不能限制带宽,因为属于不同类别的数据包是顺序离队的。使用PRIO QDisc可以很容易对流量进行优先级管理,只有属于高优先级类别的数据包全部发送完毕,才会发送属于低优先级类别的数据包。为了方便管理,需要使用 iptables或者ipchains处理数据包的服务类型(Type Of Service,ToS)。
操作原理
类(Class)组成一个树,每个类都只有一个父类,而一个类可以有多个子类。某些QDisc(例如:CBQ和HTB)允许在运行时动态添加类,而其它的QDisc(例如:PRIO)不允许动态建立类。
允许动态添加类的QDisc可以有零个或者多个子类,由它们为数据包排队。
此外,每个类都有一个叶子QDisc,默认情况下,这个叶子QDisc使用pfifo的方式排队,我们也可以使用其它类型的QDisc代替这个默认的QDisc。而且,这个叶子叶子QDisc有可以分类,不过每个子类只能有一个叶子QDisc。
当一个数据包进入一个分类QDisc,它会被归入某个子类。我们可以使用以下三种方式为数据包归类,不过不是所有的QDisc都能够使用这三种方式。
tc过滤器(tc filter)
如果过滤器附属于一个类,相关的指令就会对它们进行查询。过滤器能够匹配数据包头所有的域,也可以匹配由ipchains或者iptables做的标记。
服务类型(Type of Service)
某些QDisc有基于服务类型(Type of Service,ToS)的内置的规则为数据包分类。
skb->priority
用户空间的应用程序可以使用SO_PRIORITY选项在skb->priority域设置一个类的ID。
树的每个节点都可以有自己的过滤器,但是高层的过滤器也可以直接用于其子类。
如果数据包没有被成功归类,就会被排到这个类的叶子QDisc的队中。相关细节在各个QDisc的手册页中。
命名规则
所有的QDisc、类和过滤器都有ID。ID可以手工设置,也可以有内核自动分配。
ID由一个主序列号和一个从序列号组成,两个数字用一个冒号分开。
QDISC
一个QDisc会被分配一个主序列号,叫做句柄(handle),然后把从序列号作为类的命名空间。句柄采用象10:一样的表达方式。习惯上,需要为有子类的QDisc显式地分配一个句柄。
类(CLASS)
在同一个QDisc里面的类分享这个QDisc的主序列号,但是每个类都有自己的从序列号,叫做类识别符(classid)。类识别符只与父QDisc有关,和父类无关。类的命名习惯和QDisc的相同。
过滤器(FILTER)
过滤器的ID有三部分,只有在对过滤器进行散列组织才会用到。详情请参考tc-filters手册页。
单位
tc命令的所有参数都可以使用浮点数,可能会涉及到以下计数单位。
带宽或者流速单位:
kbps
千字节/秒
mbps
兆字节/秒
kbit
KBits/秒
mbit
MBits/秒
bps或者一个无单位数字
字节数/秒
数据的数量单位:
kb或者k
千字节
mb或者m
兆字节
mbit
兆bit
kbit
千bit
b或者一个无单位数字
字节数
时间的计量单位:
s、sec或者secs
秒
ms、msec或者msecs
分钟
us、usec、usecs或者一个无单位数字
微秒
TC命令
tc可以使用以下命令对QDisc、类和过滤器进行操作:
add
在一个节点里加入一个QDisc、类或者过滤器。添加时,需要传递一个祖先作为参数,传递参数时既可以使用ID也可以直接传递设备的根。如果要建立一个 QDisc或者过滤器,可以使用句柄(handle)来命名;如果要建立一个类,可以使用类识别符(classid)来命名。
remove
删除有某个句柄(handle)指定的QDisc,根QDisc(root)也可以删除。被删除QDisc上的所有子类以及附属于各个类的过滤器都会被自动删除。
change
以替代的方式修改某些条目。除了句柄(handle)和祖先不能修改以外,change命令的语法和add命令相同。换句话说,change命令不能一定节点的位置。
replace
对一个现有节点进行近于原子操作的删除/添加。如果节点不存在,这个命令就会建立节点。
link
只适用于DQisc,替代一个现有的节点。