Radix Tree用法

一、Radix Tree简介

基数树(Radix Tree)也称为压缩前缀树(compact prefix tree)或 compressed trie,是一种更节省空间的前缀树。在基数树中,树的叶子结点是实际的数据条目。每个结点有一个固定的、 2^n 指针指向子结点 (每个指针称为槽 slot,n 为划分的基的大小)。当父节点只有一个子节点时,子节点会合并到父节点。

基数树示例如下:
在这里插入图片描述
上图引用自维基百科Radix tree
在Linux内核中,Radix Tree由 include/linux/radix-tree.hlib/radix-tree.c两个文件实现。

二、数据结构

Linux 内核中,使用一个 struct radix_tree_root 结构作为根维护整棵 radix-tree。 每个节点采用 struct radix_tree_node 进行维护。在 radix-tree 中,节点被分作三类:

  • 第一类称为 internal 内部节点,内部节点不存储任何私有数据,主要用于维护下一级 节点的 slots 数组;
  • 第二类称为 exceptional 节点,这类节点与 internal 类似, 专门维护 radix-tree 中的下一级 exceptional 节点;
  • 第三类就是一般节点,一般节点 的 slots 数组里面存储的就是私有数据,且不包含任何 internal 节点。

2.1 radix_tree_root

// file: include/linux/radix-tree.h
/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */
struct radix_tree_root {
    unsigned int        height;
    gfp_t           gfp_mask;
    struct radix_tree_node  __rcu *rnode;
};

基数树的树根由结构体radix_tree_root 表示,它包含 3 个成员:

  • height – 树的高度
  • gfp_mask – 内存分配标识
  • rnode – 指向下一层的 radix_tree_node 节点(如果有的话)

2.2 radix_tree_node

// file: lib/radix-tree.c
struct radix_tree_node {
    unsigned int    height;     /* Height from the bottom */
    unsigned int    count;
    union {
        struct radix_tree_node *parent; /* Used when ascending tree */
        struct rcu_head rcu_head;   /* Used when freeing node */
    };
    void __rcu  *slots[RADIX_TREE_MAP_SIZE];
    unsigned long   tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
};

除根节点外,基数树的其它节点使用结构体 radix_tree_node 来表示。该结构体包含如下字段:

  • height – 从最底层到当前层的高度
  • count – 子节点数量
  • parent – 指向父节点的指针
  • slots – 指针数组

如果当前节点为位于树的最底层,则数组中的指针指向数据;否则,指向下一级节点。

同样的想法应用到了PG_writeback 标记,该标记表示页正在被写回磁盘。

这样,为基数树的每个节点引入了两个页描述符的标记:PG_dirty 和 PG_writeback。每个节点的 tags 字段中使用了 2 个 64 位的数组(即位图)来存放这两个标记,tags[0] 数组是脏位标记,而 tags[1] 数组是写回标记。

2.3 tags 说明

在普通的基数树中,我们只需要对数据本身进行查询、添加、删除等操作。但是在内核中,还有一种需求就是获取特定状态的数据。

以页高速缓(Page Cache)存为例。由于页的状态可能是脏页(PG_dirty),当我们要批量查找页高速缓存中的脏页时,如果要遍历整个基数树顺序访问所有的叶子节点(页描述符)就太慢了。相反,为了能快速搜索脏页,基数树中的每个中间节点都包含一个针对每个子节点(或叶子节点)的脏位标记,当至少有一个子节点的脏位标记被置位时,此标志被置位。通过这种方式,当内核遍历基数树搜索脏页时,就可以跳过脏位标记为 0 的中间节点的所有子树:中间节点的脏位标记为 0 说明其子树中的所有页描述符都不是脏的。

同样的想法应用到了PG_writeback 标记,该标记表示页正在被写回磁盘。

这样,为基数树的每个节点引入了两个页描述符的标记:PG_dirty 和 PG_writeback。每个节点的 tags 字段中使用了 2 个 64 位的数组(即位图)来存放这两个标记,tags[0] 数组是脏位标记,而 tags[1] 数组是写回标记。

参考资料

  1. Linux Kernel:内核数据结构之基数树(Radix Tree)
  2. Linux 内核之radix tree(基数树) 图文介绍
  3. Radix Tree --驱动示例代码
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值