基数树与RCU锁
基数树是一种用空间换时间的数据结构,通过空间的冗余减少时间上的消耗.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锁的更多相关文章
- 基数树(radix tree)
原文 基数(radix)树 Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制).内存管理等.ID ...
- Linux 内核中的数据结构:基数树(radix tree)
转自:https://www.cnblogs.com/wuchanming/p/3824990.html 基数(radix)树 Linux基数树(radix tree)是将指针与long整数键值相 ...
- (转)Linux内核基数树应用分析
Linux内核基数树应用分析 ——lvyilong316 基数树(Radix tree)可看做是以二进制位串为关键字的trie树,是一种多叉树结构,同时又类似多层索引表,每个中间节点包含指向多个节点的 ...
- nginx 学习八 高级数据结构之基数树ngx_radix_tree_t
1 nginx的基数树简单介绍 基数树是一种二叉查找树,它具备二叉查找树的全部长处:检索.插入.删除节点速度快,支持范围查找.支持遍历等. 在nginx中仅geo模块使用了基数树. nginx的基数树 ...
- [翻译]Linux 内核里的数据结构 —— 基数树
目录 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree Linux内核基数树API 链接 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree 正如你所知道 ...
- linux RCU锁机制分析
openVswitch(OVS)源代码之linux RCU锁机制分析 分类: linux内核 | 标签: 云计算,openVswitch,linux内核,RCU锁机制 | 作者: yuzhih ...
- 014-数据结构-树形结构-基数树、Patricia树、默克尔树、梅克尔帕特里夏树( Merkle Patricia Tree, MPT)
一.基数树 Radix树,即基数树,也称压缩前缀树,是一种提供key-value存储查找的数据结构.与Trie不同的是,它对Trie树进行了空间优化,只有一个子节点的中间节点将被压缩.同样的,Radi ...
- 菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t[转]
菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blo ...
- 一步一步分析Gin框架路由源码及radix tree基数树
Gin 简介 Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much ...
随机推荐
- 十一个行为模式之备忘录模式(Memento Pattern)
定义: 在不破坏原有封装的情况下,捕获一个对象的内部状态,并在对象之外保存.当对象出错或者无效是,可以根据该备忘录进行恢复. 结构图: Originator:原发类,被记录的对象,包含若干内部状态.一 ...
- Elasticsearch初步使用(安装、Head配置、分词器配置)
目录 返回目录:http://www.cnblogs.com/hanyinglong/p/5464604.html 1.ElasticSearch简单说明 a.ElasticSearch是一个基于Lu ...
- 谷歌浏览器下载地址 chrome最新版本 百度云地址
每次下载更新谷歌浏览器是一件很蛋疼的事情.百度搜索"谷歌浏览器下载地址",居然有很多骗子网站,相信有很多不知所以的人中招了.收集了一些chrome的安装包,放在了百度云里面(打不开 ...
- 元首的愤怒 SharePoint Apps
柏林数据中心的服务器机架已经插满.CPU 100%.电力基础设施处在崩溃的边缘,但当元首决定迁移到 Office 365 的时候,将军们却告诉他那里没有 Farm Solution,5 年多的投资将付 ...
- vmwawre 虚拟机优化配置
vmware虚拟机如何设置不当的话会造成运行速度慢,并影响主机运行,甚至会出现死机. 一下是提高vmware虚拟机运行速度的几个技巧, 文章来自:http://blog.csdn.net/shanzh ...
- 如何提升代码编译的速度 iOS
前阵子有遇到代码编译速度慢的问题,特别是在swift和object-c混编的过程中问题很突显. 网上找到一篇蛮好的文章里面又一些解决方法 推荐一下 http://www.open-open.com/l ...
- 隐式启动判断是否有匹配的Intent
一.PackageManager的resolveActivity public abstract ResolveInfo resolveActivity(Intent intent, int flag ...
- Linux下Oracle 10.2.0.1升级到10.2.0.4总结
最近部署测试环境时,将测试环境ORACLE数据库从10.2.0.1升级到了10.2.0.4,顺便整理记录一下升级过程. 实验环境: 操作系统:Oracle Linux Server release 5 ...
- 【转】Hive的insert操作
insert 语法格式为: 1. 基本的插入语法: insert overwrite table tablename [partition(partcol1=val1,partclo2=val2)] ...
- PostgreSQL-系统表、系统视图
系统表显示的都是当前操作数据库下的信息,对象都来自当前数据库.因为不同的系统表都用不同名的字段来记录不同对象的oid,这个表引用那个表,那个表又引用另一个表,所以这些字段名不太好记. pg_class ...