Linux 网络流量控制 - 实现概述

摘要

Linux 提供了一整套丰富的 流量控制( traffic control)功能。本文档概述了相应的内核代码设计,描述了其结构,并通过描述一种新的排队策略来说明新元素的添加。

1 引言

最近的Linux内核提供了多种流量控制功能。Alexey Kuznetsov([email protected])实现了流量控制的内核部分以及几个用户空间程序来控制它们。这项工作受到了[1]中描述的概念的启发,但它也涵盖了支持IETF“intserv”组[2]开发的架构所需的机制,并将作为支持更近期的“diffserv”[3]工作的基础。有关intserv和diffserv之间关系的更多详细信息,请参见[4]。本文档说明了底层架构,并描述了如何将新的流量控制功能添加到Linux内核中。我们使用的内核版本是2.2.6。

图1大致展示了内核如何处理从网络接收到数据,以及如何生成要发送到网络的新数据:传入的数据包被检查,然后要么直接转发到网络(例如,如果机器充当路由器或桥接器,则在不同的接口上),要么传递到协议栈的更高层(例如,传递给像UDP或TCP这样的传输协议)进行进一步处理。这些更高层也可能自己生成数据,并将其交给较低层执行封装、路由和最终传输等任务。

图 1 网络数据的处理

“Forwarding”包括选择输出接口、选择下一跳、封装等。一旦完成所有这些操作,数据包就会在各自的输出接口上排队。这就是流量控制发挥作用的地方。流量控制可以决定数据包是排队还是丢弃(例如,如果队列达到了某个长度限制,或者流量超过了某个速率限制),它可以决定数据包的发送顺序(例如,为某些流提供优先级),它也可以延迟数据包的发送(例如,限制出站流量的速率),等等。

一旦流量控制释放了一个数据包进行发送,设备驱动程序就会接管它并将其发送到网络。

第2至4节提供了概述并解释了一些术语。第5至8节更详细地描述了Linux内核中流量控制的元素。第9节描述了作者实现的一种排队规则。

2 概述

Linux内核中的流量控制代码由以下主要概念组件组成:

  • 排队规则(queuing disciplines)
  • 类(classes,位于排队规则内)
  • 过滤器(filters)
  • 策略(policing)

这些组件共同协作,实现对网络流量的精细控制和管理。排队规则决定了数据包如何被排队和发送,类用于在排队规则内部进一步划分和管理流量,过滤器用于根据特定条件将数据包分配到不同的类或应用不同的策略,而策略则用于执行流量限制和检测。

每个网络设备都有一个与之关联的排队规则,该规则控制如何处理在该设备上排队的数据包。非常简单的排队规则可能只包含一个队列,其中所有数据包都按照它们入队的顺序存储,并且以相应设备能够发送的最快速度清空。请参阅图2,这是一个外部不可见内部结构的排队规则。

图 2:没有类的简单排队规则

更复杂的排队规则可能会使用过滤器来区分不同类型的数据包,并以特定方式处理每个类别,例如,通过给予一个类别优于其他类别的优先级。

图3展示了这样一个排队规则的示例。请注意,多个过滤器可能会映射到同一类别。

图 3 一个具有多个类别的简单排队规则

排队规则和类别紧密地结合在一起:类别的存在及其语义是排队规则的基本属性。相比之下,只要排队规则有类别,过滤器可以任意地与排队规则和类别组合。但灵活性还不止于此——通常,类别并不会自己存储数据包,而是使用另一个排队规则来处理存储。这个排队规则可以从可用的排队规则集中任意选择,并且它可能有类别,这些类别又使用排队规则,等等。

图4展示了这样一个堆栈的示例:首先,有一个具有两个延迟优先级的排队规则。被过滤器选择的数据包进入高优先级类别,而所有其他数据包进入低优先级类别。每当高优先级队列中有数据包时,它们会在低优先级队列的数据包之前发送(例如,sch_prio排队规则就是这样工作的)。为了防止高优先级流量饿死低优先级流量,我们使用令牌桶过滤器(TBF),强制执行最多1 Mbps的速率。最后,低优先级数据包的排队是由FIFO排队规则完成的。请注意,这里有更好的方法来实现我们所做的,例如,使用基于类的排队(CBQ)[5]。

