玩转VFS(sys_open, overlayfs, rename)

带着问题学习dentry

1)文件删除时 dentry的结构是如何变化的?是直接设置无效的吗?如果此时正好有个访问是在要删除目录下的一个文件,这咋办呢?

2)内核document目录下级联式地使用 d_seq 去自顶向下地访问目录文件,到底是怎么实现的呢?顺序锁又是个什么东西?

3)dentry 应该是整个内核中被优化最多的地方了吧,既包括 RCU ,又有 seqcount_t, 甚至在RCU模式查找下两者都用到了!并且两种锁设计的初衷都是解决读和写互斥的问题,并且,seqcount_t锁好像是RCU的关键,这几天争取把这个问题搞明白的。

4)http://blog.csdn.net/denzilxu/article/details/9188003

overlayfs简直就是一个逆天的发明,不过这也可能是因为自己刚开始看。它让lowdir老老实实地在下面坐着等,我upperlayer的改动丝毫不会影响到lowerdir,这就逆天了知道吗?除了比较常用的iso文件的读写之外,你还可以搭建一个不同分支多世界观系统啊!比如f2fs-stable仓库中有3.10,4.1,4.4四个分支,每个分支你都是要用的,那么3.10时你就git checkout到3.10分支,需要4.1时你就checkout到4.1的分支,这不合适,那么就需要一个轻量级的多世界系统啊,从此之后,在不拷贝的情况下,你可以同时访问f2fs-stable的所有分支,咋样?

2017/2/7

rename操作简直就疯了,因为它涉及到的东西很多很多,之前一直以为读/写/删除才是文件系统中最重要的部分,但是后面发现rename和setlink操作才是这些操作中比较难的,不过这个难是集中在VFS层,因为VFS要保证文件不会被删除,IBM有篇博客是写文件系统加锁的,里面就涉及到nesting的锁。还有一些概念,比如文件系统的差异备份功能,文件系统的原子写等特殊功能,都是要好好看看的。

rename操作中主要有如下几个标签,包括 RENAME_EXCHANGE 和 RENAME_WHITEOUT 等,材料上说这些都是为union filesystem准备的,现在还不是很清楚到底是怎么回事,试下renameat函数。

2017/2/8

dcache存在的最大好处是不用在目录里一项项查找,

最近在看f2fs的rename,感到这个操作的复杂,晚上坐班车回来看微博,看到阿里的一群人也曾经碰到rename/mv的问题,这么说rename的问题不是孤例。

问题:cp,mv对inode的影响有什么不同;cp和mv覆盖动态库有什么区别;

cp A B, 会首先把B给截断掉,mv则是会保留

 2017/2/11

rename操作中涉及到的标签:RENMAE_NOREPLACE, RENAME_EXCHANGE, RENAME_WHITEOUT,这几个标签涉及到的操作。

在linux文件系统中,元数据的加锁操作基本上都是由其虚拟文件系统(VFS)规定的。这样的好处是可以统一管理所有元数据操作的加锁机制,底层的具体文件系统可以不理会这些问题,只需要按照VFS的调用来执行对元数据的操作即可。

在VFS中,对于大多数的元数据操作,可以通过制定统一的加锁顺序来避免死锁的发生,这个顺序是先对父目录加锁,再对要操作的对象加锁,以创建操作为例,当我们要在一个目录下创建新的对象时,必须先对这个目录进行加锁,然后才能放心地进行创建对象的步骤,因此也不用担心该目录中途会被删除,或者其他操作对该创建操作所造成的干扰。按照元数据名字空间的树状结构来看,我们可以认为,”先对父目录加锁,再对要操作的对象(目录或者文件)加锁“是一种从上到下的加锁顺序,只要所有的元数据都遵循这个规则,就不会出现相反的加锁顺序(即从下到上的顺序)。

这个普遍的加锁规则对绝大多数元数据操作都是适用的,比如创建、删除等操作,他们的加锁对象都是要操作的对象以及父目录。但是有两个元数据操作例外:rename和link,因为它们的操作对象不止两个,而这些对象可能位于名字树状结构中的任意几个位置,导致加锁的路径有可能不在名字空间树状结构”从上到下“的范畴内。【其实就是怕两空间是有交叠呗:比如 file1的路径是/home/hon/dir1/file1, file2的目录是/home/hon/dir1/dir2/file2,对file1的加锁路径是dir1->file1,对file2的加锁路径是dir2和file2,应该也没什么特殊才对啊】。错!rename中也有这样一个操作:

 http://www.ibm.com/developerworks/cn/linux/l-cn-fsmeta/

