流量控制--3.Linux流量控制的组件

Linux流量控制的组件

流量控制元素与Linux组件之间的相关性:

traditional element Linux component
入队列 修订:从用户或网络接收报文
整流 class 提供了整流的能力
调度 一个 qdisc 就是一个调度器。调度器可以是一个简单的FIFO,也可以变得很复杂,包括classes和其他qdiscs,如HTB。
分类 filter 对象通过一个classifier 对象执行分类。严格上讲,除filter之外的组件不会用到分类器。
策略 policer仅作为filter的一部分而存在。
丢弃 drop 流量要求使用一个带 policerfilter ,动作为"drop"
标记 dsmark qdisc 用于标记报文。
入队列; 驱动队列位于qdisc和网络接口控制器(NIC)之间。驱动队列给上层(IP栈和流量控制子系统)提供了数据异步入队列的位置(后续由硬件对数据进行操作)。队列的大小由Byte Queue Limits (BQL)动态设置。

4.1 qdisc

简单讲,一个qidsc就是一个调度器。每个出接口都需要某种类型的调度器,默认的调度器为FIFO。Linux下的其他qdisc会根据调度器的规则来重新安排进入调度器队列的报文。

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

classful qdiscs 可以包含,并提供了可以附加到过滤器的句柄。一个classful qidsc可以不使用子类,但这样通常会消耗CPU周期和其他系统资源,且毫无意义。

classless qdiscs 不包含类,也不会附加过滤器。由于一个classless qdisc不包含任何类的子类,因此不能使用分类,意味着不能附加任何过滤器。

在使用中可能会对术语root qdisc 和ingress qdisc产生混淆。实际中并不存在真正的排队规则,而是连接流量控制结构的出站(出流量)和入口(入流量)的位置。

每个接口都会包含root qdisc 和ingress qdisc。最主要和最常用的是egress qdisc,即root qdisc,它可以包含任何排队规则(qdiscs)以及潜在的和类结构。大部分文档适用于root qdisc及其子qdisc。一个接口传输的流量会经过egress或root qdisc。

一个接口上接收到的流量会经过ingress qdisc。由于其功能的限制,不允许创建子类,且仅允许存在一个被过滤器 附加的对象。事实上,ingress qdisc仅仅是一个对象,可以在其上附加策略器来限制网络接口上接收的流量。

总之,由于egress qdisc包含一个真正的qdisc,且具有流量控制系统的全部功能,因此可以使用egress qdisc做很多事情。而一个ingress qdisc仅支持一个策略器。除非另有说明,本文后续将主要关注附加到root qdisc的流量控制结构。

4.2 类

类仅会存在于classful qdisc (如 HTBCBQ)。类非常灵活,可以包含多个子类或单个子qdisc。一个子类本身也可以包含一个classful qdisc,通过这种方式可以实现复杂的流量控制场景。

任何类都可以附加任意多的过滤器,从而允许选择一个子类或使用过滤器来重新分类或直接丢弃进入特定类的流量。叶子类是qdisc中的终止类,它包含一个qdisc(默认是FIFO),且不会包含子类。任何包含子类的类都属于内部类(或root类),而非叶子类。

4.3 过滤器

过滤器是Linux流量控制系统中最复杂的组件,提供了将流量控制的主要元素粘合到一起的机制。过滤器最简单和最明显的角色就是对报文进行分类(Section 3.3,“Classifying”)。Linux过滤器允许用户使用多个或单个过滤器来将报文分类到一个输出队列。

过滤器可能附加到classful qdiscs或,但入队列的报文总是首先进入root qdisc。在报文经过的root qdisc上附加的过滤器后,报文可能被重定向到任何子类(子类可以包含自己的过滤器),后续可能对报文进一步分类。

4.4 分类器

过滤器的对象,可以使用tc进行操作,且可以使用不同的分类机制,其中最常用的是u32分类器。u32分类器允许用户根据报文的属性选择报文。

分类器可以作为过滤器的一部分来标识报文的特征或元数据。Linux分类器对象可以看作是流量控制分类的基本操作和基本机制。

4.5 策略器

该机制仅作为Linux流量控制中的过滤器的一部分。一个策略器可以在速率超过指定速率时执行一个动作,在速率低于指定速率时执行另一个动作,善用策略可以模拟出一个三色表。参见 Section 10,“Diagram”

