基数树是一种用空间换时间的数据结构,通过空间的冗余减少时间上的消耗.radix tree很适合稀疏的结构!

自从把RCU机制引入到基树中来,这里就有了个协议叫做:lockless的page-cache协议!

一个小小的find_get_entry函数,里面处理了两种异常的情况,一是文件系统的page被free或者将要被free掉的情况,二是文件系统的page被remove掉的情况!第三种异常是slot中直接被标记无效的情况,还有就是slot中没有对应的page*的情况!但是这个槽为什么能搜到?是否和基树的结构有关呢?

tmpfs和procfs在内存中是个什么样子呢? 是不是也是这样一个superblock挂上一个pagecache树这样的情形?

RCU锁应该是内核中最不好想的锁了吧?

介于文件系统中大量用到这个锁:包括pagecache变成lockless的了,还有dentry的管理也是采用RCU的方式.

RCU的原理很简单:该机制记录了指向共享数据结构的指针的所有使用者.在该结构将要改变时,首先创建一个副本!在副本中修改!在所有进行读访问的使用者结束对旧副本的读取之后,指针可以替换为指向新的,修改之后的副本的指针.这种机制允许读写并发执行!

反引用指针并使用其结果的代码,需要用rcu_read_lock和rcu_read_unlock调用保护起来:

rcu_read_lock();

p = rcu_dereference(ptr);

if (p != NULL)

do_something(p);

rcu_read_unlock();

如果必须要修改ptr指向的对象,则需要使用rcu_assign_pointer:

struct super_duper *new_ptr = kmalloc(...)

new_ptr->meaning = xyz;

new_ptr->of = 42;

new_ptr->life = 23;

rcu_assign_pointer(ptr, new_ptr);

根据RCU的术语,该操作公布了这个指针,后续的读操作将看到新的结构,而不是原来的.

如果更新操作来自于内核中的许多地方,那么必须使用普通的同步原语,防止并发的写操作,如自旋锁.尽管RCU能够保证读访问不受写访问的干扰,但它不对写访问之间的相互干扰提供保护.

新的值已经公布以后,就得结构实例会被释放!在所有的读访问完成以后,讷河可以释放该内存,但它需要知道何时释放内存是安全的.为此,RCU提供了另外两个函数:

synchronize_rcu()等待所有现存的读访问完成.在该函数返回之后,释放与原指针关联的内存是安全的!

2. call_rcu可用于注册一个函数,在所有针对共享资源的读访问完成之后调用.这要求将一个rcu_head实例嵌入(不能通过指针)到RCU保护的数据结构:

struct super_duper {
     struct rcu_head head;

int meaning, of, life;

}

该回调函数可以通过参数访问对象的rcu_head成员,进而使用container_of机制访问对象本身.

其中super_duper是整个要释放的结构体

RCU机制中引入了垃圾回收器,真是个高大上的概念!

RCU不维护锁!

CU中有一篇文章把这个问题说得还不错, 但是仍然不知道call_rcu函数是怎么用的, 以及

http://blog.chinaunix.net/uid-23769728-id-3080134.html
http://blog.chinaunix.net/uid-20648784-id-1592810.html

--------------------

2016年11月5日

RCU writer在释放元素一般会有call_rcu, RCU writer在释放元素时的三种调用方式:调用synchronize_rcu,同步方式,

list_del_rcu(p);

synchronize_rcu();

kfree(p);

另一种是异步方式,在iRQ中调用call_rcu,异步方式

list_del_rcu(p);

call_rcu(&p->rcu, p_call_back);

static void p_callback(stuct rcu_head *);

那么就从page_cache中调用call_rcu来看看一个radix_tree_node是如何被平稳释放的!

并且同步的释放节点和异步的释放节点会不会有什么不同呢?

异步释放与同步释放是不是为了解决不同的问题?

同步释放

为什么page-cache会用rcu机制? 其实并不是为了一个page, 或者说是slot的互斥, 而是radix-node粒度的互斥. 因为page粒度的互斥, lock_page已经做了!  所以不用担心此处, 所以page-cache算是巧用了rcu机制.