图 4 组合优先级、TBF 和 FIFO 排队规则

数据包入队的过程如下:当调用排队规则的入队函数时,它会一个接一个地运行过滤器,直到其中一个匹配为止。然后它会将数据包排入相应的类别,这通常意味着调用该类别“拥有”的排队规则的入队函数。不匹配任何过滤器的数据包通常会被分配给某个默认类别。

通常,每个类别“拥有”一个队列,但从原理上讲,多个类别共享同一个队列,甚至是单个队列被相应排队规则的所有类别使用也是可能的。然而,需要注意的是,数据包没有任何明确的指示表明它们被归类到哪个类别。排队规则在出队包时更改每个类别的信息(例如,CBQ)可能无法正常工作,如果“内部”队列是共享的,除非它们能够通过某些方式重复分类或在入队和出队之间传递分类结果。

通常,在入队包时,相应的流可以被策略控制,例如,丢弃超过特定速率的数据包。

我们不会尝试引入新的术语来区分算法、它们的实现以及这些元素的实例,而是在本文档的大部分中使用排队规则、类别和过滤器这些术语,以同时指代所有三个抽象层次。

3 资源

Linux流量控制分布在相对较多的文件中。请注意,所有路径名都是相对于相应组件的基目录而言的,例如,对于Linux内核,这是/usr/src/linux/,对于tc程序,这是iproute2/tc/。

tc是一个用户空间程序,用于操作单个流量控制元素。其源代码位于文件iproute2-version.tar.gz中,可以从ftp://linux.wauug.org/pub/net/ip-routing/获取。

内核代码主要位于目录net/sched/中。内核流量控制元素和使用它们的用户空间程序之间的接口在include/linux/pkt_cls.h和include/linux/pkt_sched.h中声明。仅在内核内部使用的声明和一些内联函数的定义可以在include/net/pkt_cls.h和include/net/pkt_sched.h中找到。

用于用户空间和内核中流量控制元素之间通信的rtnetlink机制在net/core/rtnetlink.c和include/linux/rtnetlink.h中实现。rtnetlink基于netlink,可以在net/netlink/和include/linux/netlink.h中找到。

可以从通常众所周知的来源获取内核源代码,例如,从ftp://ftp.kernel.org/pub/linux/kernel/v2.2/下载。

第9节中的示例包含在Linux上的ATM发行版中,可以从http://icawww1.epfl.ch/linux-atm/dist.html下载。