虽然策略整流 都是流量控制中用来限制带宽的基本元素,但使用策略器并不会导致流量延迟。它只会根据特定的准则来执行某个动作。参见Example 5,“tc filter

4.6 丢弃

该流量控制机制仅作为策略器的一部分。任何附加到过滤器的策略器都包含一个drop动作。

注:策略器是流量控制系统中唯一可以显式地丢弃报文的地方。策略器可以限制入队列的报文的速率,或丢弃匹配特定模式的所有流量。

流量控制系统中,报文的丢失可能是由某个动作引起的副作用。例如,如果使用的调度器使用和GRED一样的方法控制流时,报文将被丢弃。

或者,当出现突发或超负荷时,如果整流器或调度器的缓冲用尽,也可能会丢弃报文。

4.7 句柄

每个类和classful qdisc(Section 7,“Classful Queuing Disciplines (qdiscs)”)都要求在流量控制结构中存在一个唯一的标识符,该唯一标识符被称为句柄,每个句柄包含两个组成成员,一个主号和一个次号。用户可以根据以下规则随意分配这些号。

类和qdiscs的句柄号:

主号

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

次号

  • 如果次号为0,则表明该对象为qdisc,否则表明该对象为一个类。所有共享同一个qdisc的类必须包含一个唯一的次号。

特殊的句柄 ffff:0 保留给ingress qdisc使用。

句柄作为tc过滤器的classid和flowid的目标参数,同时也是用户侧应用使用的标识对象的外部标识符。内核为每个对象维护内部标识符。

4.8 txqueuelen

可以使用ipifconfig目录获取当前传输队列的长度。令人困惑的是,这些命令对传输队列长度的命名各部不同:

$ifconfig eth0

eth0      Link encap:Ethernet  HWaddr 00:18:F3:51:44:10
          inet addr:69.41.199.58  Bcast:69.41.199.63 Mask:255.255.255.248
          inet6 addr: fe80::218:f3ff:fe51:4410/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:435033 errors:0 dropped:0 overruns:0 frame:0
          TX packets:429919 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:65651219 (62.6 MiB)  TX bytes:132143593 (126.0 MiB)
          Interrupt:23
      
$ip link

1: lo:  mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:18:f3:51:44:10 brd ff:ff:ff:ff:ff:ff

Linux默认的传输队列的长度为1000个报文,这是一个相当大的缓冲(特别是当带宽比较低时)。(为了理解其原因,请参见针对延迟和吞吐量的讨论,特别是缓冲膨胀。)

更有趣的是,txqueuelen仅用作这些排队规则的默认队列长度。

  • pfifo_fast (Linux的默认队列规则)
  • sch_fifo
  • sch_gred
  • sch_htb (仅用于默认队列)
  • sch_plug
  • sch_sfb
  • sch_teql

txqueuelen参数控制上述QDiscs的队列大小。对于大多数的队列规则,tc命令行中的limit参数会覆盖默认的txqueuelen 值。总之,如果没有使用上述的任意一种队列规则或覆盖了默认的队列长度,那么txqueuelen 就没有任何意义。

可以使用ipifconfig命令来配置接口的传输队列长度。

ip link set txqueuelen 500 dev eth0

注意:ip命令使用qlen来表示txqueuelen

4.9 驱动队列(即ring buffer)

在IP栈和网络接口控制器之间存在驱动队列。该队列通常使用先进先出的ring buffer来实现(可以认为是一个固定长度的缓冲)。驱动队列不包含任何报文数据,仅包含指向其他数据结构(socket kernel buffers,简称SKBs)的描述符,SKB包含报文数据,并在整个内核中使用。

驱动队列的输入源为保存了完整IP报文的IP栈,这些报文可能是本地的,或当设备作为路由器时接收到的需要从一个NIC路由到另一个NIC的报文。IP栈会将报文添加到驱动队列,并由硬件驱动出队列,在传输时会通过数据总线发送到NIC硬件。

驱动队列存在的原因是为了保证在任何时候,当系统需要传输数据时,NIC会立即传输该数据。即,驱动队列为IP栈和硬件操作提供了一个异步处理数据的位置。一个备选方案是,一旦物理媒介就绪时就向IP栈查询可用的报文。但由于对这类请求的响应不可能是即时的,因此这种设计浪费了宝贵的传输机会,导致吞吐量降低。相反的方案是,IP栈在创建一个报文后会等待硬件就绪,这种方案同样不理想,因为IP栈将无法继续其他工作。

