| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> TC-Traffic Control in Linux -> 正文阅读 |
|
[C++知识库]TC-Traffic Control in Linux |
Linux中的流量控制, 一篇手册的翻译,很遗憾随着项目的结束对TC应用的不多,仅仅写到这里了。 1. TC原理Linux操作系统中的流量控制器TC(Traffic Control)用于Linux内核的流量控制,主要是通过在输出端口处建立一个队列来实现流量控制。
一般只能限制网卡发送的数据包,不能限制网卡接收的数据包,所以可以通过改变发送次序来控制传输速率。Linux流量控制主要是在输出接口排列时进行处理和实现的。 2. 规则2.1 队列(Queues) 和排队规则(Queueing Disciplines)通过对包进行排队(queuing),我们可以决定数据的发送方式(the way in which data is SENT)。但理解下面这一点非常重要:我们只能对发送(transmit)的数据进行整形(shape the data)。 互联网的工作机制决定了接收端无法直接控制发送端的行为。这就像你家的 (实体!)邮箱一样:除非能联系到所有人(告诉他们未经同意不要寄信给你),否则 你无法控制别人寄多少东西过来。😉 但与实际生活不同的是,互联网基于 TCP/IP 协议栈,这多少会带来一些帮助。TCP/IP 无法提前知道两台主机之间的网络带宽,因此开始时它会以越来越快的速度发送数据(慢启 动),直到开始出现丢包,这时它知道已经没有可用空间来存储这些待发送的包了,因此就会 降低发送速度。TCP/IP 的实际工作过程比这个更智能一点,我们后面会再讨论。 这就好比你留下一半的信件在实体邮箱里不取,期望别人知道这个状况后会停止给你寄新的信件。 但不幸的是,这种方式只对互联网管用,对你的实体邮箱无效 😃 如果内网有一台路由器,你希望限制某几台主机的下载速度,那你应该找到发送数据到 这些主机的路由器内部接口(inner interface of your router),然后在这些 路由器内部接口上做 整流(traffic shaping,流量整形)。 此外,还要确保链路瓶颈(bottleneck of the link)也在你的控制范围内。例如,如果网 卡是 100Mbps,但路由器的链路带宽是 256Kbps,那首先应该确保不要发送过多数据给路由器,因为它可能扛不住。否则,链路控制和带宽整形的决定权就不在主机侧而到路由器侧了。要达到限速目的,我们需要对“发送队列”有完全的把控(”own the queue”),这里的“发送队列”也就是整条链路上最慢的一段(slowest link in the chain)。 幸运的是,大多数情况下这个条件都是能满足的。 2.2 Simple, classless qdisc(简单、不分类排队规则)如前所述,排队规则(queueing disciplines)改变了数据的发送方式。 不分类(或称无类别)排队规则(classless queueing disciplines)可以对某个网络 接口(interface)上的所有流量进行无差别整形。包括对数据进行:
与 classless qdisc 对应的是 classful qdisc,即有类别(或称分类别)排队规则,后者是一个排队规则中又包含其他排队规则(qdisc-containing-qdiscs)!😮 先理解了 classless qdisc,才能理解 classful qdisc。 2.2.1 pfifo_fast(先入先出队列)如名字所示,这是一个先入先出队列(First In, First Out),因此对所有包都一视同仁。 pfifo_fast 有三个所谓的 “band”(可理解为三个队列),编号分别为 0、1、2:
不要将 2.2.1.1 参数与用法
下面介绍这份写死的配置是什么样的。
TOS 字段占用 4 个比特,各 bit 含义如下:
第二列是对应的十进制表示,第三列是对应的含义。例如,
例如,priority 4 会映射到 band 1。priomap 还能列出 priority > 7 的那些 不是由 TOS 映射、而是由其他方式设置的优先级。例如,下表列出了应用(application)是如何设置它们的 TOS 字段的,来自 RFC 1349(更多信息可阅读全文)
当一个包到来时,会从 bucket 中拿到一个 token,然后收集这个包的信息,最后从 bucket 中删除这个 token。 这个算法和 token flow、data flow 结合起来,会产生三种可能的场景:
第三种非常重要,因为它使我们能够对数据可用的带宽进行整形(administratively shape the bandwidth)。 积压的 token 使得超过限速的短时抖动数据仍然能发送,不会丢包,但持续的 overload 会导致数据不断被 delay,然后被丢弃。
2.2.2.1 参数与用法虽然通常情况下并不需要修改 TBF 配置参数,但我们还是可以看一下有哪些。 首先,永远可用的(always available)参数:
bucket 的大小,单位是字节。这是累积可用的 token 所支持的最大字节数( maximum amount of bytes that tokens can be available for instantaneously)。总 体来说,越大的整流速率(shaping rates)需要越大的缓冲区。要在 Intel 网卡 上实现 10Mbps 整流,你至少需要 10KB 缓冲区。 如果缓冲区太小,可能会丢包,因为 token 到来太快导致无法放入 bucket 中。
“零长度”的包占用的并不是零带宽(A zero-sized packet does not use zero bandwidth)。例如对于以太网,任何一个包的字节数不会少于 64。 Minimum Packet Unit(最小包单元)决定了一个包所使用的最小 token 量(the minimal token usage for a packet)。
速度旋钮(speedknob)。
如前所述,默认情况下,包到了之后只要有 token 就会被立即发送。这可能不是你期 望的,尤其当 bucket 很大的时候。 p e a k r a t e peakrate peakrate 可指定 bucket 发送数据的最快速度。通常来说,这需要做的 就是:放行一个包 - 等待恰当的时长 - 放行下一个包。通过计算等待时长,最终实现 了 peakrate 效果。 但实际中,由于 Unix 默认的 10ms 定时器精读限制,如果平均每个包 10K bits , 我们只能做到 1 M b p s 1Mbps 1Mbps peakrate!( 10 K b / 10 m s = 1000 K b p s = 1 M b p s 10Kb/10ms = 1000Kbps = 1Mbps 10Kb/10ms=1000Kbps=1Mbps,译注)。
1 M b i t / s 1Mbit/s 1Mbit/s 的 peakrate 通常并不是很有用,因为实际中的带宽要远大于此。实现更高 peakrate 的一种方式是:每个 timer tick 发送多个包,在效果上就好像我们创建 了第二个 bucket! 这第二个 bucket 默认只有一个包(defaults to a single packet),完全算不上一个 bucket。 计算最大可能的 peakrate 时,用 MTU 乘以 100(更准确地说,乘以 HZ 数,例如 Intel 上是 100,Alpha 上是 1024)。 2.2.2.2 示例配置一个简单但非常有用的配置:
为什么说这个配置很有用呢?如果你有一个 queue 很大的网络设备,例如 DSL modem 或 cable modem,而且用一个快速设备(例如以太网接口)连接到这个网络设备,那你会发现 大文件上传会严重影响实时交互。 这是因为上传的数据会被缓存到 modem 的 queue 里,而且缓存的数据量很大(以提升吞吐) 。但这并不是期望的,你希望的是 queue 不要太大,这样能保证交换式数据的实时性,因 此能在上传数据过程中同时做其他事情。 上面的配置将发送速率降低到了 modem 不会对数据进行排队缓存(queuing)的水平 —— 此时 queue 前移到了 Linux 中,而我们可以将它控制在一个合理的范围内。 这里的 220kbit 是上行链路的真实带宽乘以一个系数,如果你的 modem 足 够快,可以将 burst 调大一些。 2.2.3 SFQ(Stochastic Fairness Queueing,随机公平排队)随机公平排队(SFQ)是公平排队算法族的一个简单实现。相比其他算法,SFQ 精准性要差 一些,但它所需的计算量也更少,而结果几乎是完全公平的(almost perfectly fair)。 SFQ 中的核心是 conversion(会话)或 flow(流),大部分情况下都对应一个 TCP session 或 UDP stream。每个 conversion 对应一个 FIFO queue,然后将流量分到不 同 queue。发送数据时,按照 round-robin 方式,每个 session 轮流发送。 这种机制会产生非常公平的结果,不会因为单个 conversion 太大而把其他 conversion 的带宽都 挤占掉。SFQ 被称为**“随机的”(stochastic)是因为它其实并没有为每个 session 分配一个 queue**,而是用算法将流量哈希到了一组有限的 queue。 但这里会出现另一个问题:多个 session 会可能会哈希到同一个 bucket(哈希槽), 进而导致每个 session 的 quota 变小,达不到预期的整流带宽(或速度)。为避免这个 问题过于明显,SFQ 会不断变换它使用的哈希算法,最终任何两个会话冲突的持续时间都不会很长,只会有几秒钟。 SFQ 只有在实际出向带宽已经非常饱和的情况下才有效,这一点非常重要!否则, Linux 机器上就不存在 queue,因此也就没用效果。稍后会看到如何将 SFQ 与其他 qdisc 相结合来实现一般情况下的公平排队。 说的更明确一点:没用配套的整流配置的话,单纯在(连接 modem 的)以太网接口上配 置SFQ 是毫无意义的。 2.2.3.1 参数与用法SFQ 大部分情况下默认参数就够了, -perturb 每隔多少秒就重新配置哈希算法。如果这个参数没设,哈希算法就永远不会重新配置。 建议显式设置这个参数,不要为空。10s 可能是个不错的选择。 -quantum 在轮到下一个 queue 发送之前,当前 queue 允许出队(dequeue)的最大字节数。默认是 一个 MTU。不建议设置为小于 MTU 的值。 -limit SFQ 能缓存的最大包数(超过这个阈值将导致丢包)。 2.2.3.2 示例配置如果你有一个带宽已经饱和的网络设备,例如一个电话调制解调器(phone modem),那下 面的配置有助于提高公平性:
解释: - 2.3 使用建议:何时选择哪种队列?总结起来,上面几种都是简单的 qdisc,通过重排序(reordering)、降速(slowing)或 丢包(dropping)来实现流量管理。 选择使用哪种 qdisc 时,下面几点可供参考。其中提到了几种在第 14 章才会介绍到的 qdisc。 -单纯对出向流量限速(slow down outgoing traffic),推荐使用** TBF**。如果是 针对大带宽进行限速,需要将 bucket 调大。
2.4 术语为方便理解接下来更复杂的配置,我们需要先引入一些概念。由于这项技术本身比较复杂, 发展也还处在较为早期的阶段,因此大家可能会用不同的术语描述同一样东西。 下列术语大体上来自 draft-ietf-diffserv-model-06.txt, An Informal Management Model for Diffserv Routers。想进一步了解一些术语的定义,可参考这份文档。 我们接下来会用到下列术语:
有了以上概念,我们来看它们都是在哪里用到的。
上图中的框代表 Linux 内核。最左侧的箭头表示流量从外部网络进入主机。然后进入 Ingress Qdisc,这里会对包进行过滤(apply Filters),根据结果决定是否要丢弃这个包。这个过程称为 “Policing”。这个过程发生在内核处理的很早阶段,在穿过大部 分内核基础设施之前。因此在这里丢弃包是很高效的,不会消耗大量 CPU。 如果判断允许这个包通过,那它的目的端可能是本机上的应用(local application),这 种情况下它会进入内核 IP 协议栈进行进一步处理,最后交给相应的用户态程序。另外,这 个包的目的地也可能是其他主机上的应用,这种情况下就需要通过这台机器 Egress Classifier 再发送出去。主机程序也可能会发送数据,这种情况下也会通过 Egress Classifier 发送。 Egress Classifier 中会用到很多 qdisc。默认情况下只有一个: 此时包位于 qdisc 中了,等待内核召唤,然后通过网络接口(network interface)发送出去。 这称为“出队”(”dequeueing”)。 以上画的是单网卡的情况。在多网卡的情况下,每个网卡都有自己的 ingress 和 egress hooks。 2.5 Classful qdisc(分类别排队规则)如果想对不同类型的流量做不同处理,那 classful qdisc 非常有用。其中一种是 CBQ( Class Based Queueing,基于类别的排队),由于这种类型的 qdisc 使用太广泛了,导致 大家将广义上基于类别的排队等同于 CBQ(identify queueing with classes solely with CBQ),但实际并非如此。 CBQ 只是其中最古老 —— 也是最复杂 —— 的一种。它的行为有时可能在你的意料之外。 那些钟爱 “sendmail effect” 的人可能感到震惊。
接下来介绍更多关于 CBQ 及其类似 qdisc 的信息。 2.5.1 Classful qdisc & class 中的 flow当流量进入一个 classful qdisc 时,该 qdisc 需要将其发送到内部的某个 class —— 即 需要对这个包进行“分类”。而要这个判断过程,,实际上是查询所谓的“过滤器”( ‘filters’)。过滤器是在 qdisc 中被调用的,而不是其他地方,理解一点非常重要! 过滤器返回一个判决结果给 qdisc,qdisc 据此将包 enqueue 到合适的 class。 每个 subclass 可能会进一步执行其他 filters,以判断是否需要进一步处理。如果没有 其他过滤器,这个 class 将把包 enqueue 到它自带的 qdisc。 除了能包含其他 qdisc,大部分 classful qdisc 还会执行流量整形。这对包调 度(packet scheduling,例如,基于 SFQ)和速率控制(rate control)都非常有用。 当高速设备(例如,以太网)连接到一个低速设备(例如一个调制解调器)时,会用到这个 功能。 如果只运行 SFQ,那将什么事情都不会发生,因为包会无延迟地进入和离开你的路由 器:网卡的发送速度要远大于真实的链路速度。瓶颈不在主机中,就无法用“队列”(queue )来调度这些流量。 2.5.2 qdisc 大家庭:roots, handles, siblings and parents
关于 handle:
2.5.2.1 如何用过滤器(filters )对流量进行分类综上,一个典型的 handle 层级如下:
但不要被这棵树迷惑!不要以为内核位于树的顶点,网络位于下面。包只会通过 root qdisc 入队或出队(get enqueued and dequeued),这也是内核唯一与之交互的部分( the only thing the kernel talks to)。 一个包可能会被链式地分类如下(get classified in a chain):
最后到达 attach 到
在这种情况下,attach 到 root qdisc 的 filter 决定直接将包发给 12:2。 2.5.2.2 包是如何从 qdisc 出队(dequeue)然后交给硬件的当内核决定从 qdisc dequeue packet 交给接口(interface)发送时,它会
在这个例子中,内核需要遍历整棵树,因为只有 简单来说,嵌套类(nested classes)只会和它们的 parent qdiscs 通信,而永远不会直 接和接口交互。内核只会调用 root qdisc 的 最终结果是,classes dequeue 的速度永远不会超过它们的 parents 允许的速度。而这正 是我们所期望的:这样就能在内层使用一个 SFQ 做纯调度,它不用做任何整形的工作 ;然后在外层使用一个整形 qdisc 专门负责整形。 2.5.3 PRIO qdisc(优先级排队规则)PRIO qdisc 实际上不会整形,只会根据设置的过滤器对流量进行分类。 可以将 当一个包 enqueue 到 PRIO qdisc 之后,它会根据设置的 filters 选择一个 class ,并将包送到这个 class。默认情况下会创建三个 class。每个 class 默认情况下都包含一 个纯 FIFO qdisc,没有其他内部结构,但你可以用其他类型的 qdisc 替换掉 FIFO。 当从 PRIO qdisc 取出(dequeue)一个包时,会先尝试 如果想基于 tc filters 而不仅仅是 TOS flags 做流量优先级分类时,这个 qdisc 会非常 有用。还可以向这三个预置的 classes 添加额外的 qdisc,毕竟 由于 PRIO** 没有流量整形功能**,因此针对 SFQ 的忠告也适用于这里:
用正式的术语来说,PRIO qdisc 是一个** work-conserving 调度器(随到随发)**。 2.5.3.1 参数与用法下面几个参数能被
需要创建的 band 数量。这个每个 band 实际上都是一个 class。如果改变这个配置, 还需要同时修改
如果没有提供 tc filters 来指导如何对流量分类,那 PRIO qdisc 将依据 TC_PRIO 优先级来决定优先级。这里的工作方式与 PRIO qdisc 里面的 band 都是 class,默认情况下名字分别为 major:1、 major:2、 major:3, 因此如果你的 PRIO qdisc 是 12:,那 tc filter 送到 12:1 的流量就有更高的优先级。 重复一遍:band 0 对应的 minor number 是 1! band 1 对应的 minor number 是 2 ,以此类推。 2.5.3.2 示例配置我们将创建一棵如下所示的树:
高吞吐流量(Bulk traffic)将送到
然后查看创建出来的 qdisc:
可以看到,band 0 已经有了一些流量,而且在执行这条命令的过程中,刚好又发送了一个 包! 现在我们来用
可以看到,所有的流量都进入了优先级最低的 handle
正如预期 —— 所有额外流量都进入了 2.5.4 著名的 CBQ(Class Based Queueing)qdisc前面提到,CBQ(Class Based Queueing,基于类的排队) 是最复杂、最花哨、最少被理 解、也可能是最难用对的 qdisc。这并非因为它的发明者都是魔鬼或者能力不够,而是 因为 CBQ** 算法经常不够精确**,而这是由于它与 Linux 的工作方式不是太匹配造成的。 除了是 classful qdisc 之外,CBQ 还是一个整流器(shaper),作为一个整流器来说, 其实它工作地并不是非常理想。理想的工作方式应该是这样的:如果想将一个 10Mbps 的连 接整形为 1Mbps,那这条链路应该有 90% 的时间是空闲的。否则,我们就需要 throttle 来确保链路 90% 的时间是空闲的。 但空闲时间是很难测量的,CBQ 的方式是:用硬件层连续两次请求数据的时间间隔( 毫秒)来推算。这可以用来近似估计链路的空闲状态(how full or empty the link is)。 这种测量方式是非常间接的,因此结果并不总是很准确。例如,接口的物理带宽是 100Mbps ,但它可能永远打不到 100Mbps,而原因可能是网卡驱动写的太烂。另一个例子,PCMCIA 网 卡永远打不到 100Mbps,这是由于其总线设计导致的 —— 因此,又回到那个问题:应该如何计算空闲时间? 当考虑到非纯物理网络设备(not-quite-real network devices)时,例如 PPP over Ethernet 或 PPTP over TCP/IP,情况会更加糟糕。在这些场景中,有效带宽可能是由到用户空间的管道(pipe)效率决定的 —— 这个值可能很高。 真正测量过的人会发现,CBQ 并不是永远很精确,有时甚至完全偏离了真实值。 但在某些场景下,CBQ 能很好地满足需求。基于本文的介绍,你应该能恰当地配置 CBQ,使 其在大部分情况下都工作良好。 2.5.4.1 CBQ shaping 详解如前所述,CBQ 的工作原理是:在发送包之前等待足够长的时间,以将带宽控制到期望 的阈值。为实现这个目标,它需要计算包之间的等待间隔。 系统在运行过程中会计算一个有效空闲时间(effective idletime):用指数加权移动平均( exponential weighted moving average,EWMA)来计算,这个算法假设包的优先级大小 是指数变化的,越近的包(recent packets)优先级越高。UNIX 的 平均空闲时间( 如果发生 overlimit,理论上 CBQ 会严格等待 calculated_idletime,然后才发生下一个 包,然后再次 throttle 自己。但此时也要注意 下面是整形(shaping)相关的配置参数:
在内部,CBQ 有很多优化。例如,在 dequeue 包时,已经明确知道没有数据的 class 都会跳过。 Overlimit 的 class 会通过降低其有效优先级(effective priority)的方式进行惩罚。 所有这些都是很智能也很复杂的。 2.5.4.2 CBQ classful behaviour除了整形之外,基于前面提到的 idletime 近似,CBQ 也能完成类似 PRIO queue 的功能 ,因为 class 可以有不同优先级,优先级高的总是限于优先级低的被 poll。 每次硬件层请求一个数据包来发送时,都会开启一个 weighted round robin (WRR)过程, 从优先级最高的 class 开始(注意,优先级越高对应的 priority number 越小)。 优先级相同的 class 会作为一组,依次判断它们是否有数据要发送。 下列参数控制 WRR 过程: |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 23:22:32- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |