TranslateProject/translated/tech/20151123 Data Structures in the Linux Kernel.md

9.3 KiB
Raw Blame History

Linux内核数据结构

基数树 Radix tree

正如你所知道的Linux内核提供了许多不同的库和函数它们实现了不同的数据结构和算法。在这部分我们将研究其中一种数据结构——基数树 Radix tree。在Linux内核中有两个与基数树实现和API相关的文件

让我们讨论什么是基数树吧。基数树是一种压缩的字典树,而字典树是实现了关联数组接口并允许以键值对方式存储值的一种数据结构。该键通常是字符串,但能够使用任何数据类型。字典树因为它的节点而与n叉树不同。字典树的节点不存储键;相反,字典树的一个节点存储单个字符的标签。与一个给定节点关联的键可以通过从根遍历到该节点获得。举个例子:

+-----------+
||
|" "|
               |           |
+------+-----------+------+
||
||
+----v------++-----v-----+
||||
|g||c|
   |           |            |           |
+-----------++-----------+
||
||
+----v------++-----v-----+
||||
|o||a|
   |           |            |           |
+-----------++-----------+
|
|
+-----v-----+
||
|t|
                            |           |
+-----------+

因此在这个例子中,我们可以看到一个有着两个键gocat字典树。压缩的字典树或者基数树字典树不同于所有只有一个孩子的中间节点都被删除。

Linu内核中的基数树是映射值到整形键的一种数据结构。include/linux/radix-tree.h文件中的以下结构体表示了基数树:

struct radix_tree_root {
         unsigned int            height;
         gfp_t                   gfp_mask;
         struct radix_tree_node  __rcu *rnode;
};

这个结构体表示了一个基数树的根并包含了3个域成员

  • height - 树的高度;
  • gfp_mask - 告诉如何执行动态内存分配;
  • rnode - 孩子节点指针.

我们第一个要讨论的域是gfp_mask

底层内核内存动态分配函数以一组标志作为gfp_mask,用于描述如何执行动态内存分配。这些控制分配进程的GFP_标志拥有以下值:(GF_NOIO标志)意味着睡眠等待内存,(__GFP_HIGHMEM标志)意味着高端内存能够被使用,(GFP_ATOMIC标志)意味着分配进程拥有高优先级并不能睡眠等等。

  • GFP_NOIO - 睡眠等待内存
  • __GFP_HIGHMEM - 高端内存能够被使用;
  • GFP_ATOMIC - 分配进程拥有高优先级并且不能睡眠;

等等。

下一个域是rnode

struct radix_tree_node {
        unsigned int    path;
        unsigned int    count;
        union {
                struct {
                        struct radix_tree_node *parent;
                        void *private_data;
                };
                struct rcu_head rcu_head;
        };
        /* For tree user */
        struct list_head private_list;
        void __rcu      *slots[RADIX_TREE_MAP_SIZE];
        unsigned long   tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
};

这个结构体包含的信息有父节点中的偏移以及到底端(叶节点)的高度、孩子节点的个数以及用于访问和释放节点的域成员。这些域成员描述如下:

  • path - 父节点中的偏移和到底端(叶节点)的高度
  • count - 孩子节点的个数;
  • parent - 父节点指针;
  • private_data - 由树的用户使用;
  • rcu_head - 用于释放节点;
  • private_list - 由树的用户使用;

radix_tree_node的最后两个成员——tagsslots非常重要且令人关注。Linux内核基数树的每个节点都包含一组存储指向数据指针的slots。Linux内核基数树实现的空slots存储NULL值。Linux内核中的基数树也支持与radix_tree_node结构体的tags域相关联的标签。标签允许在基数树存储的记录中设置各个位。

既然我们了解了基数树的结构那么该是时候看一下它的API了。

Linux内核基数树API

我们从结构体的初始化开始。有两种方法初始化一个新的基数树。第一种是使用RADIX_TREE宏:

RADIX_TREE(name, gfp_mask);

正如你所看到的,我们传递name参数,所以使用RADIX_TREE宏,我们能够定义和初始化基数树为给定的名字。RADIX_TREE的实现是简单的:

#define RADIX_TREE(name, mask) \
         struct radix_tree_root name = RADIX_TREE_INIT(mask)

#define RADIX_TREE_INIT(mask)   { \
        .height = 0,              \
        .gfp_mask = (mask),       \
        .rnode = NULL,            \
}

RADIX_TREE宏的开始,我们使用给定的名字定义radix_tree_root结构体实例并使用给定的mask调用RADIX_TREE_INIT宏。RADIX_TREE_INIT宏只是初始化radix_tree_root结构体为默认值和给定的mask而已。

第二种方法是亲手定义radix_tree_root结构体并且将它和mask传给INIT_RADIX_TREE宏:

struct radix_tree_root my_radix_tree;
INIT_RADIX_TREE(my_tree, gfp_mask_for_my_radix_tree);

where:

#define INIT_RADIX_TREE(root, mask)  \
do {                                 \
        (root)->height = 0;          \
        (root)->gfp_mask = (mask);   \
        (root)->rnode = NULL;        \
} while (0)

RADIX_TREE_INIT宏所做的初始化一样,初始化为默认值。

接下来是用于从基数树插入和删除数据的两个函数:

  • radix_tree_insert;
  • radix_tree_delete;

第一个函数radix_tree_insert需要3个参数

  • 基数树的根;
  • 索引键;
  • 插入的数据;

radix_tree_delete函数需要和radix_tree_insert一样的一组参数但是没有data。

基数树的搜索以两种方法实现:

  • radix_tree_lookup;
  • radix_tree_gang_lookup;
  • radix_tree_lookup_slot.

第一个函数radix_tree_lookup需要两个参数:

  • 基数树的根;
  • 索引键;

这个函数尝试在树中查找给定的键,并返回和该键相关联的记录。第二个函数radix_tree_gang_lookup有以下的函数签名:

unsigned int radix_tree_gang_lookup(struct radix_tree_root *root,
                                    void **results,
                                    unsigned long first_index,
                                    unsigned int max_items);

和返回记录的个数,(results指向的数据)按键排序并从第一个索引开始。返回的记录个数将不会超过max_items

最后一个函数radix_tree_lookup_slot将会返回包含数据的slot。

链接


via: https://github.com/0xAX/linux-insides/edit/master/DataStructures/radix-tree.md

作者:[0xAX] 译者:cposture 校对:校对者ID

本文由 LCTT 原创翻译,Linux中国 荣誉推出