更多关于驱动队列的细节参见5.5章节

4.10 Byte Queue Limits(BQL)

Byte Queue Limits (BQL) 是Linux内核(> 3.3.0 )引入的一个新特性,用于尝试自动解决驱动程序队列大小的问题。该特性添加了一层处理,它会根据当前系统的情况计算出避免出现饥饿的最小缓冲大小,以此作为报文进入驱动队列的依据。回顾一下,队列中的数据总量越少,队列中的报文的最大延迟越小。

需要注意的是,BQL不会修改驱动队列的实际长度,相反,它会计算当前时间可以入队列的数据的(字节数)上限。当队列中的数据超过该限制之后,驱动队列的上层需要决定是否保留会丢弃这部分数据。

当发生两种情况时会触发BQL机制:当报文进入驱动队列,或当线路上的传输已经结束。下面给出了一个简单的BQL算法。LIMIT指BQL计算出的值。

****
** After adding packets to the queue
****

if the number of queued bytes is over the current LIMIT value then
        disable the queueing of more data to the driver queue
    

BQL基于测试设备是否发生了饥饿现象,如果是,则增加LIMIT来允许更多的报文入队列,以此降低饥饿的概率。如果设备繁忙,且后续还有报文持续传输到队列中,当队列中的报文大于当前系统所需要的数量时,会降低LIMIT来限制饥饿。

下面给出一个真实的例子,可以帮助了解BQL能够在多大程度上影响排队的数据量。在一台服务器上,驱动队列的大小默认为256个描述符。由于以太网的MTU为1500字节,意味着驱动队列中的报文最大为256 * 1,500 = 384,000字节(禁用TSO,GSO等)。但此时BQL计算出的限制值为3012字节。如你所见,BQL大大限制了进入队列的数据量。

从名称的第一个单词可以推断出BQL的一个有趣的特点--字节。与驱动队列和其他大多数报文队列不同,BQL操作的是字节。这是因为相比报文数或描述符,字节数与物理媒介的传输时间有着更为直接的关系。

BQL将进入队列的数据量限制到避免饿死所需的最小数量,从而减少了网络延迟。它还有一个非常重要的副作用,那就是将大多数报文排队的点从驱动队列(一个简单的FIFO)移动到排队规则(QDisc)层,从而实现更复杂的排队策略。下一节将介绍Linux的QDisc层。

4.10.1 设置BQL

BQL算法是自适应的,并不需要过多的人为接入。但如果需要关注低比特率下的最佳延迟,则有可能需要覆盖计算出的LIMIT值。可以在/sys目录根据NIC的名称和位置下找到BQL的状态和配置。例如我的一台服务器上的eth0 的目录为:

可以使用ethtool -i <接口名称>来查看设备的PCI号。

/sys/devices/pci0000:00/0000:00:14.0/net/eth0/queues/tx-0/byte_queue_limits

该目录中的文件为:

  • hold_time: 修改LIMIT的间隔时间,单位毫秒
  • inflight: 队列中还没有传输的字节数
  • limit: BQL计算出的LIMIT值。如果NIC驱动不支持BQL,则为0
  • limit_max: 可配置的LIMIT的最大值,减小该值可以优化延迟
  • limit_min: 可配置的LIMIT的最小值,增大该值可以优化吞吐量

要对可排队的字节数设置上限,请将新值写入limit_max文件:

echo "3000" > limit_max

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


