Linux内核Radix Tree(二)

1.   并发技术

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

通过使用RCU,RCU Radix树可以进行完全并发的查询操作。RCU从根本上要求原子操作地移动指针从数据结构的一个版本到新的版本,保持旧版本直到系统经过静止状态。在静止状态点,旧版本数据结构已没有用户,因此可以被安全释放。

RCU radix树的修改操作之间还需要串行化,但是查询不再需要与修改操作串行化。

RCU可使RCU radix树查询完全并行化,但修改操作成了“瓶颈”。这可通过将全树的锁破碎成较小的锁进行改善,再明显的方法是对结点进行加锁而非对整个树加锁。

radix树修改操作可分为单向和双向操作。单向操作仅执行从根节点和叶子结点的单方向指针移动,它包括插入、更新和设置标签操作。双向操作较复杂,它需要在指针移到叶子后又回移,它包括删除和清除标签操作。
梯级加锁(Ladder Locking)和锁耦合(Lock-Coupling)技术常用于数据库方面,允许单向遍历结点加锁的树(双向可能产生死锁)。如果所有的修改者从树顶到树底进行修改,并且修改的结点持有锁,那么,向下遍历时对孩子加锁,在孩子被锁住时再释放该结点锁。在这种情况下并发操作是可能的,因为只要根结点解锁,另一个操作就可以自上向下进行。如果两操作的路径没有相同操作结点,后一个操作可能在前一个操作完成之前完成。最坏的情况是流水线操作,但这还是比串行化操作好很多。

双向操作包括删除和清除标签操作,分别说明如下:

8.1清除标签

在radix树中清除一个标签包括向下遍历树、查找定位条目和清除条目标签的操作。只要孩子结点没有打标签的条目,就可以向上遍历结点清除标签。结束条件是:如果遍历遇到一个结点,在清除一个标签后,它还有一个或多个条目带有标签集,就可以结束向上遍历。为了与向下遍历期间有同样的结束点,将终止条件改为:向上遍历将在有比清除标签数更多标签的结点处结束。这样,不论何时遇到这样的结点,将作为上遍历树的结束点。

8.2删除元素

删除元素在删除无用结点时还需要删除该条目的所有标签。它的终止条件需要满足这两个方面。向上回退遍历树时需要满足下面的条件:当遇到一个非空结点且没有无用的标签时应终止向上回退遍历树。
在向下遍历树时鉴别此点的条件是:当遇到有超过2个孩子的结点、并且每个标签来说结点有多于一个标签条目被清除时,结束向上遍历。该条件用来鉴别向上回退遍历的终止点。

8.3并行操作的API实现:查询获取slot操作

查询操作支持RCU无阻塞并行读操作,因此,需要遵循RCU的用法加RCU读锁,还需要将rcu_dereference()用于获得的slot,在写(或更新)操作时,需要给新的slot使用rcu_assign_pointer()。查询操作的使用方法列出如下:

struct page **slot, *page;
rcu_read_lock();
slot = radix_tree_lookup_slot(&mapping->page_tree, index);
page = rcu_dereference(*slot);
rcu_read_unlock();

8.4并行操作的API实现:查询修改slot操作

Linux内核的radix树需要打补丁才支持并发修改。查询仅有一个全局状态:RCU静止状态,并发修改需要跟踪持有什么锁。锁状态对于操作来说必须是外部的,因此,我们需要实例化一个本地上下文跟踪这些锁。查询修改slot的方法列出如下:

struct page **slot;
DEFINE_RADIX_TREE_CONTEXT(ctx,&mapping->page_tree);
radix_tree_lock(&ctx); /*锁住了根结点*/
/* ctx.tree代替&mapping->page_tree作为根,可以传递上下文
slot = radix_tree_lookup_slot(tx.tree, index);
rcu_assign_pointer(*slot, new_page);
radix_tree_unlock(&ctx);

radix树API函数radix_tree_lookup_slot含有锁从树顶向下移动机制,锁移动的代码部分列出如下:

void **radix_tree_lookup_slot(struct
radix_tree *root, unsigned long index)
{
    ...
    RADIX_TREE_CONTEXT(context, root); /*提供上下文和实际的root指针*、
    ...
    do {
        ...
        /* 从树顶向下移动锁*/
        radix_ladder_lock(context, node);
        ...
    } while (height > 0);
    ...
}

2.  
其他注意点

间接指针和直接指针

