Linux内核Radix Tree(一)

摘要:
Linux无线电树的API函数可以在lib/radio树C中找到。Linux基数树是一种将指针与长整数键关联的机制。它的存储效率很高,可以快速查询将指针映射到整数值、内存管理等。函数radiox_tree_预加载操作成功后

一、概述

Linux radix树最广泛的用途是用于内存管理,结构address_space通过radix树跟踪绑定到地址映射上的核心页,该radix树允许内存管理代码快速查找标识为dirty或writeback的页。Linux radix树的API函数在lib/radix-tree.c中实现。

Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制)、内存管理等。

Linux内核Radix Tree(一)第1张

上图显示了一个有3级结点的radix树,每个数据条目(item)可用3个6位的键值(key)进行索引,键值从左到右分别代表第1~3层结点位置。没有孩子的结点在图中不出现。因此,radix树为稀疏树提供了有效的存储,代替固定尺寸数组提供了键值到指针的快速查找。
以index=0x5BFB68为例,化为二进制,每6位为一组:10110(22,第一层编号) 111111(63第二次编号) 101101(45第三层编号) 101000(40第四层编号)

二、基本数据结构

struct radix_tree_root {
unsigned int height;
gfp_t gfp_mask;
struct radix_tree_node *rnode;/*间接指针指向结点而非数据条目,通过设置root->rnode的低位表示是否是间指针*/
};

struct radix_tree_node {
unsigned int height; /* 从叶子向上计算的树高度 */
unsigned int count; /*非叶子结点含有一个count域,表示出现在该结点的孩子的数量*/
struct rcu_head rcu_head;
void *slots[RADIX_TREE_MAP_SIZE]; //64个指针,每层64个子节点
unsigned long tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
//2X2数组,每个成员都为32位,
在结点结构radix_tree_node中,tags域是一个二维数组,每个slot用2位标识,这是一个典型的用空间换时间的应用。tags域用于记录该结点下面的子结点有没有相应的标志位。
//结点标签数组=每个slot需要的最大标签位数*slot数所需的long类型变量数
/*tag[0]64位,2个long类型,每一位代表一个slot,表示其PG_dirty
tag[0]64位,2个long类型,每一位代表一个slot,表示其PG_Writeback
如果当前节点的tags[0]值为1,那么它的子树节点就存在PAGE_CACHE_DIRTY节点,否则这个子树分枝就不存在着这样的节点,就不必再查找这个子树了。比如在查找PG_dirty的页面时,就不需要遍历整个树,而可以跳过那些tags[0]为0值的子树,这样就提高了查找效率*/
};

Linux内核Radix Tree(一)第2张

三、全局定义

#define RADIX_TREE_MAP_SHIFT 6
/*值为6时,表示每个结点有2^6=64个slot,值为4时,表示有2^4=16个slot*/
#define RADIX_TREE_MAP_SIZE (1UL <<RADIX_TREE_MAP_SHIFT) //1000000,2^6=64
/*表示1个叶子结点可映射的页数,如:1<<6=64,表示可映射64个slot映射64页*/
#define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) //111111
#define RADIX_TREE_TAG_LONGS
((RADIX_TREE_MAP_SIZE + BITS_PER_LONG - 1) /BITS_PER_LONG) //(64+32-1)/32=2
#define RADIX_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsignedlong)) //32
#define RADIX_TREE_MAX_PATH (DIV_ROUND_UP(RADIX_TREE_INDEX_BITS,
RADIX_TREE_MAP_SHIFT)) //(32+6-1)/6=6
#define RADIX_TREE_MAX_TAGS 2
/*定义slot数占用的long类型长度个数,每个slot用位图1位进行标记,如:64个slot时,值为2*/

static unsigned long height_to_maxindex[RADIX_TREE_MAX_PATH + 1]
// 全局数组,在32位机器上,这个数组大小是7,表示每一层的最大有多少个slot
height=0:maxindex=0, 第一层只有一个radix_tree_node
height=1:maxindex=2^6-1, 第二层最多63个
height=2:maxindex=2^12-1,
height=3:maxindex=2^18-1,
height=4:maxindex=2^24-1, 若每个slot为4k,则表示支持64G
height=5:maxindex=2^30-1,
height=6:maxindex=2^32-1, 16T

四、初始化函数

初始化一个名字是name的树根。mask是gfp相关的掩码,在内存管理的时候用到。
#define RADIX_TREE(name, mask)
struct radix_tree_root my_tree;
INIT_RADIX_TREE(my_tree, gfp_mask);
248 void __init radix_tree_init(void)
1249 {
1250 radix_tree_node_cachep = kmem_cache_create("radix_tree_node",
1251 sizeof(struct radix_tree_node), 0, SLAB_PANIC | SLAB_RECLAIM_ACCOUNT,
1253 radix_tree_node_ctor);
1254 radix_tree_init_maxindex();//初始化全局数组height_to_maxindex
1255 hotcpu_notifier(radix_tree_callback, 0);
1256 }

 五、插入条目

int radix_tree_insert(struct radix_tree_root *root, unsigned long, void *item);
函数radix_tree_insert插入条目item到树root中,如果插入条目中内存分配错误,将返回错误-ENOMEM。该函数不能覆盖写正存在的条目。如果索引键值index已存在于树中,返回错误-EEXIST。插入操作成功返回0。
对于插入条目操作失败将引起严重问题的场合,下面的一对函数可避免插入操作失败:
int radix_tree_preload(gfp_t gfp_mask);
void radix_tree_preload_end(void);
函数radix_tree_preload尝试用给定的gfp_mask分配足够的内存,保证下一个插入操作不会失败。在调用插入操作函数之前调用此函数,分配的结构将存放在每CPU变量中。函数radix_tree_preload操作成功后,将完毕内核抢占。因此,在插入操作完成之后,用户应调用函数radix_tree_preload_end打开内核抢占。
 六、删除条目
void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
函数radix_tree_delete删除与索引键值index相关的条目,如果删除条目在树中,返回该条目的指针,否则返回NULL。
七、查询条目
/*在树中查找指定键值的条目,查找成功,返回该条目的指针,否则,返回NULL*/
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index);
/*返回指向slot的指针,该slot含有指向查找到条目的指针*/
void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index);
/*多键值查找,max_items为需要查找的item个数,results表示查询结果。查询时键值索引从first_index开始*/
radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);

 八、标签操作
/*将键值index对应的条目设置标签tag,返回值为设置标签的条目*/
void *radix_tree_tag_set(struct radix_tree_root *root, unsigned long index, unsigned int tag);
/*从键值index对应的条目清除标签tag,返回值为清除标签的条目*/
void *radix_tree_tag_clear(struct radix_tree_root *root, unsigned long index, unsigned int tag);
/*检查键值index对应的条目tag是否设置。tag参数为0或者1,表示Dirty位或者WB位
如果键值不存在,返回0,如果键值存在,但标签未设置,返回-1;如果键值存在,且标签已设置,返回1*/
int radix_tree_tag_get(struct radix_tree_root *root, unsigned long index, unsigned int tag);
/*从first_index起查询树root中标签值为tag的条目,在results中返回*/
unsigned int radix_tree_gang_lookup_tag(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items, unsigned int tag);
/*如果树root中有任何条目使用tag标签,返回键值*/
int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag);

参考http://blog.csdn.net/joker0910/article/details/8250085

免责声明:文章转载自《Linux内核Radix Tree(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇git学习——svn代码同步到git(3)c# e语言 字节集 表示方式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

linux修改文件所有者和文件所在组

chgrp  用户名    文件名  -R chown 用户名   文件名  -R   -R表示递归目录下所有文件   以上部分已验证    一、修改文件所属组群——chgrp    修改文件所属组群很简单-chgrp命令,就是change group的缩写(我们可以利用这些来记忆命令)    语法:chgrp  组群  文件名/目录     举例: [r...

倒排表数据结构、通配符查询、拼写纠正详解

目录: Dictionary Data Structure 词典数据结构 Wild-Card Query 通配符查询 Spelling Correction 拼写纠正 搜索引擎里的dictionary data通常存储着这些信息: 索引词(term vocabulary)。 文档频率(document frequency,即这个词在多少个文档里出现)...

Linux基础学习(6)--Linux软件安装

第六章——Linux软件安装 一、软件包管理简介 1.软件包分类: (1)源码包:脚本安装包 (2)二进制包(RPM包、系统默认包) 2.源码包: (1)源码包的优点:开源,如果有足够的能力,可以修改源代码; 可以自由选择所需的功能; 软件是编译安装,所以更加适合自己的系统,更加稳定也效率更高; 卸载方便 (2)源码包的缺点:安装过程步骤较多,尤其安...

Linux平台Oracle 12.1.0.2 单实例安装部署

主题:Linux平台Oracle 12.1.0.2 单实例安装部署环境:RHEL 6.5 + Oracle 12.1.0.2需求:安装部署OEM 13.2需要Oracle 12.1.0.2版本作为资料库 1.下载介质 目前从OTN就可以直接下载到12.1.0.2的介质。 linuxamd64_12102_database_1of2.zip linuxamd...

Linux下PHP的完全卸载

如果想把PHP彻底的卸载干净,直接用yum的remove命令是不行的,而需要查看有多少rpm包,然后按照依赖顺序逐一卸载,在网上查了好多,都是通过 "rpm -qa | grep php" 命令查看有哪些rpm包,然后按照依赖关系依次卸载。 通过命令查看 [root@localhost test]# rpm -qa | grep php php-cli-5...

linux查看进程和终止进程

1. 在LINUX命令平台输入1-2个字符后按Tab键会自动补全后面的部分(前提是要有这个东西,例如在装了tomcat的前提下,输入tomcat的to按tab)。   2. ps 命令用于查看当前正在运行的进程。   grep 是搜索   例如: ps -ef | grep java   表示查看所有进程里CMD是java的进程信息   ps -aux |...