为什么, 因为rcu机制本质上是很简单地对一个共享区的互斥: 很简单的一个场景, 全局变量, 所有的rcu机制都围着这一个变量转! 但是page-cache里的rcu不同了[说到这里, 好像也没啥不同的......晕,只是10分钟前一直没有找到rcu要保护的对象而已......]

也就是说整个page-cache树中的结构有两个同步机制, 一是lock_page, 二是rcu锁.

那么address_space也不难理解了,  tree_lock就是用来保护写了.

真是由于pagecache的RCU化,导致了每次lock_page成功之后, 都要判断page->mapping != mapping

=========================================

缓存是计算机中非常重要的概念, 其思想是数据访问的时间局部性,即当前被访问的数据后续很可能还会被访问到. 文件的访问是现代文件系统中pagecache是内核中非常重要的缓存,

对pagecache的操作主要有:查找,插入,删除.

插入和删除操作都是写操作, 所以需要address_space->tree_lock来保证互斥,是整棵树层面的互斥,这里是典型的RCU的写使用场景.

查找操作不需要加锁

对查找的影响是什么?

1) 暂时还找不到一个page, 查找操作结束后page进来了, 某些gang操作, insert进来的page可能不被临幸;

2) 找到了一个page, 但是这个page后来被释放掉了(最典型的场景是删除了一个page);

这些都是由于RCU引起来的吗?

首先谈比较熟悉的2)即删除的场景: 函数pagecache_get_page中,首先通过 find_get_entry 得到一个page, 然后lock_page, 就在这个find_get_entry和lock_page的空挡处, 这个page可能就被truncate掉了! 被truncate掉的page有什么什么特征呢? 被truncate掉的page->mapping is null

所以在lock_page的时候,一定要注意这个page的的mapping是否还是原来的mapping, 只要是, 那么我们就可以断定这个page肯定是不可以被truncate掉的!

2)中的场景肯定是由RCU引起来的,因为这样可以保证最大限度的读并发效率; 那么1)的场景会导致这个问题吗?应该是不会的!要么能得到这个page,要么就得不到!

那么还有个问题,释放的page什么时候回收? delete部分是pagecache操作中比较有意思的一部分,首先是从page-cache中去掉这个page,然后是调用put_page, 如果此时普通_page后page_count仍然不是zero, 那么说明page还在用, 直至最后一个page的user释放了这个page, 那么page归还到buddy中去.

其实整个系统中涉及到插入删除的还真不是page, 而是radix_tree_node结构体, page-cache是基于radix-tree的, 所以radix-tree的插入删除算法中涉及到中间索引节点的变更,radix_tree_node的变更!

中间节点的变更其实只是page插入删除的一个副反应! 为什么这么说是因为page-cache的操作对象都是page,page通过中间的radix_tree_node索引

在删除radix_tree_node时, 必须要有page的locked状态! lockpage与RCU完美搭配,实现了tree_node顺利卸载!

那么问题又来了,如何rcu是如何保证读和写同时读到一个指针时读不会读到中间状态的?(和RCU机制相关,assigned)

入下面代码, 指针的赋值采用 rcu_assign_pointer 去做: #define rcu_assign_pointer(p, v) smp_store_release(&p, RCU_INITIALIZER(v))

 56 #define smp_store_release(p, v)                     \
57 do { \
58 compiletime_assert_atomic_type(*p); \
59 smp_mb(); \
60 ACCESS_ONCE(*p) = (v); \
61 } while (0)
62

这里是一个原子的操作[后续可以研究下arm64中原子操作是怎么完成的], 后面的rcu reader就都读不到这个page了, 但和教科书中案例相比,本例有个非常非常重要的不同, 那就是指针指向的page是不需要调用free掉的,因为这棵树中的指针直接就是struct page结构体的地址, 有点探底了,不涉及到内存的释放,这里就是最彻底的释放了!

那么radix_tree_node的释放是怎么回事呢? 怎么就丝毫没有使用assign_pointer这样的赋值呢?

是因为rcu的本质是去找page,所以slot中指向page的指针才是要保护的关键, 所以radix_tree_node就是可有可无的赋值了?

[page chache的命中率, extent_tree的命中率,对数字敏感!]

--------2016.11.07

pagecache的

http://lwn.net/Articles/291826/

