上面所提到的不同类型的流量在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 /DownloadFiles\2005september\2005-09-13\/DownloadFiles\2005september\2005-09-13\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 /DownloadFiles\2005september\2005-09-13\/DownloadFiles\2005september\2005-09-13\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这一行. 通过压缩包电邮给我,并且付上问题描述和时间.