来自CSDN博客 http://blog.csdn.net/csalp/archive/2011/05/23/6439727.aspx
 LINUX TC:HTB相关源码
HTB(hierarchy token buffer)是linux tc(traffic control)模块中的排队队列的一种。它的配置比CBQ要简单.同时实现功能也很强大。下面,就来看看,HTB在linux中的源码。
1、             Qdisc_ops的注册
先从module_init函数看起(遵从fudan_abc的建议)
| 
static int __init htb_module_init(void) 
{ 
       return register_qdisc(&htb_qdisc_ops); 
} | 
上面的code会调用register_qdisc函数,将htb_qdisc_ops注册到系统中,那么htb_qdisc_ops包含了那些内容:
| 
static struct Qdisc_ops htb_qdisc_ops __read_mostly = { 
       .cl_ops        =     &htb_class_ops, 
       .id          =     "htb", 
       .priv_size   =     sizeof(struct htb_sched), 
       .enqueue     =     htb_enqueue, 
       .dequeue     =     htb_dequeue, 
       .peek            =     qdisc_peek_dequeued, 
       .drop            =     htb_drop, 
       .init              =     htb_init, 
       .reset           =     htb_reset, 
       .destroy      =     htb_destroy, 
       .dump          =     htb_dump, 
       .owner         =     THIS_MODULE, 
}; | 
可以看出,htb_qdisc_ops其实就是注册了htb管理queue的函数,最重要的莫过于:enqueue 和dequeue函数,它们作用如同它们的名字一样。那么到底将htb_qdisc_ops注册到那了呢?
这就要看看register函数了
| 
static struct Qdisc_ops *qdisc_base; 
/* 
*qdisc_base 就是系统维护所以qdisc所使用的变量,系统中的所有的qdisc都要 
*注册到这变量变量中 
*在struct Qdisc_ops中,包含了成员(struct Qdisc_ops *)next 
*也就是,所以的qdisc是以链表的形式存在的 
*/ 
int register_qdisc(struct Qdisc_ops *qops) 
{ 
       struct Qdisc_ops *q, **qp; 
       int rc = -EEXIST; 
       write_lock(&qdisc_mod_lock); 
/* 
*首先,检测这个qdisc是否已经注册过了,这是通过比较id实现的, 
*id的类型是char 数组:char                 id[IFNAMSIZ];IFNAMESIZ=16 
*htb的id=”htb” 
*/ 
       for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next) 
              if (!strcmp(qops->id, q->id)) 
                     goto out; 
/* 
*然后检测ops中的enqueue、dequeue、peek函数, 
*如果这些函数都没有被初始化,将使用noop_qdisc_ops函数来初始化 
*noop_qdisc_ops也是Qdisc_ops结构, 
*它的作用就像是定义了Qdisc_ops的默认值 
*/ 
       if (qops->enqueue == NULL) 
              qops->enqueue = noop_qdisc_ops.enqueue; 
       if (qops->peek == NULL) { 
              if (qops->dequeue == NULL) 
                     qops->peek = noop_qdisc_ops.peek; 
              else 
                     goto out_einval; 
       } 
       if (qops->dequeue == NULL) 
              qops->dequeue = noop_qdisc_ops.dequeue; 
/* 
*然后检测cl_ops成员。 
*cl_ops是结构Qdisc_class_ops, 
*它定义了用于管理挂载到这个qdisc下的所有class(或者qdisc) 
*/ 
       if (qops->cl_ops) { 
              const struct Qdisc_class_ops *cops = qops->cl_ops; 
              if (!(cops->get && cops->put && cops->walk && cops->leaf)) 
                     goto out_einval; 
              if (cops->tcf_chain && !(cops->bind_tcf && cops->unbind_tcf)) 
                     goto out_einval; 
       } 
/* 
*最后将新的qops插入到链表的尾部:*qp = qops; 
*这样就注册完成了 
*/ 
       qops->next = NULL; 
       *qp = qops; 
       rc = 0; 
out: 
       write_unlock(&qdisc_mod_lock); 
       return rc; 
out_einval: 
       rc = -EINVAL; 
       goto out; 
} 
EXPORT_SYMBOL(register_qdisc); | 
Qdisc_class_ops是管理这个tree的,那么看看htb的cl_ops有哪些函数:
| 
static const struct Qdisc_class_ops htb_class_ops = { 
       .graft           =     htb_graft, 
       .leaf             =     htb_leaf, 
       .qlen_notify      =     htb_qlen_notify, 
       .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, 
}; | 
我们知道,tc qdisc命令添加qdisc到某个设备后,为了对数据包进行分类,需要使用tc filter 来添加fitler到某个qdisc, 当数据包来时,通过fitler来区分数据包,并转发到不同的subqdisc 或者subclass。而绑定fitler都是通过函数:bind_tcf来实现的。

 
0 意見
張貼留言