基数树是一种用空间换时间的数据结构,通过空间的冗余减少时间上的消耗.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. Apache的初中级面试题

    --- 原文:[关于Apache的25个初中级面试题](http://www.oschina.net/translate/apache-interview-questions) Apache 求职面试 ...

  2. 谷歌浏览器下载地址 chrome最新版本 百度云地址

    每次下载更新谷歌浏览器是一件很蛋疼的事情.百度搜索"谷歌浏览器下载地址",居然有很多骗子网站,相信有很多不知所以的人中招了.收集了一些chrome的安装包,放在了百度云里面(打不开 ...

  3. Egret白鹭H5小游戏开发入门(二)

    前言: 昨天的文章中简单的介绍了Egret白鹭引擎从安装到基本的使用配置等问题,今天着重介绍H5小游戏开发的起步阶段,如Wing面板的使用,素材的处理,类的说明,开始布局等等. 整体概况: 根据上一篇 ...

  4. 学习zepto.js(对象方法)[6]

    first: 获取当前对象集合中的第一个dom元素. $("div").first(); // 返回第一个div对象(zepto对象) //相当于$("div" ...

  5. sharepoint

    <script> $("#sideNavBox").css('display','none'); $("#contentBox").css('mar ...

  6. UITextView回收或关闭键盘

    iOS开发中,发现UITextView没有像UITextField中textFieldShouldReturn:这样的方法,那么要实现UITextView关闭键盘,就必须使用其他的方法,下面是可以使用 ...

  7. CSS3选择器——基本选择器

    CSS是一种用于屏幕上渲染html,xml等一种语言,CSS主要是在相应的元素中应用样式,来渲染相对应用的元素,那么这样我们选择相应的元素就很重要了,如何选择对应的元素,此时就需要我们所说的选择器.选 ...

  8. iOS之UIApplication详解

    UIApplication对象特点: 特点1: UIApplication对象是应用程序的象征,一个UIApplication对象就代表一个应用程序,而且是单例的.(用来封装整个应用程序的一个对象, ...

  9. UITableViewController和XML解析还有地图的简单结合

    然后我的代码就按照上面的这个顺序输出. #import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interfa ...

  10. SQL SERVER 临时表导致存储过程重编译(recompile)的一些探讨

    SQLSERVER为了确保返回正确的值,或者处于性能上的顾虑,有意不重用缓存在内存里的执行计划,而重新编译执行计划的这种行为,被称为重编译(recompile).那么引发存储过程重编译的条件有哪一些呢 ...