- 浏览: 321814 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
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 2245本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1531本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1980本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1401本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1567本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 2021本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1615Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1991本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1848本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1979本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 2001本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2686本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2165本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2040本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(9)
2011-01-10 16:19 1875本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(8)
2011-01-10 16:18 1543本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2973本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1538本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1767本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(4)
2011-01-10 16:15 1688本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
在Linux内核中,TCP和UDP模块处理连接建立、数据传输、流量控制和拥塞控制等问题。 5. **应用层**:这一层包含各种应用协议,如HTTP、FTP、SMTP等,它们直接与用户交互。Linux内核通过socket API为上层应用提供了与...
【基于Linux内核的BT流量控制的原理与实现】 Linux操作系统以其开源、可定制的特点,在系统开发领域广泛应用,尤其在网络流量控制方面具有显著优势。针对BitTorrent(BT)这种大量占用带宽的P2P协议,Linux内核提供...
4. **网络堆栈**:从硬件接口到应用层协议的整个网络传输流程,如TCP/IP协议族、套接字API、网络设备驱动程序以及流量控制策略。 5. **设备驱动**:内核如何与硬件交互,驱动程序的工作原理,包括字符设备、块设备...
通过分析源码,我们可以了解到数据包的接收与发送过程,理解TCP连接的建立与断开、拥塞控制、流量控制等机制,这对于网络编程和网络故障排查非常有帮助。 此外,Linux内核还涉及中断处理、设备驱动、I/O管理等多个...
【基于Linux内核扩展模块的P2P流量控制】这篇文献主要探讨了如何在Linux操作系统中,通过内核扩展模块来实现对P2P流量的有效控制。P2P(Peer-to-Peer)技术的兴起改变了互联网的中心化结构,使得资源分享更为便捷,...
基于Linux内核扩展模块的P2P流量控制
基于LQL库的流量控制方法可以直接在Linux内核的框架下实现,而不需要使用传统方法中的TC命令解析、netlink传输和内核空间执行的三层结构。这可以提高流量控制的效率和可靠性,同时也可以减少流量控制的延迟和资源...
书中的内容涵盖了从内核基础到高级技术的方方面面,为那些希望提升Linux内核理解和开发能力的读者提供了宝贵的资源。在本文中,我们将探讨几个关键的知识点,包括Linux内核的基本结构、进程管理、内存管理和设备驱动...
2.6.24版本在网络方面加强了IPv6的支持,并改进了网络流量控制算法。 6. **安全与权限管理**:Linux内核采用了用户和组的概念,通过权限系统(如chmod、chown等)来控制文件访问。此外,还有SELinux(Security-...
接着,作者深入剖析了网络设备数据结构net_device,它包含了设备的配置信息、统计信息、状态标志以及各种管理列表和流量控制字段,这些细节揭示了网络设备如何在内核中被抽象和管理。 通过以上内容,我们可以看到,...
它处理网络数据的接收和发送,进行网络层的路由选择,以及传输层的拥塞控制和流量控制。 5. **设备驱动**:设备驱动程序是内核与硬件之间的桥梁,使得内核能够控制硬件设备。Linux内核支持大量设备驱动,包括块设备...
在Linux操作系统中,高级路由和流量控制是网络管理员和系统管理员必须掌握的关键技能。这篇文档“Linux高级路由和流量控制HOWTO中文版”由牛老师翻译,为读者提供了深入理解这些概念的宝贵资源。以下是对其中核心...
该模型内置于Linux内核中,并利用队列算法对不同服务质量(Quality of Service, QoS)需求的数据流进行分类,以提供灵活且差异化的服务。实验结果表明,采用该流量控制模型后,网络性能显著提高,并能很好地适应未来...
同时,还会讨论TCP的流量控制和拥塞控制机制,如滑动窗口、慢启动、快速重传和快速恢复算法等,这些都是保证网络通信质量和效率的关键。 其次,关于IP协议,书里会涉及IP地址、子网掩码、路由选择等概念,以及IP分...
TC 工具基于 Linux 内核的网络设备驱动程序,通过对网络设备的控制,来实现流量控制。TC 的工作原理可以分为以下三个阶段: 1. 流量控制方式:TC 提供了多种流量控制方式,包括 Token Bucket Filter(TBF)、...
TC(Linux 下流量控制工具)详细说明及应用实例 TC 是 Linux 下的一种流量控制工具,用于控制和管理网络流量。它提供了一个灵活的方式来管理网络带宽、延迟和丢包率等网络性能参数,以满足不同应用场景的需求。 TC...
2. **TCP/IP协议**:在传输层,TCP(传输控制协议)提供可靠的数据传输服务,通过确认、重传和流量控制确保数据的完整性和顺序。IP(互联网协议)在网络层负责数据包的路由和分片,是互联网的基础协议。 3. **套接...
Linux内核中的sock和socket数据结构是网络编程的核心组成部分,它们是实现网络通信的基础构件。在Linux操作系统中,网络通信的实现依赖于BSD套接字接口,而这一接口在内核中是通过sock和socket数据结构来实现的。 ...