在真实环境中,地址都是字节对齐的,所以不存在最后一位为1的情况。那么就可以用地址的最后一位来标识一些有用的信息:标识为0则意味着该节点是直接节点,直接指向item数据,反之则则为间接节点,指向的是下一层节点。

#define RADIX_TREE_INDIRECT_PTR   1

//把最后一位值为1

static inline void
*radix_tree_ptr_to_indirect(void *ptr)

{       return
(void *)((unsigned long)ptr | RADIX_TREE_INDIRECT_PTR);  }

//把最后一位值为0

static inline void
*radix_tree_indirect_to_ptr(void *ptr)

{       return
(void *)((unsigned long)ptr & ~RADIX_TREE_INDIRECT_PTR);  }

//判断是否是间接节点

static inline int
radix_tree_is_indirect_ptr(void *ptr)

{       return
(int)((unsigned long)ptr & RADIX_TREE_INDIRECT_PTR);  }

时间: 2024-10-15 03:30:51

Linux内核Radix Tree(二)的相关文章

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(三):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, **s

Linux内核分析(二)----内核模块简介|简单内核模块实现

Linux内核分析(二) 昨天我们开始了内核的分析,网上有很多人是用用源码直接分析,这样造成的问题是,大家觉得很枯燥很难理解,从某种意义上来说linux系统本身就是由一个个模块构成的,所以我会结合内核模块的设计,去分析内核,从而达到对linux内核的理解. 今天我们会分析到以下内容: 1.      Linux内核模块简介 2.      简单内核模块实现 l  Linux内核模块简介 1.       何为内核模块 在上一篇博文中我们先通过内核配置,在配置的过程中我们对内核的组件进行了选择(当

“Linux内核分析”实验二报告

张文俊 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.第二周学习内容总结 1.计算机工作“三大法宝” 首先,计算机工作原理最重要的三个内容就是:存储程序计算机工作模型.中断机制和函数调用堆栈. 存储程序计算机工作模型是计算机系统最最基础性的逻辑结构: 中断机制是多道程序操作系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其他程序: 函数调用堆栈是高级语言得以

例说Linux内核链表(二)

链表使用 我认为熟悉内核链表功能最好的方法就是看一些简单的实例,实例是一个非常好的素材去更好的理解链表. 下面是一个例子,包含创建,添加,删除和遍历链表. <span style="font-size:14px;"><span style="color:#330099;">#include <stdio.h> #include <stdlib.h> #include "list.h" struct

把握linux内核设计(二):硬中断及中断处理

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 操作系统负责管理硬件设备,为了使系统和硬件设备的协同工作不降低机器性能,系统和硬件的通信使用中断的机制,也就是让硬件在需要的时候向内核发出信号,这样使得内核不用去轮询设备而导致做很多无用功. 中断使得硬件可以发出通知给处理器,硬件设备生成中断的时候并不考虑与处理器的时钟同步,中断可以随时产生.也就是说,内核随时可能因为新到来的中断而被打断.当接收到一个中断后,中断控制器会给处

linux 内核移植(二)——操作系统

一:什么是操作系统 (1)linux.windows.android.ucos就是操作系统 (2)操作系统本质上是一个程序,由很多个源文件构成,需要编译 连接成操作系统程序(vmlinz.zImage) (3)操作系统的主要作用就是管理计算机硬件,给应用程序提供一 个运行环境. 二:操作系统的核心功能 (1)内存管理.如果没有操作系统,内存是需要程序自己来管理 的.譬.如在uboot中要使用内存的哪里是自己随便用的,没有注册也 没有限制.这时候如果程序自己不小心把同一块内存重复用了就会出 现程序

Linux内核分析实验二:mykernel实验指导(操作系统是如何工作的)

计算机是如何工作的?(总结)——三个法宝 存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: 函数调用堆栈,高级语言得以运行的基础,只有机器语言和汇编语言的时候堆栈机制对于计算机来说并不那么重要,但有了高级语言及函数,堆栈成为了计算机的基础功能: enter pushl %ebp movl %esp,%ebp leave movl %ebp,%esp popl %ebp 函数参数传递机制和局部变量存储 中断,多道程序操作系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其他程

基数树(radix tree)

原文 基数(radix)树 Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制).内存管理等.IDR(ID Radix)机制是将对象的身份鉴别号整数值ID与对象指针建立关联表,完成从ID与指针之间的相互转换.IDR机制使用radix树状结构作为由id进行索引获取指针的稀疏数组,通过使用位图可以快速分配新的ID,IDR机制避免了使用固定尺寸的数组存放指针.IDR机制的API函数在lib/idr.c中实