linux常用进程通信方式包括管道(pipe)、有名管道(FIFO)、信号(signal)、消息队列、共享内存、信号量、套接字(socket)。管道用于具有亲缘关系的进程间通信,有名管道的每个管道具有名字,使没有亲缘关系的进程间也可以通信。信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除
Linux性能观测工具按类别可分为系统级别和进程级别,系统级别对整个系统的性能做统计,而进程级别则具体到进程,为每个进程维护统计信息。&#xD;&#xA;&#xD;&#xA;按实现原理分,可分为基于计数器和跟踪以及剖析。含义如下:&#xD;&#xA;&#xD;&#xA;计数器:内核维护的统计数据,通常为无符号整型,用于对发生的事件计数,比如,网络包接收计数器,磁
本文详细介绍了curl命令基础和高级用法,包括跳过https的证书验证,详细追踪整个交互过程,可用于调用网络后端接口,诊断http和https网络服务故障。
本文包含作者工作中常用到的一些命令,用于诊断网络、磁盘占满、fd泄漏等问题。命令包括ping、fping、tcpdump、lsof、netstat、/proc/$pid/fd、du、grep、traceroute、dig。
linux的平均负载表示运行态和就绪态及不可中断状态(正在io)的进程数目,用uptime查看到负载很高,既有可能是CPU利用率高,也可能是大量在等待io的进程导致,用mpstat查看每个CPU的使用情况,查看CPU的使用率或者CPU花在等待io的时间,接着用pidstat定位具体的进程
CPU上下文频繁切换会导致系统性能下降,切换分为进程切换、线程切换及中断切换,进程切换的开销较大,除了需要保存寄存器和程序计数器中的值还需保存全局变量、栈等到内存中,以便下次运行恢复,而同一进程中的线程切换开销会小很多,只需更新寄存器和线程独有的栈,共享资源如打开的文件、全局变量等无需切换,当硬件中
1.top命令 作用:该命令可以按CPU使用.内存使用和执行时间对任务进行排序,常用来监控系统中占用CPU或内存较高的程序及CPU和内存的负载。 默认视图: 当想看系统负载时,可观察汇总的%CPU中的us用户进程和sy系统进程是否占用CPU很高,相加接近100%就说明占用很高了,有些程序可能得不到及
文章浏览阅读1.8k次,点赞63次,收藏54次。Linux下的目录权限!!!粘滞位!!!超详解!!!
文章浏览阅读1.6k次,点赞44次,收藏38次。关于Qt的安装、Windows、Linux、MacBook_mack book 安装qt
本文介绍了使用shell脚本编写一个 Hello
文章浏览阅读1.5k次,点赞37次,收藏43次。【Linux】初识Linux——了解操作系统的发展历史以及初次体验Linux编程环境
文章浏览阅读3k次,点赞34次,收藏156次。Linux超详细笔记,个人学习时很认真的记录的,觉得好的麻烦点个赞。
文章浏览阅读6.8k次,点赞109次,收藏114次。【Linux】 OpenSSH_9.3p1 升级到 OpenSSH_9.5p1(亲测无问题,建议收藏)_openssh_9.5p1
文章浏览阅读3.5k次,点赞93次,收藏78次。初识Linux中的线程,理解线程的各种概念,理解进程地址空间中的页表转换,介绍pthread线程库并理解线程库!
文章浏览阅读863次。出现此问题为Linux文件权限问题,解决方案为回到引擎目录执行命令。输入用户密码后运行./UnrealEditor。_increasing per-process limit of core file size to infinity.
文章浏览阅读2.9k次。使用文本编辑器:打开CSV文件,并使用文本编辑器(如Notepad++、Sublime Text、Visual Studio Code等)来查看文件的字符编码格式。通常在编辑器的底部状态栏或设置中可以找到当前编码的显示。请注意,上述方法并非绝对准确,特别是当文件没有明确的编码标识时。因此,如果你发现CSV文件在不同的工具或方法中显示不同的编码格式,可能需要进行进一步的分析和判断,或者尝试使用不同的编码转换方法。该命令将输出文件的MIME类型和编码信息。使用命令行工具:在命令行中,你可以使用。_shell读取csv文件逐行处理
本文介绍了如何在Linux系统中升级gcc版本,以便更好地支持C++11及以上版本的新特性。通过升级gcc,可以提升编译器的功能和性能,获得更好的开发体验。详细的步骤和方法请参考原文链接。
文章浏览阅读4.4k次,点赞6次,收藏19次。Mosquitto是一个开源的MQTT消息代理服务器。MQTT是一个轻量级的、基于发布/订阅模式的消息传输协议。 mosquitto的安装使用比较简单,可以方便的来进行一些测试。_linux mosquitto
文章浏览阅读7.2k次,点赞2次,收藏12次。Linux中,用于根目录下有一个.ssh目录,保存了ssh相关的key和一些记录文件。_~/.ssh/
文章浏览阅读4.5k次,点赞5次,收藏18次。首先需要安装 snmp ,使用下面的命令进行安装安装完毕之后,使用下面的命令查看是否安装成功当命令行显示如图即为安装成功。_snmp工具