《cbq流速控制实现分析》由会员分享,可在线阅读,更多相关《cbq流速控制实现分析(6页珍藏版)》请在金锄头文库上搜索。
1、本文档的本文档的 Copyleft 归归 wwwlkk 所有,使用所有,使用 GPL 发布,可以自由拷贝、转载,转载时请保持文档的完整性,严发布,可以自由拷贝、转载,转载时请保持文档的完整性,严 禁用于任何商业用途。禁用于任何商业用途。 E-mail: 来源来源: http:/ unsigned sz; unsigned bps = r-rate;获得配置流速获得配置流速(字节单位字节单位) unsigned mpu = r-mpu;最小数据包长度最小数据包长度if (mtu = 0) mtu = 2047;如果最大数据包长度未设定,使用默认值如果最大数据包长度未设定,使用默认值if (ce
2、ll_log cell_log) 255) cell_log+; for (i=0; icell_align=-1; / Due to the sz calc r-cell_log=cell_log;/填写指数偏移参数填写指数偏移参数 return cell_log;/返回指数偏移参数返回指数偏移参数 流速控制结构如图流速控制结构如图 0 所示所示;图图 0 流速控制结构流速控制结构 rtab 数组的每个元素存储相应长度的数据包应消耗的剩余资源,将数据包的长度进行指数偏数组的每个元素存储相应长度的数据包应消耗的剩余资源,将数据包的长度进行指数偏 移,得到对应的数组索引号,也就如果得到的值大于移
3、,得到对应的数组索引号,也就如果得到的值大于 rtab 的大小,则对应的消耗量,将会很大的大小,则对应的消耗量,将会很大 (惩罚惩罚)。 现在整个限速控制流程可以总结:现在整个限速控制流程可以总结: 1. 启动启动 CBQ 流控对象,并根据优先级顺序选择一个子类。流控对象,并根据优先级顺序选择一个子类。 2. 判断选择的子类是否为空,不为空,则试图发送子类中的一个数据包。判断选择的子类是否为空,不为空,则试图发送子类中的一个数据包。 3. 根据根据 rtab 数组计算数据包的空闲资源消耗量。数组计算数据包的空闲资源消耗量。 4. 如果子类的可用空闲资源大于需要消耗的空闲资源,发送数据包,否则不
4、发送,并选择下一个如果子类的可用空闲资源大于需要消耗的空闲资源,发送数据包,否则不发送,并选择下一个 子类。子类。 5. 发送数据包完毕,刷新子类的空闲资源,如果大于发送数据包完毕,刷新子类的空闲资源,如果大于 0,执行,执行 2,否则选择下一个子类。,否则选择下一个子类。可以进行流速控制的流控对象有 CBQ, TBF(令牌桶),HTB(分层令牌桶),HFSC,其中前 3 个都是使用这个流控算法。(二二) cbq 流速整形实现机制流速整形实现机制cbq 类型的流控对象是用于流速整形,而流速整形的实现是基于 cbq 子类,cbq 子类的流速控 制是基于空闲资源流控算法,前面已经介绍了空闲资源流控
5、算法,在这里就不详细介绍了。 cbq 子类:一个流速整形器,使用结构 struct cbq_class 来实现,其关键的属性有:1.流速大小 2.最大空闲资源 3.最小空闲资源 4.平均数据包大小 5.优先级 6.权重。 每个 cbq 对象必然有一个根 cbq 子类,也可以通过 tc class add 命令来建立更多的 cbq 子类, 并可以建立一定的逻辑结构。 (tc class add | del | change | get 命令就是用于操作 cbq 子类) 每个 cbq 子类都有自己的 classid,根子类的 classid 等于 cbq 对象句柄。 每个 cbq 子类都有一个过滤
6、器链,和一个流控对象。 cbq 子类就是用来控制其内部流控对象的流速。流速的控制是基于空闲资源流控算法。 运行一个 cbq 子类就是运行 cbq 子类中的流控对象。子类的流控对象每发送一个数据包,子 类的空闲资源就相应的减少,当空闲资源不足时,停止运行这个子类。 图 1,图 2 展示了 cbq 子类和 cbq 子类间,cbq 子类和对象间的逻辑关系:(其中 cbq_sched_data 是 cbq 对象的私有数据)图图 1 cbq 子类逻辑关系子类逻辑关系图图 2 流控对象逻辑关系流控对象逻辑关系 cbq 子类建立起亲属关系的逻辑结构有以下 2 方面作用: 1. 当子类的空闲资源不够时,可以借
7、用父类的空闲资源。 2. 入队时,最先遍历根类的过滤器链选择一个子类,然后会继续调用这个子类的过滤器链选择一 个子类,直到最终确定一个子类,并将数据包加入最终确定的子类的流控对象中。 注意:cbq 子类的查找并不是根据亲属逻辑来进行,而是使用 cbq 子类查找哈希表,每个 cbq 子 类必须在查找哈希中注册。 cbq 子类查找哈希表结构如图 3 所示:图图 3 cbq 子类查找哈希表子类查找哈希表 函数 struct cbq_class *cbq_class_lookup(struct cbq_sched_data *q, u32 classid)用来遍历 cbq 查找 哈希表,返回 id 号
8、是 classid 的 cbq 子类地址。cbq 入队过程可以总结如下: 1. 获得根 cbq 子类,如果过滤器为空,加入根 cbq 子类的流控对象,最后返回;否则执行 2。 2. 遍历过滤器链,试图选择一个子类,如果没有匹配成功,加入根 cbq 子类的流控对象,并返回; 如果匹配成功,选择一个 cbq 子类,转入 3. 3. 遍历前边选择的 cbq 子类的过滤器链,试图选择一个子类,如果没有匹配成功,加入本子类的 流控对象,并返回;如果匹配成功,选择一个 cbq 子类,重新执行 3.注意:每个 cbq 子类在建立时都自带一个流控对象(pfifo 类型),称作默认流控对象。默认流控对 象是没有
9、对象句柄的,我们是没法引用这个对象,替换后的对象必定有一个对象句柄(默认的句柄 或者指定的),是可以被引用的对象(比如修改,删除,添加子对象等等)。只有默认对象才可以直 接使用 tc qdisc add 替换,其它对象必须先删除或者使用 tc qdisc replace 来替换。 (这是由于默认子 对象没有在根对象中注册,tc qdisc add 添加的位置必定是默认对象或者非默认对象,如果该对象 没有在根对象注册,说明是默认对象,可以使用 add 被替换,否则不能使用 add 替换) 。cbq 对象的出队过程使用到一个新的结构优先级活动队列数组,队列数组结构如图 4 所示: 数组的每个元素都
10、指向一个 cbq 子类队列。第一个 cbq 子类队列的优先级最高,最多可以设置 8 个优先级。图图 3 优先级活动队列数组优先级活动队列数组 cbq 对象的出队函数会根据优先级顺序轮询优先级活动队列中的所有 cbq 子类,运行一个 cbq 子类就是运行 cbq 子类中的流控对象。 每个 cbq 子类必须加入优先级活动队列,才可以发送数据包。如果 cbq 子类的优先级是 4,将 会加入第 4 个活动队列。 cbq 对象是通过以下成员来使每个队列都轮流取得发包的机会。(在 cbq 对象的私有数据结构 中) intnclassesTC_CBQ_MAXPRIO+1;/相应活动队列的子类个数 unsig
11、nedquantaTC_CBQ_MAXPRIO+1;/相应活动队列权重 unsignedactivemask;/活动队列掩码 struct cbq_class *activeTC_CBQ_MAXPRIO+1;/活动队列数组 优先级活动队列数组的大小是 8,相应位置的元素和 activemask 相应位对应,0 表示链中有子 类。例如:activemask = 11111101 则只有 active2中有 cbq 子类,其他元素都指向 NULL。active0的优先级最高。cbq 对象运行的过程: 1. 根据队列掩码 activemask 选择一个非空队列里面优先级最高的队列,运行队列中的所有
12、cbq 子 类。 2. 接着选择非空队列里面优先级第二个高的队列,运行队列中的所有 cbq 子类。 3. 一直选择下去,最后又返回到 1。为了不使某个队列否则某个 cbq 子类一直占用资源,而其它 cbq 子类一直得不到运行,引入了 队列权重和子类权重。 cbq 子类的权重:假设有两个 cbq 子类,永远都有大量的数据包等待发送,流速都设置的很大, 也就是说两个子类永远都有空闲资源。如果某个 cbq 子类先启动,由于流速永远不会超限,那么 会一直运行这个 cbq 子类,第二个子类将永远得不到运行。为了避免这种情况,引入了 cbq 子类 权重,每发送一个数据包,子类权重就会相应的减少,当子类权重小于 0 时,将停止本子类,子 类权重恢复为初始值并选择其他子类。未配置子类权重时,tc 会分配一个默认值。显然子类权重 越高,获得的资源将会越多。 优先级队列权重的概念和 cbq 子类权重的概念相同。