NFS文件系统的unlink操作,当目标节点的目录下引用计数大于0时,则采用sillyrename策略,最后一个打开文件的进程关闭文件时候,会检测sillyrename的标志位,若置位,则将其删除。
×××××××××××××××××××××××××××××××××××
上文是我11年7月刚进入实验室看NFSv3代码时候,做的笔记,现在回头看,理解的连皮毛都算不上,着实汗颜。
最近看FUSE lib库的文件,又发现了FUSE也使用了silly rename策略就很好奇,为什么VFS层没有做一个统一的控制,而是交由具体的文件系统来处理,还有,为什么本地文件系统没有使用silly rename策略,现在略微懂了一些,把一些结论记录在此。
0.关于d_count,i_count,nlink
d_count描述有多少file结构指向这个dentry;
i_count描述有多少个dentry指向这个inode
nlink描述磁盘的硬链接数目。
filp_open filp_close会增加或减少d_count;
d_count减为0会递减i_count;
unlink会减少nlink;
1.nfs为什么要使用silly rename策略?
nfs v3是无状态的协议,在server端,就连基本的open- close的状态也没有,即客户的RPC中,是没有open和close 的操作的。
任何操作都是使用fh直接发请求,而fh的获取是最开始的lookup操作完成的。lookup操作会在server端建立起inode和dentry的数据结构,而一旦server端内存中有了dentry和inode结构,重复的lookup操作并不会增加引用计数d_count,这也就意味着,server端并不知道client端有多少个文件打开了同一个文件。
而在本地文件系统ext3中,d_count会随着open的调用而增加。
在nfs中,同一个客户端在执行unlink系统调用时候,若发现本机还有其他进程同时打开此文件时候,就会采用silly rename策略,否则server端就会简单粗暴的执行删除操作,而不去管是否还有其他进程也在使用同一个文件。
2.本地文件系统为何不必采用sillyrename 策略
当执行文件删除系统调用时,会调用vfs_unlink操作,此操作会将--nlink,然后尝试将对应dentry从哈希表中删掉,并递减i_count。由于本地文件系统,open时候会增加d_count,因此,不会导致dentry的错误删除。
而inode可能会成为孤儿节点。ext3和ext4都有专门的机制来处理。
orphan在英文中是孤儿的意思,在这里取被遗弃、被删除之意。
orphan inode是什么样的inode呢?这种inode是怎样产生的呢?
先介绍一个概念,文件的引用计数,准确地说应该是inode的引用计数,因为一般来说一个文件会对应一个inode。文件的引用计数,简单地说是表示有多少个文件指向该文件,准确地说是文件的硬链接的个数。
情况1:设想一个进程,open一个文件,然后unlink该文件,然后进行文件读写。这是允许的,并且在进程退出时,内核会自动将引用计数为0的文件删除。
但是如果该进程尚未退出之前,系统崩溃了,那么,内核就没有机会将已被unlink、并且引用计数为0的inode从磁盘上删除了。
情况2:设想我们正在截断一个大文件(系统调用truncate),但是操作尚未完成,系统就崩溃了。同样,内核也没有办法将该文件的所有数据块全部删除了。
ext3、ext4的orphan inode机制就是处理上述两种情况的。基本思想是这样的:如果要删除或截断一个inode,要先把这个inode记录到磁盘上的一个特殊的orphan inode链表上。如果删除或截断操作能够正常完成,那么,就从磁盘上的orphan inode链表上删除该inode;否则,如果删除或截断操作未完成之前,系统就发生崩溃了,那么,系统重启后,文件系统会遍历磁盘上的orphan inode链表,对链表上的每一个inode都重新进行一遍删除或截断操作,以此来保证这些inode真正在磁盘上被删除,维护文件系统的一致性。
内核版本:2.6.35
二、相关数据结构及之间的关系
先总体说一下orphan inode的组织。
orphan inode需要在两个地方组织,分别是在内存中和在磁盘上。不论在哪里,从抽象角度来看,orphan inode都被组织成一个单向链表。
1、ext4_inode
struct ext4_inode {
__le32 i_dtime; /* Deletion Time */
.........
}
这个是磁盘上的inode的结构,i_dtime本来表示该inode被删除的时间,在orphan inode机制中,因为此时该域的值并不重要,故借用一下,用于记录下一个被unlink/truncate的inode号。
2、ext4_super_block
struct ext4_super_block {