基数树与RCU锁的更多相关文章

  1. 基数树(radix tree)

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

  2. Linux 内核中的数据结构:基数树(radix tree)

    转自:https://www.cnblogs.com/wuchanming/p/3824990.html   基数(radix)树 Linux基数树(radix tree)是将指针与long整数键值相 ...

  3. (转)Linux内核基数树应用分析

    Linux内核基数树应用分析 ——lvyilong316 基数树(Radix tree)可看做是以二进制位串为关键字的trie树,是一种多叉树结构,同时又类似多层索引表,每个中间节点包含指向多个节点的 ...

  4. nginx 学习八 高级数据结构之基数树ngx_radix_tree_t

    1 nginx的基数树简单介绍 基数树是一种二叉查找树,它具备二叉查找树的全部长处:检索.插入.删除节点速度快,支持范围查找.支持遍历等. 在nginx中仅geo模块使用了基数树. nginx的基数树 ...

  5. [翻译]Linux 内核里的数据结构 —— 基数树

    目录 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree Linux内核基数树API 链接 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree 正如你所知道 ...

  6. linux RCU锁机制分析

    openVswitch(OVS)源代码之linux RCU锁机制分析 分类: linux内核  |  标签: 云计算,openVswitch,linux内核,RCU锁机制  |  作者: yuzhih ...

  7. 014-数据结构-树形结构-基数树、Patricia树、默克尔树、梅克尔帕特里夏树( Merkle Patricia Tree, MPT)

    一.基数树 Radix树,即基数树,也称压缩前缀树,是一种提供key-value存储查找的数据结构.与Trie不同的是,它对Trie树进行了空间优化,只有一个子节点的中间节点将被压缩.同样的,Radi ...

  8. 菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t[转]

    菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blo ...

  9. 一步一步分析Gin框架路由源码及radix tree基数树

    Gin 简介 Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much ...

随机推荐

  1. java servlet 几种页面跳转的方法及传值

    java servlet 几种页面跳转的方法及传值   java web 页面之间传值有一下这几种方式1.form 表单传递参数2.url地址栏传递参数3.session4.cookie5.appli ...

  2. android.view.InflateException: Binary XML file line #34: Error inflating class

    问题一般出在xml的第三方View的全类名,你可能是直接粘贴过来的,没有改成自己项目的全类名.

  3. c#中奖算法的实现

    算法名称 Alias Method public class AliasMethod { /* The probability and alias tables. */ private int[] _ ...

  4. JavaScript高阶函数

    所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并返回一个新的函数用以计算f(g ...

  5. mariadb 10.2.3支持oracle execute immediate语法

    在之前的版本包括oracle mysql/percona server版本中,所有的动态SQL都需要通过prepare执行,如下: "; execute stmt; deallocate p ...

  6. 异常处理的解决方案 OneTrueError

    应用程序安装在用户计算机上,异常处理一直是反复出现的问题.用户报障中的描述不足以重现该问题.你不得不猜测,或者只是做猴子测试,以找出其异常出现的根源. 最严重的问题是当认为你已经找出了原因并纠正它,但 ...

  7. 纯HTML5+CSS3制作生日蛋糕

    以一个前端开发的身份绘制一个简单的蛋糕庆祝一下今天这个好日子吧,程序员庆生的乐趣与哀愁啊.写的比较简陋,感兴趣的看一下吧. 先发个效果图吧 蛋糕分为三个部分,底部蛋糕,顶层蛋糕和蜡烛部分.HTML的布 ...

  8. ArcGIS 10.5新功能预览

    ArcGIS for Server产品线被重命名为ArcGIS Enterprise. 带来更多丰富的时空GIS功能. 分析地理大数据 捕捉和分析实时传感器数据 快速地理影像分析 ArcGIS Ent ...

  9. Appfuse:起步

    在众多开源的Java开源CMS中探索了很久,终于选定了appfuse,理由如下: 1. 简洁:只搭建了框架,没有做多余的事 2. 完成了基本的用户管理:用户.角色.权限的定义很清晰 3. 符合预期的架 ...

  10. 你买了多少ERP?

    企业使用ERP(或是其他管理系统),有三种模式:自开发.采购.租赁(SAAS).不知道企业的各级管理者们,是否想过这个问题——我们到底买(做.租)了多少“量”的ERP呢? 这个问题也许不是那么好回答. ...