Linux内核Radix Tree(三):API介绍

1.     单值查找radix_tree_lookup

函数radix_tree_lookup执行查找操作,查找方法是:从叶子到树顶,通过数组索引键值值查看数组元素的方法,一层层地查找slot。其列出如下

void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
    unsigned int height, shift;
    struct radix_tree_node *node, **slot;

node =
rcu_dereference(root->rnode); /*获取根结点*/
    if (node == NULL)
        return NULL;

/*间接指针指向结点而非数据条目,通过设置root->rnode的低位表示是否是间指针。对于间接指针来说,树高度值root->height大于0,但是RCU查找需要测试间接指针,因为root->height 值不可靠。这种问题仅的RCU下需要考虑*/ 
    if (!radix_tree_is_indirect_ptr(node)) { /*非间接指针,说明只有根结点*/
        if (index > 0)
            return NULL;
        return node;
    }

/*获取真正结点指针,因为根结点指针的第0位设置为1表示为间接指针。当使用结点指针时,必须将第0位设置为0,因为地址以字对齐*/
    node = radix_tree_indirect_to_ptr(node);

height =
node->height;
    if (index > radix_tree_maxindex(height)) /*索引键值不能超过最大索引值*/
        return NULL;

/*每层索引偏移值为RADIX_TREE_MAP_SHIFT,叶子索引值偏移基数为(树高-1)*每层索引偏移值*/ 
    shift = (height-1) * RADIX_TREE_MAP_SHIFT;

do { /*从叶子到树顶,通过树路径组成的索引查找指定索引键值的slot*/
        slot = (struct radix_tree_node
**)(node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK)); /*如:slots +1*/
        node = rcu_dereference(*slot);
        if (node == NULL)
            return NULL;

shift -= RADIX_TREE_MAP_SHIFT; /*向上移一层,再迭代查找*/
        height--;
    } while (height > 0);

return node;
}

2.    
多值查找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)
{
    unsigned long max_index;
    struct radix_tree_node *node;
    unsigned long cur_index = first_index;
    unsigned int ret;

node =
rcu_dereference(root->rnode);
    if (!node)
        return 0;

if
(!radix_tree_is_indirect_ptr(node)) { /*如果为非间接指针,表示只有根节点*/
        if (first_index > 0)
            return 0;
        results[0] = node;
        return 1;
    }
    node = radix_tree_indirect_to_ptr(node); /*清除用于间接指针标识的第0位*/

max_index =
radix_tree_maxindex(node->height); /*获取树的最大索引键值*/

ret = 0;
    while (ret < max_items) { /* max_items为查找的最大条目数*/
        unsigned int nr_found;
        unsigned long next_index; /* 下一个搜索的索引键值*/

if (cur_index > max_index) /*已查询完所需查询的索引键值*/
            break;
        nr_found = __lookup(node, results +
ret, cur_index,
        max_items - ret, &next_index);
        ret += nr_found;
        if (next_index == 0)
            break;
        cur_index = next_index;
    }

return ret;
}

static unsigned int
__lookup(struct radix_tree_node *slot, void **results, unsigned long index,
unsigned int max_items, unsigned long *next_index)
{
    unsigned int nr_found = 0;
    unsigned int shift, height;
    unsigned long i;

height =
slot->height;
    if (height == 0)
        goto out;
    /*所有叶子slot的索引键值基数偏移*/
    shift = (height-1) * RADIX_TREE_MAP_SHIFT;

/*从底层向树顶层,
    for ( ; height > 1; height--) { /*从叶子向树顶查找*/
        i = (index >> shift) &
RADIX_TREE_MAP_MASK;
        for (;;) { /*遍历每一层的各个路径,由树顶到当前层一条路径组成索引键值*/
            /*如果slot不为空,那么它挂有子结点,跳出本循环,进入子结点层进行本循环*/
            if
(slot->slots[i] != NULL)
               
break;
           /*如果slot为空,就跳过slot对应的所有索引键值*/ 
           /*清除索引号低位.将索引号与该层slot的起始索引号对齐*/
           index &=
~((1UL << shift) - 1);
           /*跳过一个slot的索引键值数*/
          index += 1UL <<
shift;
          if (index == 0)
             
goto out; /* 32-bit wraparound */
          i++; /*找到多个slot*/
          if (i ==
RADIX_TREE_MAP_SIZE)
             
goto out;
       }

shift
-= RADIX_TREE_MAP_SHIFT; /*向上移一层,基数偏移减少*/
       slot =
rcu_dereference(slot->slots[i]);
       if (slot == NULL)
           goto out;

}

