分类流控qdisc之htb

tc参数

# 命令行语法
tc qdisc ... dev dev ( parent classid | root) [ handle major: ] htb [ default minor-id ]

tc class ... dev dev parent major:[minor] [ classid major:minor ] htb rate rate [ ceil rate ] burst bytes [ cburst bytes ] [ prio priority ]
  • “parent classid | root”: 如果htb作为队列的根qdisc,那么应该选择root。否则应该指明其属于哪个类的孩子;

  • “handle major:”: 指定该qdisc的句柄,该major无需和parent classid的主号码一致;

  • “default minor-id”: 表示如果数据包没有被分到某个class,那么应该将其分到哪个class,即默认class的此号码(主号码是明确的,就是该qdisc的主号码);

  • parent major:[minor]: 指定class的父亲,如果class的父亲就是qdisc,那么该class也称为根class,其父亲的minor可以不指定;其它class则必须指定其父亲class的句柄;

  • classid major:minor: 指定class自己的句柄;

  • rate rate: 指定该class要配置的速率;

  • ceil rate: htb在条件允许的情况下可以让class的速率超过rate参数,但是最大不能超过本参数。一个class其超过rate的速率其实是和父亲借来的(不用还),特别注意根class是不能向qdisc借用速率的;

  • prio priority: 指定class的优先级,值越小,优先级越高。当同一层的class都可以发送时,htb会对这些class进行遍历,每个class传输其quantum个数据包后轮询下一个;

配置示例

# htb为eth0的根qdisc,其句柄为1:,默认的数据包分类为1:12
tc qdisc add dev eth0 root handle 1: htb default 12

# 建立一个如右图所示的class树,rate分别代表其速率,ceil分别代表其最大速率
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps                      1:1
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps                  /   |   \
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps               1:10  1:11  1:12
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps

算法思想

htb将class组织成一颗树,它将待传输的数据包缓存在叶子class的队列中,其它class是不可以缓存流量的,但是孩子clas可以共享parent calss的流量配额。

htb中的每个class虽然都配置了rate,但是并不是说clas只能以指定的rate传输数据包,当网卡比较空闲时,是可以以高于rate的速率传输的,但不会高于ceil。简单来说,思想就是空闲时共享,繁忙时按照比例分配带宽。

每个class在某个时刻可以处于三种状态中的一种:

  1. CAN_SEND(令牌充足,此时发送速率小于rate);
  2. MAY_BORROW(没有令牌,但可借用,此时发送速率大于rate但小于ceil);
  3. CANT_SEND(没有令牌不可借用,此时发送速率大于ceil);

htb在收到出队请求时,按照下述原则出包:

  1. htb算法从class树的底部开始往上找处于CAN_SEND状态的class,如果找到某一层有CAN_SEND状态的类则停止;

  2. 如果该层中有多个class处于CAN_SEND状态则选取优先级最高(priority最小)的class,如果最高优先级还是有多个class,那就在这些类中轮询处理,每个类每发送自己的quantum个字节后,轮到下一个类发送;

  3. 只有叶子class才可以缓存数据包,如果步骤1,2最终选到了中间类,那么会顺着树往下找,找到一个孩子叶子class,并且该叶子类处于MAY_BORROW状态,将自己富余的令牌借给该叶子class让其传输。同样的道理,可能会有多个孩子叶子class处于MAY_BORROW状态,这里的处理跟步骤2是一样的,也是按照轮询处理;

用户态实现

数据结构

htb全局配置参数: tc_htb_glob

struct tc_htb_glob {
   
    __u32 version;		/* to match HTB/TC */
    __u32 rate2quantum;	/* bps->quantum divisor */
    __u32 defcls;		/* default class number */
    __u32 debug;		/* debug flags */

    /* stats */
    __u32 direct_pkts; /* count of non shaped packets */
};

htb类配置参数: tc_htb_opt

struct tc_htb_opt {
   
	struct tc_ratespec 	rate;
	struct tc_ratespec 	ceil;
	__u32	buffer;
	__u32	cbuffer;
	__u32	quantum;
	__u32	level;		/* out only */
	__u32	prio; // 类的优先级
};

qdisc配置参数解析: htb_parse_opt()

static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv,
    struct nlmsghdr *n)
{
   
	unsigned int direct_qlen = ~0U;
	struct tc_htb_glob opt = {
   
		.rate2quantum = 10,
		.version = 3,
	};
	struct rtattr *tail;
	unsigned int i; char *p;

	while (argc > 0) {
   
		if (matches(*argv, "r2q") == 0) {
   
			NEXT_ARG();
			if (get_u32(&opt.rate2quantum, *argv, 10)) {
   
				explain1("r2q"); return -1;
			}
		} else if (matches(*argv, "default") == 0) {
   
			NEXT_ARG();
			if (get_u32(&opt.defcls, *argv, 16)) {
   
				explain1("default"); return -1;
			}
		} else if (matches(*argv, "debug") == 0) {
   
			NEXT_ARG(); p = *argv;
			for (i = 0; i < 16; i++, p++) {
   
				if (*p < '0' || *p > '3') break;
				opt.debug |= (*p-'0')<<(2*i);
			}
		} else if (matches(*argv, "direct_qlen") == 0) {
   
			NEXT_ARG();
			if (get_u32(&direct_qlen, *argv, 10)) {
   
				explain1("direct_qlen"); return -1;
			}
		} else {
   
			fprintf(stderr, "What is \"%s\"?\n", *argv);
			explain();
			return -1;
		}
		argc--; argv++;
	}
	tail = NLMSG_TAIL(n);
	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
	addattr_l(n, 2024, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt)));
	if (direct_qlen != ~0U)
		addattr_l(n, 2024, TCA_HTB_DIRECT_QLEN, &direct_qlen, sizeof(direct_qlen));
	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
	return 0;
}

class配置参数解析: htb_parse_copt()

绝大多数参数和tbf的参数相同。

static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
{
   
	int ok = 0;
	struct tc_htb_opt opt = {
   };
	__u32 rtab[256], ctab[256];
	unsigned buffer = 0, cbuffer = 0;
	int cell_log =  -1, ccell_log = -1;
	unsigned int mtu = 1600; /* eth packet len */
	unsigned short mpu = 0;
	unsigned short overhead = 0;
	unsigned int linklayer  = LINKLAYER_ETHERNET; /* Assume ethernet */
	struct rtattr *tail;
	__u64 ceil64 = 0, rate64 = 0;

	while (argc > 0) {
   
		if (matches(*argv, "prio") == 0) {
    // 解析优先级参数
			NEXT_ARG();
			if (get_u32(&opt.prio, *argv, 10)) {
   
				explain1("prio"); return -1;
			}
			ok++;
		} else if (matches(*argv, "mtu") == 0) {
   
			NEXT_ARG();
			if (get_u32(&mtu, *argv, 10)) {
   
				explain1("mtu"); return -1;
			}
		} else if (matches(*argv, "mpu") == 0) {
   
			NEXT_ARG();
			if (get_u16(&mpu, 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值