<script type="text/javascript">
</script> bsd 的路由查找算法我研究过一段时间,当时我们要自己写一个路由查找模块,要扩展性好的,要紧凑的,耦合性低的,于是我就选择了bsd 的radix 算 法,它不同于linux 的哈希表查找算法,linux 内核实现了两种查找算法,一个是哈希表算法,一个是LC-trie 算法,我感觉linux 设计的东西 不是那么紧凑,但绝对是一件艺术品,比较适合有品味的人欣赏和使用,大多数人还是驯服不了它,唉,承认自己的品味低,只好选择radix 算法了,这里我绝 对没有贬低它的意思,其实我自己比较欣赏radix 的,呵呵。bsd 强调的东西和linux 不同,在统一性方面不管是windows 还是bsd 做的都要比 linux 好。闲话不扯了。
基于radix 树的查找算法中,bsd 将一个ip 地址分为解成了一个二进制比特串(计算机中它本身就是比特串,我是从读内核的人的角度分析的),然后在一 颗二叉radix 树中进行查找匹配,树的节点是需要测试的比特位,比如一个树节点的数据是15 ,那么当这个ip 二进制串到达这里时就要测试第15 位,如果 15 为1 ,则向右拐,如果为0 则向左拐,直到叶子节点,如果这个条目和需要的匹配则就是它了,如果不匹配,则回溯,不断的检查回溯过程中的掩码链表,以期 求得一个更大范围的普适路由。就是这样了,但是我们来看看会出现什么问题。
首先,整棵树是那么的紧凑以至于我们必须将它作为一个整体,linux 就好得多了,内核中的路由表以fib_table 为顶向下分为了好多层,比如 fn_zone,fib_node ,fib_alias,fib_info,fib_nh ,这么几层下来每层都是独立的,可灵活维护的,这看起来非常灵 活,不需要整个路由表的全局锁,只有在写操作时有一个fib_hash_lock ,只要分开的东西就可能被并行计算看到, 因此这样会比较好,所有 linux 的优点恰恰是bsd 的缺点,要访问路由表时要全局加锁,一下子锁住全树,就像linux 以前访问文件页高速缓存全局锁一样,这样在现代的多处理 器环境下非常不利,于是它变化了。
新的bsd 路由查找算法也是分层的,但是它的分层比linux 更前进了一步,它不但将算法分层,还将ip 地址分层,其实叫分级,把一个ip 地址分为相等的 4 份,每份8 位,每8 位每8 位的进行匹配,这样只需要锁柱正在计算的8 位就可以了,实际上,它将计算给分解了,这非常类似于虚拟地址到物理地址转化时的机 制,页目录负责高10 位,页表负责中10 位等等,而且恰恰巧的是,它就是这么实现的,本质上,它并没有改变原先的radix 的实质,只不过将比较位数扩展 为了8 位,以前的比较中,只比较1 位非1 即0 ,走形方向非左即右,现在比较8 位,就不是非左即右了,而是成了n 叉树,具体走哪一叉和原来一样,要看前一级 的比较结果,这个算法实际上如果写得好的话,可以在原来的radix 基础上扩充,我觉得这个新实现和linux 内核的页高速缓存的radix 树一样。
经过这盘更改后效率当然提高了很多,每一级的计算要有8 位参与,这个计算量恰好适合现代处理器的超标量以及超线程及多核进行并行处理,速度加快,而且一次 算8 位,总的计算次数只有4 次,不像以前的32 次,每次1 位还不够处理器塞牙缝呢,浪费了大量处理器,另外不再需要全局锁了,因为计算就不是全局的。更重 要的是,以前的一次比较1 位根本无法利用cpu 缓存,比较意味着处理器分支预测,你让处理器那么多次的猜你的心,它会疯的,而现在一次读入8 个位,数据都 在缓存里面,随便用,当然比较还是也有分支预测失败,但是数据都在缓存,可以不必理会存储器,而以前的一次1 位你根本不知道下面该取什么数据了,现在存储 器的带宽那么高全浪费了,就算你不让他浪费,结果该往左拐的预先取到右节点的数据,全错了,刷新缓存浪费更厉害。