6. 非分类排队规则(非分类 qdisc)

任何一个非分类排队规则都可以作为网络接口上的根对象,也可以作为分类排队规则的叶子对象。这里列出的非分类排队规则是 Linux 下最基本也是最重要的排队规则。

6.1. FIFO, 先入先出 (pfifobfifo)

[注意]注意
这个排队规则并不是 Linux 默认的排队规则。默认的排队规则是 pfifo_fast,其详细说明请参考第 6.2 节 “pfifo_fast, Linux 默认的排队规则”

先入先出算法是 Linux 下网络设备上使用的最基本的包调度算法(默认是调度算法是 pfifo_fast)。先入先出算法既不会对网络流量进行整形操作,也不会重新排列数据包,它只会尽可能快地把数据包发按顺序发送出去。默认情况下,每一个新创建的分类器都将先入先出算法作为默认的排队规则。

使用先进先出算法的排队规则都有一个缓存,这是为了在数据包(或数据)无法及时发送出去时,暂时保存这些数据包(或数据)。Linux 有两种类型的先入先出 qdisc,一种是基于字节的,另一种是基于数据包的。无论使用哪种先入先出排队规则,都可以使用 limit 参数来指定缓存大小。当使用基于数据包的 pfifo 时,缓存大小的单位就是数据包个数;当使用基于字节的 bfifo 时,缓存大小的单位就是字节。

例 6. 使用 limit 参数为先进先出排队规则指定一个缓存大小

[root@leander]# cat bfifo.tcc
/*
 * 在 eth0 设备上创建一个缓存大小为10千字节的先进先出排队规则
 *
 */

dev eth0 {
    egress {
        fifo (limit 10kB );
    }
}
[root@leander]# tcc < bfifo.tcc
# ================================ Device eth0 ================================

tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0
tc qdisc add dev eth0 handle 2:0 parent 1:0 bfifo limit 10240
[root@leander]# cat pfifo.tcc
/*
 * 在 eth0 设备上创建一个缓存大小为30个数据包的先进先出排队规则
 *
 */

dev eth0 {
    egress {
        fifo (limit 30p );
    }
}
[root@leander]# tcc < pfifo.tcc
# ================================ Device eth0 ================================

tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0
tc qdisc add dev eth0 handle 2:0 parent 1:0 pfifo limit 30
      

6.2. pfifo_fast, Linux 默认的排队规则

pfifo_fast 是 Linux 下所有网络接口默认的排队规则。pfifo_fast 源于 FIFO,它在 FIFO 的基础之上增加了三条具有不同优先级的通道,每一条通道都是一个先入先出队列。最高优先级的数据包(如交互型应用)应该通过具有最高优先级的0号通道。类似地,1号通道优先级低于0号通道而又高于2号通道,2号通道的优先级最低。系统会首先发送高优先级通道中的数据包,当高优先级通道中没有数据包后,才会发送更低优先级通道中的数据包。也就是说,只有当0号通道中没有数据包时,1号通道中的数据包才会被发送,当1号通道中的数据包被发送完且0号通道也没有数据包时,才会发送2号通道中的数据包。

pfifo_fast 没有为用户提供任何可以修改的参数。有关 priomap 和 ToS 位的详细信息,请参考 pfifo-fast section of the LARTC HOWTO

6.3. SFQ, 随机公平队列

SFQ 会使用随机算法尽力让每一个 flows 都能公平地将自己的数据发送出去。SFQ 会使用一个散列函数来完成随机分配,根据散列函数的值将数据包分配到由它自己维护的一系列 FIFO 中,并循环发送这些 FIFO 中的数据,以此实现随机发送。由于一直使用固定的散列算法可能会造成不公平的情况,所以散列算法每隔一段固定的时间就会更改一次,用户可以修改 perturb 参数的值来控制这一时间间隔。

例 7. Creating an SFQ

[root@leander]# cat sfq.tcc
/*
 * 在 eth0 设备上创建一个 SFQ 排队规则,每10秒更换一次散列算法
 *
 */

dev eth0 {
    egress {
        sfq( perturb 10s );
    }
}
[root@leander]# tcc < sfq.tcc
# ================================ Device eth0 ================================

tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0
tc qdisc add dev eth0 handle 2:0 parent 1:0 sfq perturb 10
      

不幸的是,一些客户端(如 Kazaa,eMule 等)会从随机公平队列中获利,因为这写客户端通常会开启大量 TCP 会话,每一个会话就相当于一个 flows,这样,由 SFQ 自行在其内部维护的 FIFO 就会被这些 TCP 会话占领,导致其他数据包被不公平地对待。在通常情况下,SFQ 能很好地分配网络资源,但如果网络上存在类似这些令人抓狂的客户端或用户的话,整个网络就会变得非常不稳定。

有关 SFQ 的更多设定参数,请参考 第 6.4 节 “ESFQ, 扩展的随机公平队列”

6.4. ESFQ, 扩展的随机公平队列

从概念上来说,虽然这个排队规则给了用户比 SFQ 更多的可设定参数,但是它和 SFQ 没有本质上的区别。这个排队规则让用户能够调整散列算法的细节,以便更好地分配网络资源,弥补 SFQ 的不足。

例 8. ESFQ 的用法

用法: ... esfq [ perturb 秒 ] [ quantum 字节 ] [ depth FLOWS ]
        [ divisor 散列位 ] [ limit PKTS ] [ hash 散列类型]

Where:
散列类型 := { classic | src | dst }
      

待完善:需要补充更多内容,包括例子和实践。

6.5. GRED, 早期随机丢包

待完善:笔者没有使用过这种方法。需要补充例子和实践。

早期随机丢包通常只用在骨干路由器或核心网络上,一般用户很少用到。

6.6. TBF, 令牌桶

令牌桶排队规则是基于 tokensbuckets 这两个概念之上的。要对网络设备上的出站流量进行整形,使用令牌桶排队规则是个不错的解决方案,因为它只会减慢数据包发送速率而不会丢弃数据包。

当有足够的令牌时,数据包才能被发送出去,否则,数据包将被暂存起来延迟发送。通过这种方法延迟数据包的发送将会认为地增大数据包的来回时间。

例 9. 创建一个将网络流量速率整形为 256kbit/s 的 TBF

[root@leander]# cat tbf.tcc
/*
 * 在 eth0 设备上创建一个令牌桶排队规则,将流量整形为 256kbit/s
 *
 */

dev eth0 {
    egress {
        tbf( rate 256 kbps, burst 20 kB, limit 20 kB, mtu 1514 B );
    }
}
[root@leander]# tcc < tbf.tcc
# ================================ Device eth0 ================================

tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0
tc qdisc add dev eth0 handle 2:0 parent 1:0 tbf burst 20480 limit 20480 mtu 1514 rate 32000bps