单纯的rename很简单,但是当多个rename同时操作:

进程1:mv /home/hon/dir1/file1 /home/hon/dir2/file11

进程2:mv /home/hon/dir2/file2 /home/hon/dir1/file22

此时进程1,先要对dir1上锁,再对dir2上锁,而进程2,先对dir2上锁,然后再对dir1上锁,那么这就有可能产生死锁了。这种隐患的根源在于:rename操作的source和dest可能是在名字空间中的任意一个位置。

所以使用了s_vfs_rename_mutex来保证互斥。在对 target 和 distest 分别进行加锁时要考虑是否是有亲戚关系的,即A的祖父目录是否是B,B的祖父目录是否是A,如果存在祖父的关系,那么需要先对祖父目录加锁,如果不存在这种关系,那么A和B的顺序就无所了。为什么要先加祖父呢?这是为了保证是一个从上到下的加锁关系啊,因为内核中其他部分都是从上到下的,你 s_vfs_rename_mutex 虽然解决了rename之间的互斥,但是没有处理其他方面的互斥,假设还有一个进程C,它在其整条path上顺序加上了所有的锁呢?那怎么办?这不就导致了rename和其他操作的死锁了吗?所以解决了rename之间的冲突后,那么在rename里面,我们就要遵循自上而下的加锁方法

你以为 move -i 仅仅是用户态的小把戏吗? 【如果真是这样,那么mv file1 file2命令中,无论file2是否存在,都会询问的啊,但是现在来看当file2不存在的时候,根本就没有这样指令输出来,所以move -i 在做真正的rename之前一定是对file2先查了一遍,看是否有这个目录存在,那么问题就来了,查询结束之后,mv会持有file2的引用计数吗?【在整个文件系统的目录中搜】】那么也很有可能发生如下的场景:我刚按下了y,此时有个进程就把file2和file1删掉了,甚至吧file1和file2的父目录给删掉了,这些情况都要咋整呢?】

fstat是一个挺常用的系统调用,会查询一个文件到底在不在

NOREPLACE 标签是说当目标节点存在的时候,

元数据的操作是一种事务操作。元数据的操作大多是由一些小的元数据操作组成,ACID是一个事务的基本特性:原子性,一致性,独立性,持续性。

2017/2/11  19:02 对dentry的构建再发起进攻.

有两种遍历的方式,一种是rcu-walk,一种是ref-walk。

在RCU查找的代码中,多处提到了与rename的互斥,怎么理解呢?

/××/ 插播一条信息:发现了一个很有意思的小工具叫ionotifywait,可以跟踪一个文件,并输出你对这个文件的操作,这个可能就是和文件系统中各处都是的 fsnotify 有关吧。

在 vfs_rename->d_move 中会对 dentry 进行重映射

为什么rename 在dentry的查找中一直被提及,这是因为rename操作涉及到dentry从一个 hash list 转移到另一个 hash list 中,而这种情形在普通的删除中是不存在的。这里好好说说。

这里涉及到RCU的grace period,在一次grace period彻底失效之前,这个被删除的dentry的指针是一直有效的,但是在rename中,由于他要在grace period之前把rename的dentry的及时插入到新的list中,所以其指针索引处就不是正确的,因此一定要避免rename这种情况。

A--B--C 三个dentry,怎么同时改变A和C的指针呢?

现在最关键的点是看 lookup_rcu 到底和 rename 冲突在哪里!或者说rename是怎么就在一个grace period里面就完成了历史性的从一个dentry hash表中删除,然后到另一个hash表中去。

普通的删除操作中在一个 grace period 中不会出现dentry的复用的【想起了f2fs中nid的复用!】

之前dentry想的方法是从头开始创建一个新的 dentry 插入到新的list中,但是这样做的效率太低,所以就想着直接”废物利用"了。在d_drop & d_rehash 外面,是被seq_count套住,所以lookup的地方可以在这里判断

原文地址:https://www.cnblogs.com/honpey/p/6348914.html