Linux上的差异化服务项目(http://icawww1.epfl.ch/linux-diffserv/)已经提供了更多关于Linux流量控制扩展及其使用的示例。

4 术语

不幸的是,用于描述流量控制元素的术语在文献中远非一致,在Linux流量控制中也存在一些变化。本节的目的是帮助将这些概念放入上下文中。

图5展示了IETF小组“intserv”[6]和“diffserv”[7, 8]中的架构模型和术语,以及Linux流量控制的元素如何与它们相关。请注意,类别在其中扮演着矛盾的角色,因为它们决定了分类的最终结果,并且也可以成为实现特定队列或调度行为机制的一部分。

图 5: intserv 和 diffserv 架构元素与 Linux 内核中流量控制的关系

表1总结了在tc命令行中使用的关键词、内核中使用的文件名(在net/sched/目录下),以及tc源代码中使用的文件名。

表1:用于流量控制元素的关键词和文件名

5 排队规则

每个排队规则提供以下一组函数来控制其操作(参见include/net/pkt_sched.h中的struct Qdisc_ops):

  • enqueue:将数据包排入排队规则。如果排队规则有类别,enqueue 函数首先选择一个类别,然后调用相应排队规则的 enqueue 函数进行进一步排队。

  • dequeue:返回下一个有资格发送的数据包。如果排队规则没有数据包可发送(例如,因为队列为空或因为它们尚未被安排发送),dequeue 返回NULL。

  • requeue:在通过 dequeue 出队后将数据包重新放入队列。这与enqueue不同,因为数据包应精确地放回从 dequeue 移除的位置,并且不应包含在已通过队列的累积流量统计中,因为这在 enqueue 函数中已经完成。

  • drop:从队列中丢弃一个数据包。

  • init:初始化和配置排队规则。

  • change:更改排队规则的配置。

  • reset:将排队规则恢复到其初始状态。所有队列都被清空,计时器停止等。此外,与此排队规则关联的所有类别的排队规则的 reset 函数也会被调用。

  • destroy:移除一个排队规则。它移除所有类别,可能还包括所有过滤器,取消所有待处理的事件,并返回排队规则持有的所有资源(排队规则本身的数据结构除外)。

  • dump:返回用于维护的诊断数据。通常,dump 函数返回所有足够重要的配置和状态变量。

对于所有这些函数,排队规则通常通过指向相应struct Qdisc的指针来引用。

当一个数据包在接口上排队时(net/core/dev.c中的dev_queue_xmit),设备的排队规则的 enqueue 函数(include/linux/netdevice.c中struct device的qdisc字段)被调用。之后, dev_queue_xmit 调用include/net/pkt_sched.h中的 qdisc_wakeup,在该设备上尝试发送刚刚排队的数据包。

qdisc_wakeup 立即调用net/sched/sch_generic.c中的 qdisc_restart,这是轮询排队规则和发送数据包的主要函数。 qdisc_restart 首先尝试从设备的排队规则中获取一个数据包,如果成功,它调用设备的 hard_start_xmit 函数来实际发送数据包。如果由于某种原因发送失败,数据包通过其 requeue 函数返回到排队规则。

当排队规则注意到一个数据包可能需要发送时(例如,计时器到期), qdisc_wakeup 也可以由排队规则调用。TBF是这种排队规则的一个例子。 qdisc_restart 也通过net/core/dev.c中的net_bh调用 qdisc_run_queues。net_bh是网络栈的“下半部”处理程序,每当数据包排队等待进一步处理时执行。

图6说明了该过程。为简单起见,排队规则所做的调用(例如,用于分类)未显示。

图 6: 在入队和发送数据包时调用的函数。

请注意,排队规则从不直接调用传递函数。相反,它们必须等待被轮询。

如果排队规则被编译到内核中,它应该通过net/sched/sch_api.c中的 pktsched_init 注册。或者,它也可以通过 register_qdisc 从其他地方注册,例如,如果排队规则被编译为模块,则从 init_module 函数中注册。

在创建或更改排队规则实例时,会向 init 函数传递一个选项向量(类型为struct rtattr *,在include/linux/rtnetlink.h中声明)。每个选项都使用其类型、值的长度和值(即零个或多个数据字节)进行编码。选项类型和用于值的数据结构在include/linux/pkt_sched.h中声明。通过调用 rtattr_parse 解析选项向量,该函数返回一个指向各个元素的指针数组,按选项类型索引。可以通过宏RTA_PAYLOAD和RTA_DATA分别访问选项的长度和内容。

选项向量通过rtnetlink机制在用户空间程序和内核之间传递。解释 rtnetlink 及其底层 netlink 超出了本文的范围。各源文件的位置在第3节中描述。

排队规则的实例由 32 位数字标识,这些数字分为主要编号和次要编号。通常的表示法是 major:minor。对于排队规则,次要编号始终为零。请注意,这些主要和次要编号与用于设备专用文件的编号无关。

6 类(Classes)

类(classes)可以通过两种方式标识:(1) 由用户分配的类ID,(2) 由排队规则分配的内部ID。后者在一个给定的排队规则内必须是唯一的,可以是一个索引、指针等。请注意,值0是特殊的,当get返回时意味着“未找到”。类ID的类型为u32,而内部ID的类型为unsigned long。在内核中,通常通过内部ID来引用类。只有 getchange 使用类ID。

请注意,多个类ID可能映射到同一个内部类ID。在这种情况下,类ID从分类器向排队规则或类传递附加信息。

类ID的结构类似于排队规则ID,其中主要编号对应于排队规则的实例,次要编号用于在该实例中标识类。

带有类的排队规则提供以下一组函数来操作类(参见include/net/pkt_sched.h中的struct Qdisc_class_ops):

  • graft:将一个新的排队规则附加到类上,并返回之前使用的排队规则。
  • leaf:返回类的排队规则。
  • get:通过类ID查找类并返回内部ID。如果类维护使用计数,get应增加它。
  • put:在之前通过get引用的类被取消引用时调用。如果类维护使用计数,put应减少它。如果使用计数达到零,put可以移除该类。
  • change:更改类的属性。change还用于创建新类(如果适用)——某些排队规则在初始化时创建固定数量的类。
  • delete:处理删除类的请求。它检查类是否未被使用,并在未使用时停用并移除该类。
  • walk:遍历排队规则的所有类,并为每个类调用回调函数。这用于获取排队规则所有类的诊断数据。
  • tcf_chain:返回与类关联的过滤器列表的锚点指针。这用于操作过滤器列表。
  • bind_tcf:将过滤器实例绑定到类。bind_tcf通常与get相同,除非排队规则需要能够显式拒绝类的删除(例如,sch_cbq在类被过滤器引用时拒绝删除类)。
  • unbind_tcf:从类中移除过滤器实例。unbind_tcf通常与put相同。
  • dump_class:返回诊断数据,类似于dump为排队规则所做的。

类通常在排队规则的入队函数中通过调用 tc_classify(位于 include/net/pkt_cls.h)来选择, tc_classify 返回一个包含类ID( classid)和可能的内部ID( class)的 struct tcf_result(同样位于 include/net/pkt_cls.h),详见第7节。 tc_classify 的返回值可以是 -1( TC_POLICE_UNSPEC)或由过滤器返回的监管决定(详见第8节)。 tc_classify 的返回值在 include/linux/pkt_cls.h 中声明。

还有一种本地生成流量的分类快捷方式:如果 skb->priority 包含当前排队规则中某个类的ID,则使用该类,不再进行进一步的分类。当本地生成数据包时, skb->priority(位于 include/linux/skbuff.h 中的 struct sk_buff)被设置为 sk->priority(位于 include/net/sock.h 中的 struct sock)。 sk->priority 可以通过 SO_PRIORITY 套接字选项( sock_setsockoptnet/core/sock.c 中)设置。这种类型的分类对于实现类似于 Arequipa [9] 提供的功能非常有用。

需要注意的是,至少在2.2.3版本之前的内核中,通过 SO_PRIORITY 设置的值被限制在0到7的范围内,因此这种快捷分类方式不起作用。然而,所有排队规则都支持这种方式。还要注意, skb->priority 可以包含其他优先级值,例如从IPv4头部的TOS字节获得的优先级。所有这些值都低于最小的有效类号65536。

在选择类之后,会调用相应内部排队规则的入队函数。排队规则在与其关联的数据结构中的存储方式可能因排队规则的实现而异。

传递给更改函数的选项向量与传递给排队规则初始化函数的向量具有相同的结构。相应的声明也在 include/linux/pkt_sched.h 中。

7 过滤器

过滤器(filters)用于排队规则,将传入的数据包分配到其一个类中。这发生在排队规则的入队操作期间。

图 7 过滤器的结构,第一个过滤器有一个元素列表,第二个过滤器没有内部结构

过滤器被保持在过滤器列表中,这些列表可以根据排队规则的设计,按排队规则或按类进行维护。过滤器列表按优先级升序排列。此外,条目根据它们适用的协议进行键控。这些协议号也在 skb->protocol 中使用,并在 include/linux/if_ether.h 中定义。在同一过滤器列表中,相同协议的过滤器必须具有不同的优先级。

过滤器也可能有内部结构:它可以控制内部元素,然后通过32位句柄引用它们。这些句柄类似于类ID,但它们不分为主号和次号。句柄0始终引用过滤器本身。与类一样,过滤器也有内部ID,通过get函数获取。过滤器的内部组织可以是任意的。图7显示了一个具有内部元素列表的过滤器。

图8显示了检查过滤器及其元素的顺序。顺序处理的链表当然只是过滤器内部结构的众多可能性之一。

图 8: 寻找匹配

过滤器通过以下函数进行控制(参见 include/net/pkt_cls.h 中的 struct tcf_proto_ops):

  • classify 执行分类并返回第8节中描述的 TC_POLICE_... 值之一。如果结果不是 TC_POLICE_UNSPEC,它还会返回选定的类ID,并在 res 指向的 struct tcf_result 中可选地返回内部类ID。如果省略了内部类ID,则必须在 res->class 中存储值零。
  • init 初始化过滤器。
  • destroy 用于删除过滤器。排队规则 sch_cbqsch_atm 也使用 destroy 在删除类时删除过时的过滤器。如果过滤器或其任何元素已注册到类中,则通过调用 unbind_tcf 取消注册。
  • get 通过句柄查找过滤器元素并返回内部过滤器ID。
  • put 在使用 get 引用之前调用,当不再使用该过滤器元素时。
  • change 配置新过滤器或更改现有过滤器的属性。配置参数使用与排队规则和类相同的机制传递。change 通过调用 bind_tcf 注册新过滤器或过滤器元素添加到类中。
  • delete 删除过滤器的一个元素。要删除整个过滤器,必须使用 destroy。这种区分对用户是透明的,并在 net/sched/cls_api:tc_ctl_tfilter 中进行。如果过滤器元素已注册到类中,则通过调用 unbind_tcf 取消注册。
  • walk 遍历过滤器的所有元素并为每个元素调用回调函数。这用于获取诊断数据。
  • dump 返回过滤器或其元素的诊断数据。

注意,RSVP 过滤器的代码在 cls_rsvp.h 中。 cls_rsvp.ccls_rsvp6.c 仅包含正确的头文件集合,并设置了一些参数(主要是 RSVP_DST_LEN),这些参数控制从 cls_rsvp.h 生成的过滤器类型。

图 9:通用过滤器

过滤器在其实例可以分类的数据包范围中有所不同:当使用 cls_fwcls_route 过滤器时,每个排队规则的一个实例可以为所有类分类数据包。这些过滤器从数据包描述符中获取类ID,该描述符之前由协议栈中的某些其他实体(例如 cls_fw 使用防火墙代码的标记功能)存储。我们称这种过滤器为通用过滤器。它们在图9中进行了说明。

图 10:特定过滤器,使用指向类的指针作为内部类ID。

另一种类型的过滤器( cls_rsvpcls_u32)需要每个类的一个或多个过滤器实例或其内部元素。我们称这种过滤器为特定过滤器。同一过滤器列表(例如,对于同一类)上的此类过滤器的多个实例(或其元素)通过内部过滤器ID进行区分,该ID类似于用于类的内部ID。然而,与类不同,过滤器没有“过滤器ID”。相反,它们通过它们注册的排队规则或类以及它们在那里的优先级来标识。

由于特定过滤器在每个类中至少有一个实例或元素,因此它们当然可以存储该类的内部ID,并在分类结果中提供它。这然后允许排队规则快速检索类信息。图10说明了这种情况,其中使用指向类结构的指针作为内部ID。不幸的是,通用过滤器没有提供此信息的手段。因此,它们将 struct tcf_result 中的类字段设置为零,并将查找操作留给排队规则。

从内核版本2.2.5开始,通用过滤器 cls_fwcls_route 也可以成为特定过滤器。当显式地将类绑定到它们时,这种配置更改会自动发生。

8 流量监管(Policing)

流量监管(policing)的目的是确保流量不超过一定的限制。为了简化说明,我们将采用一个宽泛的定义来解释流量监管,并认为它包括所有依赖于流量大小的流量控制行为。

我们考虑四种类型的流量监管机制:(1) 过滤器做出的监管决定,(2) 拒绝将数据包入队,(3) 从“内部”队列规则中丢弃数据包,以及 (4) 在将新数据包入队时丢弃旧的数据包。图11至图15展示了这四种机制。

图 11:入队时的监管;由过滤器做出的决策。

第一种行动是过滤器作出的决定(见图11)。过滤器的分类函数可以返回三种值以表示策略决定(这些值在 include/linux/pkt_cls.h 中声明):

  • TC_POLICE_OK:不请求特殊处理。
  • TC_POLICE_RECLASSIFY:数据包被过滤器选中,但它超出了某些界限,因此应该重新分类(详见下文)。
  • TC_POLICE_SHOT:数据包被过滤器选中并发现违反了界限,因此应被丢弃。

目前正在使用的过滤器 cls_rsvp、cls_rsvp6 和 cls_u32 支持流量监管。流量监管信息通过 tc_classify(在 include/net/pkt_cls.h 中)返回给排队规则的入队函数。然后,排队规则需要采取适当的行动。排队规则 sch_cbq 和 sch_atm 处理 TC_POLICE_RECLASSIFY 和 TC_POLICE_SHOT。sch_prio 排队规则忽略 tc_classify 返回的任何流量监管信息。

过滤器可以使用 tcf_police 函数(在 net/sched/police.c 中)来确定它们选择的流是否符合令牌桶。桶的参数(在 include/linux/pkt_cls.h 中的 struct tc_police 中声明,稍后存储在 include/net/pkt_sched.h 中的 struct tcf_police 中)与 TBF 的大致相同:最大包大小(mtu)、平均速率(rate)、峰值速率(peakrate)和桶大小(burst)。字段 action 包含当接受数据包会超过限制时返回的策略决策代码。如果数据包可以被接受,tcf_police 更新计量器并返回结果中存储的决策代码。

如果没有找到匹配的过滤器,tc_classify 返回 TC_POLICE_UNSPEC。在这种情况下,排队规则通常会丢弃数据包或以低优先级处理它。

有时,希望根据多个令牌桶来监管流量,例如,将流量分为“低”、“高”和“超额”数据包。为了构建这样的配置,需要咨询多个监管功能。为此,tcf_police 返回 TC_POLICE_UNSPEC,此时过滤器会继续处理下一个元素,或者,如果当前过滤器没有更多的符合条件的元素,则调用下一个过滤器。[10] 中给出了这样的配置示例。

图 12: 寻找匹配,带有流量监管

图12说明了当涉及监管时匹配过程如何变化。

图 13: 在入队时进行流量监管;由“内部”队列规则作出的决定

第二种类型的监管发生在排队规则无法入队数据包时(见图13)。在这种情况下,它通常只是丢弃数据包(即,调用 kfree_skb)。一些排队规则还向调用的排队规则提供更复杂的反馈,使其有机会再次尝试入队数据包:如果已设置 reshape_fail 回调函数(在 struct Qdisc 中),“内部”排队规则可以调用它,以便允许“外部”排队规则选择不同的类。如果 reshape_fail 未设置或返回非零值,则必须丢弃数据包。目前,只有 sch_cbq 提供了 reshape_fail 函数。

sch_fifo 和 sch_tbf 在可用时会调用 reshape_fail。

图 14: 在入队后进行流量监管;由“外部”队列规则作出的决定

第三种监管机制是在排队规则决定从“内部”排队规则中丢弃已入队的数据包时应用的,例如,为了为更重要的类的数据包腾出空间(见图14)。这是通过 drop 函数实现的。sch_cbq 的 cbq_dequeue_prio 函数通过 cbq_under_limit 使用这一点来从超过限制的类中移除数据包。

图 15: 丢弃旧数据包以腾出空间给新数据包。

第四种机制(见图15)也会丢弃已经成功入队的数据包:如果排队规则的 enqueue 函数认为新数据包比某些旧数据包更重要,它可以丢弃旧数据包并代之以入队新数据包。通过返回零来向调用者指示这一点。

sch_atm 排队规则

作为一个例子,展示如何添加新的流量控制元素,我们将更详细地研究 ATM 排队规则。它用于将流从默认路径(例如,通过给定接口)重定向到 ATM 虚拟信道(VC)。每个流可以有自己的 ATM VC,但多个流也可以共享同一个 VC。图16说明了此排队规则的结构。

图 16:ATM 队列规则

尽管其分类和排队部分相当通用,但 ATM 排队规则与其他排队规则的不同之处在于,入队到它的数据包可能通过其他路径离开,而不是通过 dequeue 函数或被丢弃:每次调用 dequeue 时,它首先检查所有内部排队规则是否有要发送的数据包,并通过相应的 ATM VC 发送它们。之后,它返回从默认队列中获取的数据包,这些数据包没有被分配给任何类。

为了防止在排队规则仍在使用 VC 时删除它们,相应套接字的引用计数在将 VC 附加到 ATM 排队规则的类时会增加。这发生在 net/socket.c 中的 sockfd_lookup 函数中, atm_tc_change 调用该函数将套接字描述符转换为套接字结构的指针。当类被删除时,它使用 sockfd_put 返回套接字,然后递减引用计数。这组函数大致相当于 fdopenclose 的功能。

ATM 排队规则支持监管响应 TC_POLICE_SHOTTC_POLICE_RECLASSIFY。后者可以以两种不同的方式处理:(1)将数据包分配给新类(由用户配置),或者(2)在传出的 ATM 信元中设置单元丢失优先级位。

ATM 队列规则的代码位于 net/sched/sch_atm.c 中。除了该文件外, include/linux/pkt_sched.h 包含了选项类型(前缀为 TCA_ATM_), net/sched/sch_api.c 包含了初始化部分。此外,通常还需要对 net/sched/Config.innet/sched/Makefile 进行修改,以将新的队列规则包含在配置和构建过程中。

ATM 队列规则的使用说明可以在 Linux 发行版中 ATM 目录下的文件 atm/extra/tc/README 中找到。

10 结论

Linux 流量控制由多种元素组成,它们以多种方式相互作用。所选择的模块化方法产生了一个非常通用的设计,可以轻松应用于大多数当前的流量控制任务,并且可以轻松扩展以适应不典型的应用,例如在 ATM 队列规则中实现的链路层选择。它还构成了 Linux 实现区分服务的基础,统一并推进了许多现有的流量控制概念。

我们已经描述了队列规则、类、过滤器和过滤器内的元素,说明了这些组件之间最重要的交互,并简要介绍了新队列规则的设计。我们希望这些信息对那些旨在理解 Linux 流量控制内部工作原理,特别是希望实现新流量控制功能的人有所帮助。

11 致谢

作者感谢 Jamal Hadi Salim 对本文非常有帮助的讨论和建议,以及 Alexey Kuznetsov 的关键评审和他解释的许多设计决策和流量控制的更深层次细节。

References

  1. Clark, David D.; Shenker, Scott; Zhang, Lixia. Supporting Real-Time Applications in an Integrated Services Packet Network: Architecture and Mechanism, Proceedings of SigComm’92, Baltimore, MD, August 1992. http://ana-www.lcs.mit.edu/anaweb/ps-papers/csz.ps

  2. IETF, Integrated Services (intserv) working group. http://www.ietf.org/html.charters/intserv-charter.html

  3. IETF, Differentiated Services (diffserv) working group. http://www.ietf.org/html.charters/diffserv-charter.html

  4. Bernet, Yoram; Yavatkar, Raj; Ford, Peter; Baker, Fred; Zhang, Lixia; Nichols, Kathleen; Speer, Michael; Braden, Bob. Interoperation of RSVP/Int-Serv and Diff-Serv Networks (work in progress), Internet Draft draft-ietf-diffserv-rsvp-02.txt, February 1999.

  5. Floyd, Sally; Jacobson, Van. Link-sharing and Resource Management Models for Packet Networks, IEEE/ACM Transactions on Networking, Vol. 3 No. 4, pp. 365-386, August 1995.

  6. RFC2205; Braden, Bob (Ed.); Zhang, Lixia; Berson, Steve; Herzog, Shai; Jamin, Sugih. Resource ReSerVation Protocol (RSVP) – Version 1 Functional Specification, IETF, September 1997.

  7. RFC2474; Nichols, Kathleen; Blake, Steven; Baker, Fred; Black, David. Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers, IETF, December 1998.

  8. RFC2475; Blake, Steven; Black, David; Carlson, Mark; Davies, Elwyn; Wang, Zheng; Weiss, Walter. An Architecture for Differentiated Services, IETF, December 1998.

  9. RFC2170; Almesberger, Werner; Le Boudec, Jean-Yves; Oechslin, Philippe. Application REQuested IP over ATM (AREQUIPA), IETF, July 1997.

  10. Almesberger, Werner; Hadi Salim, Jamal; Kuznetsov, Alexey. Differentiated Services on Linux (work in progress), Internet Draft draft-almesberger-wajhak-diffserv-linux-01.txt, May 1999.

原文地址:https://mp.weixin.qq.com/s/77OhzYdlzJcqPRA1X6EVjw