Radix tree/IDR/IDA

Radix tree

原理

利用radix tree可以根据一个长整形快速查找其对应的对象指针。radix树与trie树有点相似,trie树一般用于字符串到对象的映射,radix树一般用于长整数到对象的映射。

这里写图片描述
来看一个四叉radix树,树高为4,每一个根节点最多有四个叶节点,总共最多有256个节点。而且这里每个叶节点使用两个字母(00/01/10/11)表示,一层层下来,由于总共有4层,每个长整型一定是0x00000010类似的8个字母组成的数据。

当我们插入一个新节点的时候,我们根据数据的比特位,在树中向下查找,若没有相应的节点,则生成相应的节点,直到数据比特位访问完,则建立叶节点映射相应的对象。

当我们删除一个节点的时候,沿着路径查找到叶节点后,直接删除叶节点,中间的非叶节点不删除。


结构体及API介绍

linux内核中相关的文件include/linux/radix-tree.h和lib/radix-tree.c,使用时包含头文件#include<linux/radix-tree.h>

radix树的root节点的结构体

struct radix_tree_root {
        unsigned int            height;//从叶节点向上计算出的树高度
        gfp_t                   gfp_mask;//内存申请的标识
        struct radix_tree_node  __rcu *rnode;//子节点指针
};

radix树叶子节点的结构体

struct radix_tree_node {
        unsigned int    height;/* Height from the bottom */
        unsigned int    count;//子节点个数
        union {
                struct radix_tree_node *parent;//父节点指针
                struct rcu_head rcu_head;//用于节点释放的RCU链表
        };
        void __rcu      *slots[RADIX_TREE_MAP_SIZE];//指向存储数据指针
        unsigned long   tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
};

初始化

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

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

或者使用
先定义struct radix_tree_root my_radix_tree;变量之后,再使用下面的宏对这个变量初始化。

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

节点的插入与删除与查找

int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
void *radix_tree_delete(struct radix_tree_root *, unsigned long);
void *radix_tree_lookup(struct radix_tree_root *, unsigned long);

例子


IDR

IDR机制是内核中将一个整数ID号和指针关联在一起的机制。
比如,当适配器要访问总线上的I2C设备时,首先要知道他们的ID号,同时要在内核中建立一个用于描述该设备的设备结构体,驱动程序将ID号和设备结构体关联起来,如果使用数组进行索引,一旦ID号很大,则用数组索引会占据大量的存储空间,如果使用链表,在总线上设备特别多的情况下,链表的查询效率不高。此时,IDR机制应运而生,内部采用红黑树,可以很方便的将整数和指针关联起来,并且有很高的搜索效率。

下面是linux/idr.h头文件中的一些定义,具体的函数调用接口可能随着新的内核已经发生了改变,要根据对应的include/idr.h头文件中的定义来。
相关结构体

struct idr {
        struct idr_layer __rcu  *hint;  /* the last layer allocated from */
        struct idr_layer __rcu  *top;
        struct idr_layer        *id_free;
        int                     layers; /* only valid w/o concurrent changes */
        int                     id_free_cnt;
        int                     cur;    /* current pos for cyclic allocation */
        spinlock_t              lock;
};

struct idr_layer {
        int                     prefix; /* the ID prefix of this idr_layer */
        DECLARE_BITMAP(bitmap, IDR_SIZE); /* A zero bit means "space here" */
        struct idr_layer __rcu  *ary[1<<IDR_BITS];
        int                     count;  /* When zero, we can release it */
        int                     layer;  /* distance from leaf */
        struct rcu_head         rcu_head;
};

定义并初始化

//定义
#define DEFINE_IDR(name)        struct idr name = IDR_INIT(name)

//静态初始化
#define IDR_INIT(name)                                                  \
{                                                                       \
        .lock                   = __SPIN_LOCK_UNLOCKED(name.lock),      \
}

//动态初始化
void idr_init(struct idr *idp);

分配存放ID号的内存
int idr_pre_get(struct idr *idp, gfp_t gfp_mask)

将ID号和指针关联
int idr_get_new(struct idr *idp, void *ptr, int *id)
参数ptr是需要关联的指针,参数id是由内核自动分配的ID号

通过ID号查找对应的指针
void *idr_find(struct idr *idr, int id)

删除ID号
void idr_remove(struct idr *idp, int id);


IDA

ida只是用来分配id,并不将某数据结构和id关联起来。
sd设备的设备名,如sda,驱动在生成设备文件的时候会向系统申请一个ida,也就是唯一id,然后把id映射成设备文件名sdxxx,

#define DEFINE_IDA(name)        struct ida name = IDA_INIT(name)
#define IDA_INIT(name)          { .idr = IDR_INIT((name).idr), .free_bitmap = NULL, }
void ida_init(struct ida *ida);
int ida_pre_get(struct ida *ida, gfp_t gfp_mask);
int ida_get_new(struct ida *ida, int *p_id);
void ida_remove(struct ida *ida, int id);
void ida_destroy(struct ida *ida);

参考文章

  1. IDR机制
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckywang1103

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值