4. Linux 流量控制的组件

表 1. 流量控制元素与 Linux 组件之间的关联

传统元素Linux 组件
整形The class 提供整形功能。
调度A qdisc 是一个调度器。调度器可以很简单,例如 FIFO,也可以很复杂,包含类和其他 qdisc,例如 HTB。
分类The filter 对象通过 classifier 对象执行分类。严格来说,Linux 分类器不能独立于过滤器存在。
策略A policer 在 Linux 流量控制实现中仅作为 filter 的一部分存在。
丢弃drop 流量,需要一个带有 policerfilter,该 policer 使用 "drop" 作为动作。
标记Thedsmark qdisc 用于标记。

4.1. qdisc

简单来说,qdisc 是一个调度器 (Section 3.2)。每个输出接口都需要某种调度器,默认调度器是 FIFO。Linux 中可用的其他 qdisc 将根据其调度器规则重新排列进入调度器队列的数据包。

qdisc 是构建所有 Linux 流量控制的主要构建块,也称为排队规则。

The 分类 qdisc 可以包含 class,并提供一个句柄来附加 filter。虽然没有禁止在没有子类的情况下使用分类 qdisc,但这通常会消耗周期和其他系统资源而没有任何好处。

The 无类 qdisc 不能包含类,也不可能将过滤器附加到无类 qdisc。由于无类 qdisc 不包含任何类型的子项,因此 分类 没有用处。这意味着没有过滤器可以附加到无类 qdisc。

术语混淆的一个来源是术语的使用rootqdisc 和ingressqdisc。这些实际上不是排队规则,而是可以附加流量控制结构的位置,用于出口(出站流量)和入口(入站流量)。

每个接口都包含两者。主要的和更常见的是出口 qdisc,称为rootqdisc。它可以包含任何排队规则(qdisc),以及潜在的 class 和类结构。绝大多数文档适用于rootqdisc 及其子项。接口上发送的流量会遍历出口或rootqdisc。

对于接口上接受的流量,ingressqdisc 被遍历。由于其有限的实用性,它不允许创建子 class,并且仅作为可以附加 filter 的对象而存在。出于实际目的,ingressqdisc 仅仅是一个方便的对象,可以在其上附加 policer,以限制网络接口上接受的流量。

简而言之,使用出口 qdisc 可以做更多的事情,因为它包含一个真正的 qdisc 和流量控制系统的全部功能。一个ingressqdisc 只能支持 policer。文档的其余部分将关注附加到rootqdisc 的流量控制结构,除非另有说明。

4.2. class

类仅存在于分类 qdisc 中(例如,HTBCBQ)。类非常灵活,并且始终可以包含多个子类或单个子 qdisc [1]。没有禁止一个类本身包含分类 qdisc 的规定,这极大地促进了极其复杂的流量控制场景。

任何类也可以附加任意数量的 filter,这允许选择子类或使用过滤器来重新分类或丢弃进入特定类的流量。

叶子类是 qdisc 中的终端类。它包含一个 qdisc(默认 FIFO),并且永远不会包含子类。任何包含子类的类都是内部类(或根类),而不是叶子类。

4.3. filter

过滤器是 Linux 流量控制系统中最复杂的组件。过滤器提供了一种方便的机制,用于将流量控制的几个关键元素粘合在一起。过滤器最简单和最明显的作用是分类(参见 第 3.3 节)数据包。Linux 过滤器允许用户使用多个不同的过滤器或单个过滤器将数据包分类到输出队列中。

过滤器可以附加到分类 qdiscclass,但是排队的数据包始终首先进入根 qdisc。在遍历附加到根 qdisc 的过滤器后,数据包可能会被定向到任何子类(它们可以有自己的过滤器),在那里数据包可能会进行进一步的分类。

4.4. classifier

可以使用 tc 操作的过滤器对象可以使用几种不同的分类机制,其中最常见的是u32classifier。u32classifier 允许用户根据数据包的属性选择数据包。

分类器是可以用作 filter 一部分的工具,用于识别数据包或数据包元数据的特征。Linux 分类器对象直接类似于流量控制 分类 的基本操作和基本机制。

4.5. policer

这种基本机制仅在 Linux 流量控制中用作 filter 的一部分。policer 在指定速率之上调用一个动作,在指定速率之下调用另一个动作。巧妙地使用 policer 可以模拟三色计量器。另请参见 第 10 节

虽然 策略整形 都是限制带宽使用的流量控制的基本要素,但 policer 永远不会延迟流量。它只能根据指定的标准执行操作。另请参见 示例 5

4.6. drop

这种基本的流量控制机制仅在 Linux 流量控制中用作 policer 的一部分。附加到任何 filter 的任何 policer 都可能具有 drop 动作。

NoteLinux 流量控制系统中可以显式丢弃数据包的唯一位置是 policer。policer 可以限制以特定速率排队的数据包,或者可以配置为丢弃与特定模式匹配的所有流量 [2]

然而,在流量控制系统中,有些地方可能会作为副作用丢弃数据包。例如,如果使用的调度器使用此方法来控制流,例如 GRED,则数据包将被丢弃。

此外,整形器或调度器如果耗尽其分配的缓冲区空间,可能不得不在特别突发或过载期间丢弃数据包。

4.7. handle

每个 class 和分类 qdisc(另请参见 第 7 节)都需要流量控制结构中的唯一标识符。此唯一标识符称为句柄,并且有两个组成部分:主号码和次号码。这些数字可以由用户根据以下规则任意分配 [3]

类和 qdisc 的句柄编号

major

此参数对于内核完全没有意义。用户可以使用任意编号方案,但是流量控制结构中具有相同父项的所有对象必须共享一个major句柄号。传统编号方案从 1 开始,用于直接附加到rootqdisc。

minor

如果minor为 0,则此参数明确地将对象标识为 qdisc。任何其他值都将对象标识为类。共享父项的所有类都必须具有唯一的minor号码。

特殊句柄 ffff:0 保留给ingressqdisc。

句柄用作目标,在classidflowidtc filter 语句的短语中。这些句柄是对象的外部标识符,可供用户空间应用程序使用。内核维护每个对象的内部标识符。

注释

[1]

分类 qdisc 只能具有其类型的子类。例如,HTB qdisc 只能将 HTB 类作为子类。CBQ qdisc 不能将 HTB 类作为子类。

[2]

在这种情况下,你将拥有一个 filter,它使用 classifier 来选择你希望丢弃的数据包。然后,你将使用一个带有 drop 动作的 policer,如下所示:police rate 1bps burst 1 action drop/drop

[3]

我不知道这些数字的范围和基数。我认为它们是 u32 十六进制,但需要确认这一点。