/* 返回找到的多个slot*/
    for (i = index & RADIX_TREE_MAP_MASK; i <
RADIX_TREE_MAP_SIZE; i++) {
        struct radix_tree_node *node;
        index++;
        node = slot->slots[i];
        if (node) {
           
results[nr_found++] = rcu_dereference(node);
           if (nr_found ==
max_items)
              
goto out;
        }
    }
    out:
    *next_index = index;
    return nr_found;
}

3.     插入操作radix_tree_insert

函数radix_tree_insert找到索引键值对应的结点,将item加到该结点的slot指针上。其列出如下:

int radix_tree_insert(struct
radix_tree_root *root, unsigned long index, void *item)
{
    struct radix_tree_node *node = NULL, *slot;
    unsigned int height, shift;
    int offset;
    int error;

BUG_ON(radix_tree_is_indirect_ptr(item));

/* 如果树的高度不够,就扩展树。函数radix_tree_maxindex计算树高容纳的最大索引*/
    if (index > radix_tree_maxindex(root->height)) {
        error = radix_tree_extend(root,
index);
        if (error)
            return
error;
    }

slot =
radix_tree_indirect_to_ptr(root->rnode);

height =
root->height;
    shift = (height-1) * RADIX_TREE_MAP_SHIFT; /*计算偏移基数*/

offset = 0; 
    while (height > 0) {
        if (slot == NULL) { /*如果slot为空,则需要加入孩子结点*/
            /* 分配slot */
            if (!(slot =
radix_tree_node_alloc(root)))
               
return -ENOMEM;
           
slot->height = height;
            if (node)
{/*添加slot*/
               
rcu_assign_pointer(node->slots[offset], slot);
            
node->count++;
         } else
            
rcu_assign_pointer(root->rnode,radix_tree_ptr_to_indirect(slot));
        }

/* 进入上一层*/
        offset = (index >> shift)
& RADIX_TREE_MAP_MASK;
        node = slot;
        slot = node->slots[offset];
        shift -= RADIX_TREE_MAP_SHIFT;
        height--;
    }
    /*如果index对应的slot已有映射页面,返回-EEXIST*/
    if (slot != NULL)
        return -EEXIST;

if (node) {
        node->count++; /*增加子结点的计数*/
        rcu_assign_pointer(node->slots[offset],
item);
        BUG_ON(tag_get(node, 0, offset));
        BUG_ON(tag_get(node, 1, offset));
    } else { /*为顶层结点*/
        rcu_assign_pointer(root->rnode,
item);
        BUG_ON(root_tag_get(root, 0));
        BUG_ON(root_tag_get(root, 1));
    }

return 0;
}

4.     扩展树radix_tree_extend

如果当前树高度不足以存放index,就需要扩展树,扩展方法是在旧树顶上加新的根结点,并将原根结点的tag信息移到新根结点的第1个slot。函数radix_tree_extend 列出如下:
static int radix_tree_extend(struct radix_tree_root *root, unsigned long index)
{
    struct radix_tree_node *node;
    unsigned int height;
    int tag;

/* 计算扩展的深度*/
    height = root->height + 1;
    /*如果index超过树高所容纳的最大索引值,树高递增*/
    while (index > radix_tree_maxindex(height)) 
        height++;

/*到这里已计算好合适的高度height*/ 
    if (root->rnode == NULL) { /*只有根结点,设置好树高度就可返回*/
        root->height = height;
        goto out;
    }

/*将当前树扩展到高度height*/
    do {
        unsigned int newheight;
        if (!(node =
radix_tree_node_alloc(root))) /*分配一个结点*/
            return
-ENOMEM;

/* 增加树高,在树顶点之上增加一个结点*/
        node->slots[0] =
radix_tree_indirect_to_ptr(root->rnode);

/*传播tag信息到新根结点*/
        /*以前的根结点现成为新顶结点的第1个插槽。 如果以前的根结点打上了tag,就将新增结点的第1个插槽对应的子节点打上相应的tag*/
        for (tag = 0; tag <
RADIX_TREE_MAX_TAGS; tag++) {
            if
(root_tag_get(root, tag))
             
 tag_set(node, tag, 0);
        }

newheight = root->height+1;
        node->height = newheight;
        node->count = 1;
        node =
radix_tree_ptr_to_indirect(node);
        rcu_assign_pointer(root->rnode,
node);
        root->height = newheight;
    } while (height > root->height);
out:
    return 0;
}

5.    
删除操作radix_tree_delete

函数radix_tree_delete删除index对应的条目,并返回删除条目的地址。其列出如下:
void *radix_tree_delete(struct radix_tree_root *root, unsigned long index)
{
    /*数组path用于保存路径上的结点及索引偏移值*/
    struct radix_tree_path path[RADIX_TREE_MAX_PATH + 1], *pathp
= path;
    struct radix_tree_node *slot = NULL;
    struct radix_tree_node *to_free;
    unsigned int height, shift;
    int tag;
    int offset;

height =
root->height;
    if (index > radix_tree_maxindex(height)) /*index不能超过树的最大索引值*/
        goto out;

slot = root->rnode;
    if (height == 0) { /*只有根结点*/
        root_tag_clear_all(root);
        root->rnode = NULL;
        goto out;
    }
    slot = radix_tree_indirect_to_ptr(slot);

shift = (height - 1) *
RADIX_TREE_MAP_SHIFT;
    pathp->node = NULL;
    /*获取删除条目的路径*/
    /*将index从根到叶子的路径所经过的结点及相应索引偏移值存放在数组pathp中*/
    do {
        if (slot == NULL)
            goto out;

pathp++;
        offset = (index >> shift)
& RADIX_TREE_MAP_MASK;
        pathp->offset = offset;
        pathp->node = slot;
        slot = slot->slots[offset];
        shift -= RADIX_TREE_MAP_SHIFT;
        height--;
    } while (height > 0);

if (slot == NULL)
        goto out;

/*清除与删除条目相关的所有标签*/
    for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
        if (tag_get(pathp->node, tag,
pathp->offset))
          
 radix_tree_tag_clear(root, index, tag);
    }

to_free = NULL;
    /*释放不再需要的结点 */
    while (pathp->node) { /*删除
       
pathp->node->slots[pathp->offset] = NULL; /*清除slot*/
        pathp->node->count--; /*孩子数量减1*/
        /*调用RCU机制的函数call_rcu在最后一个引用结束时延迟释放结点to_free */
        if (to_free)
           
radix_tree_node_free(to_free);

if (pathp->node->count) { /*还有孩子存在*/
           if (pathp->node
== radix_tree_indirect_to_ptr(root->rnode)) /*为根结点的孩子*/
               
radix_tree_shrink(root); /*树缩小*/
           goto out;
        }

/*释放有0个slot的结点 */
        to_free = pathp->node;
        pathp--;

}
    /*运行到这里,说明是根结点*/
    root_tag_clear_all(root);
    root->height = 0;
    root->rnode = NULL;
    if (to_free)
        radix_tree_node_free(to_free);

out:
    return slot;
}

6.    
压缩树radix_tree_shrink

函数radix_tree_shrink缩小树的高度到最小。其列出如下:

static inline void radix_tree_shrink(struct
radix_tree_root *root)
{
    /* 尝试缩小树的高度*/
    while (root->height > 0) {
        struct radix_tree_node *to_free =
root->rnode;
        void *newptr;

BUG_ON(!radix_tree_is_indirect_ptr(to_free));
        to_free =
radix_tree_indirect_to_ptr(to_free);

/*候选结点多于一个孩子或孩子不在最左边slot,不能缩小树的高度,跳出循环*/
        if (to_free->count != 1)
            break;
        if (!to_free->slots[0])
            break;

/*不需要调用rcu_assign_pointer(),因为仅从树的一部分到另一部分移动结点。如果释放旧指针的引用to_free->slots[0]是安全的,那么释放新指针的引用root->rnode也是安全的*/ 
       newptr = to_free->slots[0];
       if (root->height > 1)
           newptr =
radix_tree_ptr_to_indirect(newptr);
       root->rnode = newptr;
       root->height--; 
       /* 仅释放0结点*/
       tag_clear(to_free, 0, 0);
       tag_clear(to_free, 1, 0);
       to_free->slots[0] = NULL;
       to_free->count = 0;
       radix_tree_node_free(to_free);
    }
}

时间: 2024-11-03 01:16:40

Linux内核Radix Tree(三):API介绍的相关文章

Linux内核Radix Tree(一)

一.概述 Linux radix树最广泛的用途是用于内存管理,结构address_space通过radix树跟踪绑定到地址映射上的核心页,该radix树允许内存管理代码快速查找标识为dirty或writeback的页.Linux radix树的API函数在lib/radix-tree.c中实现. Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制).内存管理等. 上图显示了一个有3级结点的radix

Linux内核Radix Tree(二)

1.   并发技术 由于需要页高速缓存是全局的,各进程不停的访问,必须要考虑其并发性能,单纯的对一棵树使用锁导致的大量争用是不能满足速度需要的,Linux中是在遍历树的时候采用一种RCU技术,来实现同步并发. RCU(Read-Copy Update),是一种保证读该radix tree的时候,可以不要管insert/delete操作,即不需使用锁.从内核代码来看,lookup操作的时候,读一个节点的时候,采用类似于 node = rcu_dereference(*slot); 的调用.Inse

20135327郭皓--Linux内核分析第三周 构造一个简单的Linux系统MenuOS

Linux内核分析第三周  构造一个简单的Linux系统MenuOS 前提回顾 1.计算机是如何工作的三个法宝 1.存储程序计算机 2.函数调用堆栈 3.中断 2.操作系统的两把宝剑 中断上下文的切换 进程上下文的切换 第一讲  Linux内核源代码介绍 arch目录包括了所有和体系结构相关的核心代码.它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录.PC机一般都基于此目录. init目录包含核心的初始化代码(不是系统的引导代

Linux内核分析(三)----初识linux内存管理子系统

Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linux的内存管理子系统,linux的内存管理子系统相当的庞大,所以我们今天只是初识,只要对其进行简单的了解就好了,不会去追究代码,但是在后面我们还会对内存管理子系统进行一次深度的分析. 在分析今天的内容之前,我们先来看出自http://bbs.chinaunix.net/thread-2018659-2-1.html的一位大神做的内存管理图,真心佩服大神.其实这张图可以

Linux内核设计第三周——构造一个简单的Linux系统

Linux内核设计第三周 ——构造一个简单的Linux系统 一.知识点总结 计算机三个法宝: 存储程序计算机 函数调用堆栈 中断 操作系统两把宝剑: 中断上下文的切换 进程上下文的切换 linux内核源代码分析 arch/目录保存支持多种CPU类型的源代码 其中的关键目录包括:Documentation.drivers.firewall.fs(文件系统).include init目录:含有main.c,内核启动相关的代码基本都在init目录下 start_kernal()函数为启动函数,初始化内

LINUX内核分析第三周学习总结——构造一个简单的Linux系统MenuOS

LINUX内核分析第三周学习总结——构造一个简单的Linux系统MenuOS 黄韧(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 回顾: [计算机三个法宝] 1)存储程序计算机 2)函数调用堆栈 3)中断 [操作系统两把宝剑] 1)中断上下文的切换:保存现场和恢复现场 2)进程上下文的切换 一.使用gdb跟踪调试内核从start_kernel到init进程启动 使用实验楼的虚拟机打开

例说Linux内核链表(三)

经常使用的linux内核双向链表API介绍 linux link list结构图例如以下: 内核双向链表的在linux内核中的位置:/include/linux/list.h 使用双向链表的过程,主要过程包括创建包括struct link_head结构的结构体(item),建立链表头.向链表中加入item(自己定义数据结构.双向链表数据单元).删除链表节点.遍历链表,判空等. 1.建立自己定义链表数据结构 struct kool_list{ int to; struct list_head li

Linux内核设计第三周学习总结 跟踪分析Linux内核的启动过程

陈巧然 原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验步骤 登陆实验楼虚拟机http://www.shiyanlou.com/courses/195 打开shell终端,执行以下命令: cd LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage-initrd rootfs.img 执行完毕后会弹出QEMU窗口,输

十天学Linux内核之第三天---内存管理方式

昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内存和内核的可用内存,还会讲到内核对内存分类的方式以及如何决定分配和释放内存,内存管理是应用程序通过软硬件协助来访问内存的一种方式,这里我们主要是介绍操作系统正常运行对内存的管理.插个话题,刚才和姐姐聊天,她快结婚了,说起了自己的初恋,可能是一句很搞笑的话,防火防盗防初恋,,嘎嘎,这个好像是的吧,尽管