流量控制--3.Linux流量控制的组件
Linux流量控制的组件流量控制元素与Linux组件之间的相关性:
4.1 qdisc简单讲,一个qidsc就是一个调度器。每个出接口都需要某种类型的调度器,默认的调度器为FIFO。Linux下的其他qdisc会根据调度器的规则来重新安排进入调度器队列的报文。 qdisc是构建所有Linux流量控制的主要部件,也被称为排队规则。 classful qdiscs 可以包含类,并提供了可以附加到过滤器的句柄。一个classful qidsc可以不使用子类,但这样通常会消耗CPU周期和其他系统资源,且毫无意义。 classless qdiscs 不包含类,也不会附加过滤器。由于一个classless qdisc不包含任何类的子类,因此不能使用分类,意味着不能附加任何过滤器。 在使用中可能会对术语 每个接口都会包含 一个接口上接收到的流量会经过 总之,由于egress qdisc包含一个真正的qdisc,且具有流量控制系统的全部功能,因此可以使用egress qdisc做很多事情。而一个 4.2 类类仅会存在于classful 任何类都可以附加任意多的过滤器,从而允许选择一个子类或使用过滤器来重新分类或直接丢弃进入特定类的流量。叶子类是qdisc中的终止类,它包含一个qdisc(默认是FIFO),且不会包含子类。任何包含子类的类都属于内部类(或root类),而非叶子类。 4.3 过滤器过滤器是Linux流量控制系统中最复杂的组件,提供了将流量控制的主要元素粘合到一起的机制。过滤器最简单和最明显的角色就是对报文进行分类(Section 3.3,“Classifying”)。Linux过滤器允许用户使用多个或单个过滤器来将报文分类到一个输出队列。
过滤器可能附加到classful qdiscs或类,但入队列的报文总是首先进入root qdisc。在报文经过的root qdisc上附加的过滤器后,报文可能被重定向到任何子类(子类可以包含自己的过滤器),后续可能对报文进一步分类。 4.4 分类器过滤器的对象,可以使用tc进行操作,且可以使用不同的分类机制,其中最常用的是 分类器可以作为过滤器的一部分来标识报文的特征或元数据。Linux分类器对象可以看作是流量控制分类的基本操作和基本机制。 4.5 策略器该机制仅作为Linux流量控制中的过滤器的一部分。一个策略器可以在速率超过指定速率时执行一个动作,在速率低于指定速率时执行另一个动作,善用策略可以模拟出一个三色表。参见 Section 10,“Diagram”。 虽然策略 和整流 都是流量控制中用来限制带宽的基本元素,但使用策略器并不会导致流量延迟。它只会根据特定的准则来执行某个动作。参见Example 5,“tc 4.6 丢弃该流量控制机制仅作为策略器的一部分。任何附加到过滤器的策略器都包含一个drop动作。
流量控制系统中,报文的丢失可能是由某个动作引起的副作用。例如,如果使用的调度器使用和GRED一样的方法控制流时,报文将被丢弃。 或者,当出现突发或超负荷时,如果整流器或调度器的缓冲用尽,也可能会丢弃报文。 4.7 句柄每个类和classful qdisc(Section 7,“Classful Queuing Disciplines (qdiscs)”)都要求在流量控制结构中存在一个唯一的标识符,该唯一标识符被称为句柄,每个句柄包含两个组成成员,一个主号和一个次号。用户可以根据以下规则随意分配这些号。 类和qdiscs的句柄号: 主号
次号
特殊的句柄 句柄作为tc过滤器的classid和flowid的目标参数,同时也是用户侧应用使用的标识对象的外部标识符。内核为每个对象维护内部标识符。 4.8 txqueuelen可以使用ip或ifconfig目录获取当前传输队列的长度。令人困惑的是,这些命令对传输队列长度的命名各部不同:
Linux默认的传输队列的长度为1000个报文,这是一个相当大的缓冲(特别是当带宽比较低时)。(为了理解其原因,请参见针对延迟和吞吐量的讨论,特别是缓冲膨胀。) 更有趣的是,
txqueuelen参数控制上述QDiscs的队列大小。对于大多数的队列规则,tc命令行中的 可以使用ip或ifconfig命令来配置接口的传输队列长度。
注意:ip命令使用 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计算出的值。
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 设置BQLBQL算法是自适应的,并不需要过多的人为接入。但如果需要关注低比特率下的最佳延迟,则有可能需要覆盖计算出的LIMIT值。可以在/sys目录根据NIC的名称和位置下找到BQL的状态和配置。例如我的一台服务器上的eth0 的目录为:
该目录中的文件为:
要对可排队的字节数设置上限,请将新值写入limit_max文件: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |