- 浏览: 314644 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
JQ_AK47:
...
Linux下直接发送以太包 -
winsen2009:
谢谢分享,如果能再来一个列子就更好了,刚接触看完还是不懂的用
UNPv1_r3读书笔记: SCTP编程
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
5.11 HTB(Hierarchical token bucket, 递阶令牌桶) HTB, 从名称看就是TBF的扩展, 所不同的是TBF就一个节点处理所有数据, 而HTB是基于分类的流控方 法, 以前那些流控一般用一个"tc qdisc"命令就可以完成配置, 而要配置好HTB, 通常情况下tc qdisc, class, filter三种命令都要用到, 用于将不同数据包分为不同的类别, 然后针对每种类别数 据再设置相应的流控方法, 因此基于分类的流控方法远比以前所述的流控方法复杂。 HTB将各种类别的流控处理节点组合成一个节点树, 每个叶节点是一个流控结构, 可在叶子节点使用 不同的流控方法,如将pfifo, tbf等。HTB一个重要的特点是能设置每种类型的基本带宽,当本类带 宽满而其他类型带宽空闲时可以向其他类型借带宽。注意这个树是静态的, 一旦TC命令配置好后就不 变了, 而具体的实现是HASH表实现的, 只是逻辑上是树, 而且不是二叉树, 每个节点可以有多个子节 点。 HTB运行过程中会将不同类别不同优先权的数据包进行有序排列,用到了有序表, 其数据结构实际是 一种特殊的二叉树, 称为红黑树(Red Black Tree), 这种树的结构是动态变化的,而且数量不只一个 ,最大可有8×8个树。 红黑树的特征是: 1) 每个节点不是红的就是黑的; 2) 根节点必须是黑的; 3) 所有叶子节点必须也是黑的; 4) 每个红节点的子节点必须是黑的, 也就是红节点的父节点必须是黑节点; 5) 从每个节点到最底层节点的所有路径必须包含相同数量的黑节点; 关于红黑树的数据结构和操作在include/linux/rbtree.h和lib/rbtree.c中定义. 5.11.1 HTB操作结构定义 // HTB操作数据包模式 enum htb_cmode { // 不能发送 HTB_CANT_SEND, /* class can't send and can't borrow */ // 借带宽 HTB_MAY_BORROW, /* class can't send but may borrow */ // 可发送 HTB_CAN_SEND /* class can send */ }; 但HTB模式是HTB_CAN_SEND时, 表示是可以发送, 没有阻塞; 为HTB_CANT_SEND时表示阻塞, 根本不能 发送数据包了; 为HTB_MAY_BORROW时也属于阻塞状态, 但可以向其他类别借带宽来发送. /* interior & leaf nodes; props specific to leaves are marked L: */ // HTB类别, 用于定义HTB的节点 struct htb_class { /* general class parameters */ // 类别ID值, 高16位用于区分不同的HTB流控, 低16位为区分同一HTB流控中的不同类别 u32 classid; // 字节数, 包数统计 struct gnet_stats_basic bstats; // 队列信息统计 struct gnet_stats_queue qstats; // 速率统计, 字节率, 包率 struct gnet_stats_rate_est rate_est; // HTB统计信息, 借出, 借入, 令牌等参数 struct tc_htb_xstats xstats; /* our special stats */ // HTB类别引用计数 int refcnt; /* usage count of this class */ #ifdef HTB_RATECM /* rate measurement counters */ // 流率控制参数 unsigned long rate_bytes, sum_bytes; unsigned long rate_packets, sum_packets; #endif /* topology */ // 在树中的层次, 0表示叶子节点, 根节点层次是TC_HTB_MAXDEPTH-1(7) int level; /* our level (see above) */ // 父类别结构节点 struct htb_class *parent; /* parent class */ // 挂接到类别ID链表 struct hlist_node hlist; /* classid hash list item */ // 兄弟节点链表 struct list_head sibling; /* sibling list item */ // 子节点链表 struct list_head children; /* children list */ // 联合: union { // 如果该节点是叶子节点, 则使用leaf结构, 实现具体的流控处理; struct htb_class_leaf { // 叶子节点的内部流控结构 struct Qdisc *q; // 优先权 int prio; int aprio; // 定额参数, 缺省是取物理网卡的队列长度值 int quantum; // 不同层次深度的赤字 int deficit[TC_HTB_MAXDEPTH]; // 挂接到丢包链表 struct list_head drop_list; } leaf; // 如果非叶子节点, 使用HTB内部类别结构inner, 用于形成分类树 struct htb_class_inner { // 提供数据包的红黑树结构, 是一个按类别ID进行排序的有序表, 以二叉树实现, // 不同优先权对应不同的二叉树 struct rb_root feed[TC_HTB_NUMPRIO]; /* feed trees */ // 当前优先权树中正在处理的那个节点的指针 struct rb_node *ptr[TC_HTB_NUMPRIO]; /* current class ptr */ /* When class changes from state 1->2 and disconnects from parent's feed then we lost ptr value and start from the first child again. Here we store classid of the last valid ptr (used when ptr is NULL). */ // 上一个有效的树节点的类别ID u32 last_ptr_id[TC_HTB_NUMPRIO]; } inner; } un; // 类别结构自己的数据包供应树 struct rb_node node[TC_HTB_NUMPRIO]; /* node for self or feed tree */ // 事件树, 实际是等待树, 当带宽超过限制时会将该类别节点挂接到HTB流控节点的 // 等待队列wait_pq struct rb_node pq_node; /* node for event queue */ unsigned long pq_key; /* the same type as jiffies global */ // 激活的优先权参数, 非0表示相应位数的数据队列有数据包可用 int prio_activity; /* for which prios are we active */ // 当前模式, 表示是否可发送数据包 enum htb_cmode cmode; /* current mode of the class */ /* class attached filters */ // 过滤规则表 struct tcf_proto *filter_list; // 过滤器使用计数 int filter_cnt; // 警告标志 int warned; /* only one warning about non work conserving .. */ /* token bucket parameters */ // 令牌率 struct qdisc_rate_table *rate; /* rate table of the class itself */ // 峰值率 struct qdisc_rate_table *ceil; /* ceiling rate (limits borrows too) */ // 缓冲区/峰值缓冲区 long buffer, cbuffer; /* token bucket depth/rate */ // 最大等待时间 psched_tdiff_t mbuffer; /* max wait time */ // 当前令牌数/峰值令牌 long tokens, ctokens; /* current number of tokens */ // 检查点时间 psched_time_t t_c; /* checkpoint time */ }; // HTB私有数据结构 struct htb_sched { // HTB根节点链表 struct list_head root; /* root classes list */ // HTB哈希表, 根据类别ID进行哈希 struct hlist_head hash[HTB_HSIZE]; /* hashed by classid */ // 8级丢包链表 struct list_head drops[TC_HTB_NUMPRIO];/* active leaves (for drops) */ /* self list - roots of self generating tree */ // RB树根节点, 对应每一层的每一个优先权值都有一个RB树 struct rb_root row[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO]; // 掩码, 表示该层的哪些优先权值的树有效 int row_mask[TC_HTB_MAXDEPTH]; // 父节点指针 struct rb_node *ptr[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO]; // 上次使用的非空父节点的类别ID u32 last_ptr_id[TC_HTB_MAXDEPTH][TC_HTB_NUMPRIO]; /* self wait list - roots of wait PQs per row */ // 等待队列, 用来挂接那些带宽超出限制的节点 struct rb_root wait_pq[TC_HTB_MAXDEPTH]; /* time of nearest event per level (row) */ unsigned long near_ev_cache[TC_HTB_MAXDEPTH]; /* cached value of jiffies in dequeue */ // 当前时间, 在dequeue时更新 unsigned long jiffies; /* whether we hit non-work conserving class during this dequeue; we use */ int nwc_hit; /* this to disable mindelay complaint in dequeue */ // 缺省类别 int defcls; /* class where unclassified flows go to */ /* filters for qdisc itself */ // 过滤规则表 struct tcf_proto *filter_list; int filter_cnt; // 速率到定额转换参数 int rate2quantum; /* quant = rate / rate2quantum */ // 当前时间 psched_time_t now; /* cached dequeue time */ // 定时器 struct timer_list timer; /* send delay timer */ #ifdef HTB_RATECM // 速率定时器 struct timer_list rttim; /* rate computer timer */ int recmp_bucket; /* which hash bucket to recompute next */ #endif /* non shaped skbs; let them go directly thru */ // 直接处理数据包队列 struct sk_buff_head direct_queue; int direct_qlen; /* max qlen of above */ // 直接处理的数据包计数 long direct_pkts; }; // HTB类别操作结构 static struct Qdisc_class_ops htb_class_ops = { .graft = htb_graft, .leaf = htb_leaf, .get = htb_get, .put = htb_put, .change = htb_change_class, .delete = htb_delete, .walk = htb_walk, .tcf_chain = htb_find_tcf, .bind_tcf = htb_bind_filter, .unbind_tcf = htb_unbind_filter, .dump = htb_dump_class, .dump_stats = htb_dump_class_stats, }; // HTB 流控操作结构 static struct Qdisc_ops htb_qdisc_ops = { .next = NULL, .cl_ops = &htb_class_ops, .id = "htb", .priv_size = sizeof(struct htb_sched), .enqueue = htb_enqueue, .dequeue = htb_dequeue, .requeue = htb_requeue, .drop = htb_drop, .init = htb_init, .reset = htb_reset, .destroy = htb_destroy, // 更改操作为空 .change = NULL /* htb_change */, .dump = htb_dump, .owner = THIS_MODULE, }; 5.11.1 HTB的TC操作命令 为了更好理解HTB各处理函数,先用HTB的配置实例过程来说明各种操作调用了哪些HTB处理函数,以 下的配置实例取自HTB Manual, 属于最简单分类配置: 1) 配置网卡的根流控节点为HTB #根节点ID是0x10000, 缺省类别是0x10012, # handle x:y, x定义的是类别ID的高16位, y定义低16位 #注意命令中的ID参数都被理解为16进制的数 tc qdisc add dev eth0 root handle 1: htb default 12 在内核中将调用htb_init()函数初始化HTB流控结构. 2) 建立分类树 #根节点总流量带宽100kbps, 内部类别ID是0x10001 tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps #第一类别数据分30kbps, 最大可用100kbps, 内部类别ID是0x10010(注意这里确实是16进制的10) tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps #第二类别数据分30kbps, 最大可用100kbps, 内部类别ID是0x10011 tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps #第三类别(缺省类别)数据分60kbps, 最大可用100kbps, 内部类别ID是0x10012 tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps 在内核中将调用htb_change_class()函数来修改HTB参数 3) 数据包分类 #对源地址为1.2.3.4, 目的端口是80的数据包为第一类, 0x10010 tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 match ip dport 80 0xffff flowid 1:10 #对源地址是1.2.3.4的其他类型数据包是第2类, 0x10011 tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4 flowid 1:11 #其他数据包将作为缺省类, 0x10012 在内核中将调用htb_find_tcf(), htb_bind_filter()函数来将为HTB绑定过滤表 4) 设置每个叶子节点的流控方法 # 1:10节点为pfifo tc qdisc add dev eth0 parent 1:10 handle 20: pfifo limit 10 # 1:11节点也为pfifo tc qdisc add dev eth0 parent 1:11 handle 30: pfifo limit 10 # 1:12节点使用sfq, 扰动时间10秒 tc qdisc add dev eth0 parent 1:12 handle 40: sfq perturb 10 在内核中会使用htb_leaf()查找HTB叶子节点, 使用htb_graft()函数来设置叶子节点的流控方法. 5.11.2 HTB类别操作 5.11.2.1 嫁接 // 设置HTB叶子节点的流控方法 static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct Qdisc **old) { struct htb_class *cl = (struct htb_class *)arg; // 类别结构非空而且层次为0(叶子节点) if (cl && !cl->level) { // 如果没定义专门的流控方法, 则缺省定义pfifo作为缺省的流控方法 if (new == NULL && (new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops)) == NULL) return -ENOBUFS; sch_tree_lock(sch); // 将新的流控方法作为类别结构叶子节点的流控方法 if ((*old = xchg(&cl->un.leaf.q, new)) != NULL) { // 如果该类别还处于活动状态, 停止, 因为其原来的流控方法已经要被释放掉, // 不再处理数据包 if (cl->prio_activity) htb_deactivate(qdisc_priv(sch), cl); /* TODO: is it correct ? Why CBQ doesn't do it ? */ // 将老流控节点释放掉 sch->q.qlen -= (*old)->q.qlen; qdisc_reset(*old); } sch_tree_unlock(sch); return 0; } // 否则出错 return -ENOENT; } 5.11.2.2 获取叶子节点 static struct Qdisc *htb_leaf(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; // 如果类别结构非空而且是叶子节点, 返回该类别叶子节点的流控 return (cl && !cl->level) ? cl->un.leaf.q : NULL; } 5.11.2.3 增加类别的引用计数 static unsigned long htb_get(struct Qdisc *sch, u32 classid) { // 查找类别ID对应的HTB类别结构 struct htb_class *cl = htb_find(classid, sch); // 找到的话增加其引用计数 if (cl) cl->refcnt++; return (unsigned long)cl; } 5.11.2.4 减少类别引用计数 static void htb_put(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; // 减少类别引用计数, 如果减到0, 释放该类别 if (--cl->refcnt == 0) htb_destroy_class(sch, cl); } // 释放类别 static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl) { // HTB私有数据结构 struct htb_sched *q = qdisc_priv(sch); if (!cl->level) { // 属于叶子节点的情况 BUG_TRAP(cl->un.leaf.q); // HTB流控总队列长度减少该叶子流控节点的队列长度 sch->q.qlen -= cl->un.leaf.q->q.qlen; // 释放叶子流控节点 qdisc_destroy(cl->un.leaf.q); } // 释放流量处理结构: 普通速率和峰值速率 qdisc_put_rtab(cl->rate); qdisc_put_rtab(cl->ceil); // 释放类别过滤规则表 htb_destroy_filters(&cl->filter_list); // 递归调用htb_destroy_class释放该节点的子节点 while (!list_empty(&cl->children)) htb_destroy_class(sch, list_entry(cl->children.next, struct htb_class, sibling)); /* note: this delete may happen twice (see htb_delete) */ // 删除类别ID链表 hlist_del_init(&cl->hlist); // 释放子类别链表 list_del(&cl->sibling); // 停止类别结构 if (cl->prio_activity) htb_deactivate(q, cl); // 如果类别结构属于需要等待模式, 将该节点从等待RB树中删除 if (cl->cmode != HTB_CAN_SEND) htb_safe_rb_erase(&cl->pq_node, q->wait_pq + cl->level); // 释放类别本身 kfree(cl); } 注意: 由于使用了递归处理, 因此HTB树不能太大, 否则就会使内核堆栈溢出而导致内核崩溃, HTB定 义的最大深度是8层. 5.11.2.5 更改类别结构内部参数 static int htb_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **tca, unsigned long *arg) { int err = -EINVAL; // HTB私有数据结构 struct htb_sched *q = qdisc_priv(sch); // 类别结构指针, 从上层传入 struct htb_class *cl = (struct htb_class *)*arg, *parent; // 通过netlink接口传来的配置参数 struct rtattr *opt = tca[TCA_OPTIONS - 1]; // 速率表, 峰值速率表结构 struct qdisc_rate_table *rtab = NULL, *ctab = NULL; // 保存解析后的参数 struct rtattr *tb[TCA_HTB_RTAB]; // HTB选项 struct tc_htb_opt *hopt; /* extract all subattrs from opt attr */ // 解析输入参数, 进行相关合法性检查 if (!opt || rtattr_parse_nested(tb, TCA_HTB_RTAB, opt) || tb[TCA_HTB_PARMS - 1] == NULL || RTA_PAYLOAD(tb[TCA_HTB_PARMS - 1]) < sizeof(*hopt)) goto failure; // 如果父节点ID不是根ID, 根据此ID查找父节点, 否则为父节点空 parent = parentid == TC_H_ROOT ? NULL : htb_find(parentid, sch); hopt = RTA_DATA(tb[TCA_HTB_PARMS - 1]); // 从输入参数中获取速率表结构: 普通速率和峰值速率 rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB - 1]); ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB - 1]); if (!rtab || !ctab) goto failure; if (!cl) { /* new class */ // 如果类别结构为空, 是需要构造的新类别 struct Qdisc *new_q; int prio; /* check for valid classid */ // 类别ID合法性检查 if (!classid || TC_H_MAJ(classid ^ sch->handle) || htb_find(classid, sch)) goto failure; /* check maximal depth */ // 如果祖父节点层次都小于2, 也就是最大是1, 表示HTB节点树太深了, 叶子节点都没法表示了 if (parent && parent->parent && parent->parent->level < 2) { printk(KERN_ERR "htb: tree is too deep\n"); goto failure; } err = -ENOBUFS; // 分配类别空间 if ((cl = kzalloc(sizeof(*cl), GFP_KERNEL)) == NULL) goto failure; // 初始化引用计数 cl->refcnt = 1; // 初始化兄弟链表 INIT_LIST_HEAD(&cl->sibling); // 初始化哈希链表 INIT_HLIST_NODE(&cl->hlist); // 初始化子节点链表 INIT_LIST_HEAD(&cl->children); // 初始化丢包链表 INIT_LIST_HEAD(&cl->un.leaf.drop_list); // 设置为空节点(父节点是本身) RB_CLEAR_NODE(&cl->pq_node); // 初始化self or feed tree节点 for (prio = 0; prio < TC_HTB_NUMPRIO; prio++) RB_CLEAR_NODE(&cl->node[prio]); /* create leaf qdisc early because it uses kmalloc(GFP_KERNEL) so that can't be used inside of sch_tree_lock -- thanks to Karlis Peisenieks */ // 新的流控节点缺省是使用pfifo new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); sch_tree_lock(sch); // 以下调整父节点的状态参数 if (parent && !parent->level) { // 如果父节点原先是叶子节点, 将其转为中间节点, 因为现在已经有新的叶子节点作为其子节点 /* turn parent into inner node */ // 释放父节点的流控结构 sch->q.qlen -= parent->un.leaf.q->q.qlen; qdisc_destroy(parent->un.leaf.q); // 如果该父节点正处于活动情况, 停止 if (parent->prio_activity) htb_deactivate(q, parent); /* remove from evt list because of level change */ // 如果不是HTB_CAN_SEND模式, 说明该节点在等待节点树中, 从该树中删除 if (parent->cmode != HTB_CAN_SEND) { htb_safe_rb_erase(&parent->pq_node, q->wait_pq); parent->cmode = HTB_CAN_SEND; } // 更新父节点的层次, 如果不存在祖父节点, 则层次为根节点, 否则使用祖父节点的层次值 parent->level = (parent->parent ? parent->parent->level : TC_HTB_MAXDEPTH) - 1; // 不再使用内部叶子结构, 而是改为使用HTB内部结构, 参数清零 memset(&parent->un.inner, 0, sizeof(parent->un.inner)); } /* leaf (we) needs elementary qdisc */ // 设置类别结构的叶子流控节点 cl->un.leaf.q = new_q ? new_q : &noop_qdisc; // 类别结构的ID和父 cl->classid = classid; cl->parent = parent; /* set class to be in HTB_CAN_SEND state */ // 令牌和峰值令牌 cl->tokens = hopt->buffer; cl->ctokens = hopt->cbuffer; // 缓冲区大小 cl->mbuffer = PSCHED_JIFFIE2US(HZ * 60); /* 1min */ // 初始化时间 PSCHED_GET_TIME(cl->t_c); // 模式为可以发送模式 cl->cmode = HTB_CAN_SEND; /* attach to the hash list and parent's family */ // 挂接到哈希链表 hlist_add_head(&cl->hlist, q->hash + htb_hash(classid)); // 添加到父节点的子节点链表 list_add_tail(&cl->sibling, parent ? &parent->children : &q->root); } else sch_tree_lock(sch); /* it used to be a nasty bug here, we have to check that node is really leaf before changing cl->un.leaf ! */ if (!cl->level) { // 如果是叶子节点, 设置其定额, 当出现赤字时会按定额大小增加 cl->un.leaf.quantum = rtab->rate.rate / q->rate2quantum; // 如果计算出的定额量太小或太大, 说明rate2quantum参数该调整了, 这就是tc命令中的r2q参数 // 对于不同的带宽, 要选择不同的r2q值 if (!hopt->quantum && cl->un.leaf.quantum < 1000) { // 定额太小 printk(KERN_WARNING "HTB: quantum of class %X is small. Consider r2q change.\n", cl->classid); cl->un.leaf.quantum = 1000; } if (!hopt->quantum && cl->un.leaf.quantum > 200000) { // 定额太大 printk(KERN_WARNING "HTB: quantum of class %X is big. Consider r2q change.\n", cl->classid); cl->un.leaf.quantum = 200000; } if (hopt->quantum) cl->un.leaf.quantum = hopt->quantum; // 设置该节点的优先权值, 最大限制为TC_HTB_NUMPRIO - 1 if ((cl->un.leaf.prio = hopt->prio) >= TC_HTB_NUMPRIO) cl->un.leaf.prio = TC_HTB_NUMPRIO - 1; } // 缓冲区 cl->buffer = hopt->buffer; // 峰值流控缓冲区 cl->cbuffer = hopt->cbuffer; // 如果该类别原先有速率控制结构, 先释放掉再更新为新的速率控制结构 // 普通速率控制结构更新 if (cl->rate) qdisc_put_rtab(cl->rate); cl->rate = rtab; // 峰值速率控制结构更新 if (cl->ceil) qdisc_put_rtab(cl->ceil); cl->ceil = ctab; sch_tree_unlock(sch); *arg = (unsigned long)cl; return 0; failure: if (rtab) qdisc_put_rtab(rtab); if (ctab) qdisc_put_rtab(ctab); return err; } 5.11.2.6 查找过滤规则表 static struct tcf_proto **htb_find_tcf(struct Qdisc *sch, unsigned long arg) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)arg; // 如果类别结构非空,使用类别结构的过滤表, 否则使用HTB私有结构的过滤表 struct tcf_proto **fl = cl ? &cl->filter_list : &q->filter_list; return fl; } 5.11.2.7 绑定过滤器 static unsigned long htb_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { struct htb_sched *q = qdisc_priv(sch); // 根据类别ID查找类别结构 struct htb_class *cl = htb_find(classid, sch); /*if (cl && !cl->level) return 0; The line above used to be there to prevent attaching filters to leaves. But at least tc_index filter uses this just to get class for other reasons so that we have to allow for it. ---- 19.6.2002 As Werner explained it is ok - bind filter is just another way to "lock" the class - unlike "get" this lock can be broken by class during destroy IIUC. */ // 如果流控类别结构有效, 增加其使用计数 if (cl) cl->filter_cnt++; else // 否则是增加整个HTB流控结构的使用计数 q->filter_cnt++; return (unsigned long)cl; } 5.11.2.8 解开过滤器 static void htb_unbind_filter(struct Qdisc *sch, unsigned long arg) { struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)arg; // 实际就是对过滤器引用计数减一 if (cl) cl->filter_cnt--; else q->filter_cnt--; } 5.11.2.9 遍历HTB static void htb_walk(struct Qdisc *sch, struct qdisc_walker *arg) { // HTB流控私有数据 struct htb_sched *q = qdisc_priv(sch); int i; // 如果设置停止标志, 返回 if (arg->stop) return; // 遍历所有HTB哈希表 for (i = 0; i < HTB_HSIZE; i++) { struct hlist_node *p; struct htb_class *cl; // 遍历哈希表中每个元素, 即HTB类别结构 hlist_for_each_entry(cl, p, q->hash + i, hlist) { // 如果要跳过skip个开始的一些节点, 跳过这些节点 if (arg->count < arg->skip) { arg->count++; continue; } // 对类别结构进行相关操作 if (arg->fn(sch, (unsigned long)cl, arg) < 0) { arg->stop = 1; return; } arg->count++; } } } 5.11.2.10 类别参数输出 static int htb_dump_class(struct Qdisc *sch, unsigned long arg, struct sk_buff *skb, struct tcmsg *tcm) { struct htb_class *cl = (struct htb_class *)arg; unsigned char *b = skb->tail; struct rtattr *rta; struct tc_htb_opt opt; spin_lock_bh(&sch->dev->queue_lock); // 父节点的类别ID tcm->tcm_parent = cl->parent ? cl->parent->classid : TC_H_ROOT; // 本节点的类别ID tcm->tcm_handle = cl->classid; // 如果是叶子节点, 提供叶子节点的流控节点的ID if (!cl->level && cl->un.leaf.q) tcm->tcm_info = cl->un.leaf.q->handle; rta = (struct rtattr *)b; RTA_PUT(skb, TCA_OPTIONS, 0, NULL); // 以下提供该类别的各种参数 memset(&opt, 0, sizeof(opt)); // 速率 opt.rate = cl->rate->rate; // 数据缓冲区 opt.buffer = cl->buffer; // 峰值速率 opt.ceil = cl->ceil->rate; // 峰值数据缓冲区 opt.cbuffer = cl->cbuffer; // 定额 opt.quantum = cl->un.leaf.quantum; // 优先权值 opt.prio = cl->un.leaf.prio; // 层次值 opt.level = cl->level; RTA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt); // 实际数据长度 rta->rta_len = skb->tail - b; spin_unlock_bh(&sch->dev->queue_lock); return skb->len; rtattr_failure: spin_unlock_bh(&sch->dev->queue_lock); skb_trim(skb, b - skb->data); return -1; } 5.11.2.10 类别统计信息输出 static int htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d) { struct htb_class *cl = (struct htb_class *)arg; #ifdef HTB_RATECM // HTB_EWMAC=2, HTB_HSIZE=16 // 字节和包速率参数 cl->rate_est.bps = cl->rate_bytes / (HTB_EWMAC * HTB_HSIZE); cl->rate_est.pps = cl->rate_packets / (HTB_EWMAC * HTB_HSIZE); #endif // 叶子节点, 提供当前内部流控结构的队列长度 if (!cl->level && cl->un.leaf.q) cl->qstats.qlen = cl->un.leaf.q->q.qlen; // 令牌数 cl->xstats.tokens = cl->tokens; // 峰值令牌数 cl->xstats.ctokens = cl->ctokens; // 分别将基本参数, 速率参数, 队列参数拷贝到目的缓存, 这些都是标准参数 if (gnet_stats_copy_basic(d, &cl->bstats) < 0 || gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 || gnet_stats_copy_queue(d, &cl->qstats) < 0) return -1; // 将应用数据(HTB自身统计数据)拷贝到目的缓存 return gnet_stats_copy_app(d, &cl->xstats, sizeof(cl->xstats)); } ...... 待续 ......
发表评论
-
Linux内核中流量控制(24)
2011-01-10 16:33 2208本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1491本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1936本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1349本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1525本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 1978本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1569Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1945本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1801本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1887本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 1958本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2634本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2109本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2004本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(9)
2011-01-10 16:19 1830本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(8)
2011-01-10 16:18 1496本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2923本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1492本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1727本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(4)
2011-01-10 16:15 1644本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
基于Linux内核扩展模块的P2P流量控制
基于Linux内核的BT流量控制的原理与实现.pdf
基于Linux内核扩展模块的P2P流量控制.pdf
Linux内核扩展模块的P2P流量控制方法与研究.pdf
该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...
15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...
在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...
xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...
Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图
虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。
15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看...
流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...
311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:...
络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...
netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的
Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...
如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...