1 简介

Tc 用于 Linux 内核的流量控制。

1.1 流量控制包括以下几种方式

  1. SHAPING(限制)

    当流量被限制,它的传输速率就被控制在某个值以下。限制值可以大大小于有效带宽,这样可以平滑突发数据流量,使网络更为稳定。shaping(限制)只适用于向外的流量。

  2. SCHEDULING(调度)

    通过调度数据包的传输,可以在带宽范围内,按照优先级分配带宽。SCHEDULING(调度)也只适于向外的流量。

  3. POLICING(策略)

    SHAPING 用于处理向外的流量,而 POLICIING (策略)用于处理接收到的数据。

  4. DROPPING(丢弃)

    如果流量超过某个设定的带宽,就丢弃数据包,不管是向内还是向外。

1.2 流量的处理由三种对象控制

  1. QDISC(排队规则)

    QDisc (排队规则)是 queueing discipline 的简写,它是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的 qdisc (排队规则)把数据包加入队列。然后,内核会尽可能多地从 qdisc 里面取出数据包,把它们交给网络适配器驱动模块。

    最简单的 QDisc 是 pfifo 它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。不过,它会保存网络接口一时无法处理的数据包。

  2. CLASS(类)

    某些 QDisc (排队规则)可以包含一些类别,不同的类别中可以包含更深入的 QDisc (排队规则),通过这些细分的 QDisc 还可以为进入的队列的数据包排队。通过设置各种类别数据包的离队次序,QDisc 可以为设置网络数据流量的优先级。

  3. FILTER(过滤器)

    filter (过滤器)用于为数据包分类,决定它们按照何种 QDisc 进入队列。无论何时数据包进入一个划分子类的类别中,都需要进行分类。分类的方法可以有多种,使用 fileter (过滤器)就是其中之一。使用 filter (过滤器)分类时,内核会调用附属于这个类(class)的所有过滤器,直到返回一个判决。如果没有判决返回,就作进一步的处理,而处理方式和 QDISC 有关。

    需要注意的是,filter (过滤器)是在 QDisc 内部,它们不能作为主体。

2 QDisc

2.1 不可分类 QDisc

无类别 QDISC 包括:

  1. [p|b]fifo

    使用最简单的 qdisc,纯粹的先进先出。只有一个参数:limit,用来设置队列的长度,pfifo 是以数据包的个数为单位;bfifo 是以字节数为单位。

  2. 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)里面的。

  3. red

    red 是 Random Early Detection (随机早期探测)的简写。如果使用这种 QDISC,当带宽的占用接近于规定的带宽时,系统会随机地丢弃一些数据包。它非常适合高带宽应用。

  4. sfq

    sfq 是 Stochastic Fairness Queueing 的简写。它按照会话(session–对应于每个 TCP 连接或者 UDP 流)为流量进行排序,然后循环发送每个会话的数据包。

  5. tbf

    tbf 是 Token Bucket Filter 的简写,适合于把流速降低到某个值。

不可分类 QDisc 的配置

如果没有可分类 QDisc,不可分类 QDisc 只能附属于设备的根。它们的用法如下:

tc qdisc add dev DEV root QDISC QDISC-PARAMETERS

要删除一个不可分类 QDisc,需要使用如下命令:

tc qdisc del dev DEV root

一个网络接口上如果没有设置 QDisc,pfifo_fast 就作为缺省的 QDisc。

2.2 可分类的 QDisc(CLASSFUL QDISC)

可分类的 QDisc 包括:

  1. CBQ

    CBQ 是 Class Based Queueing (基于类别排队)的缩写。它实现了一个丰富的连接共享类别结构,既有限制(shaping)带宽的能力,也具有带宽优先级管理的能力。带宽限制是通过计算连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的频率和下层连接(数据链路层)的带宽。

  2. HTB

    HTB 是 Hierarchy Token Bucket 的缩写。通过在实践基础上的改进,它实现了一个丰富的连接共享类别体系。使用 HTB 可以很容易地保证每个类别的带宽,虽然它也允许特定的类可以突破带宽上限,占用别的类的带宽。HTB 可以通过 TBF(Token Bucket Filter) 实现带宽限制,也能够划分类别的优先级。

  3. PRIO

    PRIO QDisc 不能限制带宽,因为属于不同类别的数据包是顺序离队的。使用 PRIO QDisc 可以很容易对流量进行优先级管理,只有属于高优先级类别的数据包全部发送完毕,才会发送属于低优先级类别的数据包。为了方便管理,需要使用 iptables 或者 ipchains 处理数据包的服务类型(Type Of Service,ToS)。

