在着手学习TC之前,请先了解TC采用如下单位来描述带宽:
mbps = 1024 kbps = 1024 * 1024 bps => byte/s
mbit = 1024 kbit => kilo bit/s
mb = 1024 kb = 1024 * 1024 b => byte
mbit = 1024 kbit => kilo bit
内定:数值以bps和b方式储存。但当设置tc输出速率时,使用如下表示:
1Mbit = 1024 Kbit = 1024 * 1024 bps => byte/s
提醒:bit=1位,相当于一个0 或1(开或关);byte=1字节,相当于一个英文字母的大小。
8bit=1byte,所以往往听说带宽是10M是指bit,我们看到的下载速度是byte/s =10M/8byte的来。
队列和队列规则
利用队列,我们可以控制数据发送的方式。但我们只能对发送数据进行控制(或称为流量整形)。
由于网络的工作机制,我们无法直接控制别人向我们发送什么数据。然而,Internet主要依靠TCP/IP,它的一些特性很有用。因为TCP/IP没办法知道两个主机之间的网络容量,所以它会试图越来越快地发送数据(所谓的“慢启动”),在因为网络容量不够而开始丢失数据时,才放慢发送数据的速度。
这就像你家门口的邮箱一样,当邮箱几乎塞满了邮件时,你会希望不要再发送邮件了。在Internet上可以做到这一点。如果你有一个路由器,并且希望防止某些主机下载速度太快,你需要在路由器的内网卡——也就是链接你的网内主机发送数据包的网卡上进行流量控制。
你还要保证你正在控制的是流量瓶颈环节。如果你有一个100M以太网卡,而你的路由器的链路速度是256k,你必须保证你发送的数据量没有超过路由器的处理能力。否则,就是路由器在控制链路和对带宽进行整形,而不是你所定义的规则在对带宽进行控制。可以说,我们需要拥有的队列必须是一系列链路中最慢的环节。
简单的无类队列规则
如前所述,利用队列,我们决定了数据被发送的方式。无类队列能够接收数据和重新编排、延迟或丢弃数据包。
这可以用作对于整个网卡的流量进行整形,而不细分各种情况。在我们进一步学习分类的队列规则之前,理解这部分是必不可少的!
最广泛应用的规则是pfifo_fast队列规则,因为它是缺省配置。还有其他队列规则,后面会有所介绍。每种队列都有它们各自的优势和弱点。
1.pfifo_fast
这个队列的特点就像它的名字一样——先进先出(FIFO),也就是说没有任何数据包被特殊处理。这个队列有3个所谓的“频道”(band)。FIFO规则应用于每一个频道。并且:如果在0频道有数据包等待发送,1频道的包就不会被处理,1频道和2频道之间的关系也是如此。
内核遵照数据包的TOS标记,把带有“最小延迟”标记的包放进0频道。
*参数与使用*
pfifo_fast队列规则作为硬性的缺省设置,不能对它进行配置。
其缺省配置如下:
priomap:
内核规则,根据数据包的优先权情况,映射到相应的频道。这个映射过程是根据数据包的TOS字节进行的。
TOS是这样的:
TOS字段的4个bit是如下定义的:
因为在这4bit的后面还有一个bit(MBZ),所以TOS字段的实际值是上述值的2倍。tcpdump -v -v命令可以让你看到整个TOS字段的情况,而不仅仅是这4个bit,也就是你在下表的第一列看到的值:
第二列写着与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:
tx queuelen
队列的长度来自网卡的配置,你可以用ifconfig和ip命令修改。
如设置队列长度为10,执行:ifconfig eth0 txqueuelen 10(不能用tc命令设置这个)。
2.令牌桶过滤器(TBF)
令牌桶过滤器(TBF,Token Bucket Filter)是一个简单的队列规则:只允许以不超过事先设定的速率到来的数据包通过,但可能允许短暂突发流量超过设定值。
TBF很精确,对于网络和处理器的影响都很小。所以如果您想对一个网卡限速,它应该是最好选择!
TBF的实现在于一个缓冲器(桶),该缓冲器(桶)不断地被一些叫做”令牌”的虚拟数据以特定速率(token rate)填充着。桶最重要的参数就是它的大小,也就是它能够存储令牌的数量。
每个到来的令牌从数据队列中收集一个数据包,然后从桶中被删除。这个算法关联到两个流上——令牌流和数据流,于是我们得到3种情景:
*数据流以等于令牌流的速率到达TBF。这种情况下,每个到来的数据包都能对应一个令牌,然后无延迟地通过队列。
*数据流以小于令牌流的速度到达TBF。通过队列的数据包只消耗了一部分令牌,剩下的令牌会在桶里积累下来,直到桶被装满。剩下的令牌可以在需要以高于令牌流速率发送数据流的时候消耗掉,这种情况下会发生突发传输。
*数据流以大于令牌流的速率到达TBF。这意味着桶里的令牌很快就会被耗尽。导致TBF中断一段时间,称为”越限”(overlimit)。如果数据包持续到来,将发生丢包。
第三种情景非常重要,因为它会对数据通过过滤器的速率进行整形。令牌的积累可以导致越限的数据进行短时间的突发传输而不必丢包,但是持续越限的话会导致传输延迟直至丢包。实际的实现是针对数据的字节数进行的,而不是针对数据包进行的。
*参数与使用*
TBF提供了一些可调控的参数。第一个参数永远可用:
limit/latency
limit确定最多有多少数据(字节数)在队列中等待令牌。你也可以通过设置latency来指定这个参数,latency参数确定了一个包在TBF中等待传输的最长等待时间。两者计算决定桶的大小、速率和峰值速率。
burst/buffer/maxburst
桶的大小,以字节计。这个参数指定了最多可以有多少个令牌能够即刻被使用。通常,管理的带宽越大,需要的缓冲器就越大。在Intel体系上,10Mbit/s的速率需要至少10k字节的缓冲区才能达到期望的速率。
如果你的缓冲区太小,就会导致到达的令牌没有地方放(桶满了),这会导致潜在的丢包。
MPU
一个零长度的包并不是不耗费带宽。比如以太网,数据帧不会小于64字节。MPU(Minimum Packet Unit,最小分组单元)决定了令牌的最低消耗。
rate
速度操纵杆。参见上面的limit。
如果桶里存在令牌而且允许没有令牌,相当于不限制速率(缺省情况)。如果不希望这样,可以调入以下参数:
peakrate(峰值速率)
如果有可用的令牌,数据包一旦到来就会立刻被发送出去,就像光速一样。那可能并不是你希望的,特别是你有一个比较大的桶的时候。
峰值速率可以用来指定令牌以多快的速度被删除。用书面语言来说,就是:释放一个数据包,然后等待足够的时间后再释放下一个。我们通过计算等待时间来控制峰值速率。例如:UNIX定时器的分辨率是10毫秒,如果平均包长10kb,我们的峰值速率被限制在了1Mbps。
MTU(Maximum Transmission Unit, 最大传输单元)/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”值提高一点。
3.随机公平队列(SFQ)
SFQ(Stochastic Fairness Queueing,随机公平队列)简单实现公平队列算法。它的精确性不如其它的方法,但是它在实现高度公平的同时,需要的计算量却很少。
SFQ的关键词是”会话”或“流”,主要针对一个TCP会话或者UDP流。流量被分成相当多数量的FIFO队列中,每个队列对应一个会话。数据按照简单轮转的方式发送,每个会话都按顺序得到发送机会。
这种方式非常公平,保证了每一个会话都不会没其它会话所淹没。SFQ之所以被称为“随机”,是因为它并不是真的为每一个会话创建一个队列,而是使用一个散列算法,把所有的会话映射到有限的几个队列中去。因为使用了散列,所以可能多个会话分配在同一个队列里,从而需要共享发包的机会,也就是共享带宽。为了不让这种效应太明显,SFQ会频繁地改变散列算法,以便把这种效应控制在几秒钟之内。
有很重要的一点需要声明:只有当你的输出网卡确实已经挤满了的时候,SFQ才会起作用!否则在你的Linux机器中根本就不会有队列,SFQ也就不会起作用。稍后我们会描述如何把SFQ与其它的队列规则结合在一起,以保证两种情况下都有比较好的结果。
<