3 操作原理

类(Class)组成一个树,每个类都只有一个父类,而一个类可以有多个子类。某些 QDisc(例如:CBQ 和 HTB)允许在运行时动态添加类,而其它的 QDisc(如PRIO)不允许动态建立类。

允许动态添加类的 QDisc 可以有零个或者多个子类,由它们为数据包排队。

此外,每个类都有一个叶子 QDisc,默认情况下,这个叶子 QDisc 使用 pfifo 的方式排队,我们也可以使用其它类型的 QDisc 代替这个默认的 QDisc。而且,这个叶子 QDisc 可以分类,不过每个子类只能有一个叶子 QDisc。

当一个数据包进入一个分类 QDisc,它会被归入某个子类。我们可以使用以下三种方式为数据包归类,不过不是所有的 QDisc 都能够使用这三种方式。

方式如下:

  1. tc 过滤器

    如果过滤器附属于一个类,相关的指令就会对它们进行查询。过滤器能够匹配数据包头所有的域,也可以匹配由 ipchains 或者 iptables 做的标记。

  2. 服务类型(Type of Service)

    某些 QDisc 有基于服务类型(Type of Service,ToS)的内置的规则为数据包分类。

  3. skb->priority

    序可以使用 SO_PRIORITY 选项在 skb->priority 域设置一个类的 ID。

树的每个节点都可以有自己的过滤器,但是高层的过滤器也可以直接用于其子类。

如果数据包没有被成功归类,就会被排到这个类的叶子 QDisc 的队中。相关细节在各个 QDisc 的手册页中。

4 命名规则

所有的 QDisc、类和过滤器都有 ID。ID 可以手工设置,也可以由内核自动分配。ID 由一个主序列号和一个从序列号组成,两个数字用一个冒号分开。

  1. QDISC

    一个 QDisc 会被分配一个主序列号,叫做句柄(handle),然后把从序列号作为类的命名空间。句柄采用象10:一样的表达方式。习惯上,需要为有子类的 QDisc 显式地分配一个句柄。

  2. 类(CLASS)

    在同一个 QDisc 里面的类分享这个 QDisc 的主序列号,但是每个类都有自己的从序列号,叫做类识别符(classid)。类识别符只与父 QDisc 有关,和父类无关。类的命名习惯和 QDisc 的相同。

  3. 过滤器(FILTER)

    过滤器的 ID 有三部分,只有在对过滤器进行散列组织才会用到。详情请参考 tc-filters 手册页。

5 单位

tc 命令的所有参数都可以使用浮点数,可能会涉及到以下计数单位。

  1. 带宽或者流速单位

    kbps					#千字节/秒
    mbps					#兆字节/秒
    kbit					#KBits/秒
    mbit					#MBits/秒
    bps或者一个无单位数字		#字节数/秒
    
  2. 数据的数量单位

    kb或者k				#千字节
    mb或者m				#兆字节
    mbit				#兆bit
    kbit				#千bit
    b或者一个无单位数字	#字节数
    
  3. 时间的计量单位

    s、sec或者secs						#秒
    ms、msec或者msecs					#毫秒
    us、usec、usecs或者一个无单位数字		#微秒
    

6 TC 命令对 QDisc、类和过滤器的操作

tc 可以使用以下命令对 QDisc、类和过滤器进行操作:

  1. add

    在一个节点里加入一个 QDisc、类或者过滤器。添加时,需要传递一个祖先作为参数,传递参数时既可以使用 ID 也可以直接传递设备的根。如果要建立一个 QDisc 或者过滤器,可以使用句柄(handle)来命名;如果要建立一个类,可以使用类识别符(classid)来命名。

  2. remove

    删除由某个句柄(handle)指定的 QDisc,根 QDisc(root)也可以删除。被删除 QDisc 上的所有子类以及附属于各个类的过滤器都会被自动删除。

  3. change

    以替代的方式修改某些条目。除了句柄(handle)和祖先不能修改以外,change 命令的语法和 add 命令相同。换句话说,change 命令不能一定节点的位置。

  4. replace

    对一个现有节点进行近于原子操作的删除/添加。如果节点不存在,这个命令就会建立节点。

  5. link

    只适用于 DQisc,替代一个现有的节点。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。