Board logo

标题: Linux 文件系统中元数据使用计数的机制(2)使用计数减少 [打印本页]

作者: look_w    时间: 2018-5-23 17:24     标题: Linux 文件系统中元数据使用计数的机制(2)使用计数减少

使用计数的减少dput 函数前面我们提到过,在元数据操作中,通过查找操作增加了 dentry 的使用计数,会在结尾处通过 dput() 进行递减。这里我们就来看看 dput() 的机制。
在 dput() 中,处理的步骤如下:
其中,释放 inode 结构的调用路径是:
1
dput() > dentry_iput() > iput() > iput_final()




在 iput() 中,会递减 inode 的使用计数,如果递减完后为0,就进一步调用 iput_final()
1
iput_final() > generic_drop_inode()




在 generic_drop_inode() 中,会判断 inode -> i_nlink 的值。我们在前面说过, i_nlink 这个域表示 inode 的 hard link 数目,这里就是通过这个域来判断该 inode 能否被删除。
若 inode -> i_nlink 为0,说明没有 hard link 指向该 inode ,可以将其删除,路径为:
1
generic_drop_inode() > generic_delete_inode() > s_op->delete_inode()




若 inode -> i_nlink 不为0,说明仍有 hard link 指向该 inode ,不能将其删除,路径为:
1
generic_drop_inode() > generic_forget_inode()




释放 inode 结构的步骤就是这些。释放 dentry 的主要做的事情就是将 dentry与inode 脱离,然后释放 dentry 结构。要注意的是,每当释放了一个 dentry ,都要获取其原来父目录的 dentry ,然后又跳转到 dput() 的开头,继续对父目录的 dentry 进行释放操作。这是因为,前面也提过,每次创建一个 dentry 结构,除了增加自身的使用计数外,还会增加其父目录 dentry 的使用计数。所以当释放了一个 dentry 后也要将其父目录 dentry 使用计数递减,才能保证父目录为空时能够被释放。
unlink 函数在 dput() 中我们提到过要判断 dentry 是否从 dcache 的哈希链上移除。在本地文件系统中,当删除一个文件时,就会将其对应的 dentry 从 dcache 的哈希链上移除,这是通过 unlink() 来实现。unlink() 的流程如下:
1
sys_unlink() > do_unlinkat() >




在do_unlinkat()中,主要可以分为五个部分:
1
2
3
4
5
(1) dentry = lookup_hash(&nd);
(2) atomic_inc(&inode->i_count);
(3) vfs_unlink(nd.dentry->d_inode, dentry);
(4) dput(dentry);
(5) iput(inode);    /* truncate the inode here */




可以看出,主体函数 vfs_unlink() 前后,有配对的操作对 dentry 和 inode 进行增减。
我们先看 vfs_unlink() ,其路径为:
1
vfs_unlink() > d_delete()




在 d_delete() 中,如果 dentry 的使用计数为1,说明此时没有其他人引用该 dentry ,那么就尝试把该 dentry 的 inode 删除,这里调用的是 dentry_iput() ,这个函数已经在 dput() 那部分介绍过了。这个过程的实质就是把 dentry 转为 negative 状态,然后返回。转为 negative 的 dentry 依然在 dcache 的哈希链中,但删除操作已经完成,对应的代码为:
1
2
3
4
5
if (atomic_read(&dentry->d_count) == 1) {
    dentry_iput(dentry);
    ...
    return;
}




否则,即 dentry 的使用计数大于1(这里不可能小于1,因为 do_unlinkat() 中调用 lookup_hash() 时已经对 dentry 的使用计数进行了增加),说明有其他人引用该 dentry ,此时不能把这个 dentry 转为 negative ,那么就把这个 dentry 从 dcache 的哈希链中脱离,对应的代码为:
1
2
if (!d_unhashed(dentry))
    __d_drop(dentry);




那么什么时候删除这个 dentry 所对应的 inode 呢?大家可以回过头看一下 dput() 介绍过的内容。在 dput() 中,当 dentry 已经从 dcache 的哈希链上移除后,就会继续进行释放元数据的操作。所以只要当最后一个使用 dentry 的操作结束时调用 dput() ,就会调用 dentry_iput() 。
总结一下,删除 inode 必须由 unlink 的 d_delete 发起,如果可能就在 d_delete 中完成删除;否则就 unhash ,然后由最后一个使用者调用 dput() 删除。但只有在 d_delete 完成 unhash 之后, dput 才有可能删除 inode 。
看完主体函数 vfs_unlink() 后,我们关注一下对 inode 使用计数的增减。
在 do_unlinkat() 中,调用 vfs_unlink() 之前,递增了 inode -> i_count ,因此,如果刚进入 do_unlinkat() 时 dentry 的使用计数为1,真正的删除操作并不是在 vfs_unlink() 的 d_delete() 进行,而是在 vfs_unlink() 之后的 iput() 进行(源码里也在 iput() 旁边进行了注释)。
大家也许要问, vfs_unlink() 之后,在 iput() 之前不是还有一个 dput() 吗?那么会不会在这里就删除了 inode 呢?其实不会,我们分三种情况来讨论:
总结一下,这部分的操作比较繁杂,需要结合 dput() 和 unlink 操作一起来看,但理清思路后,也会发现其逻辑还是很清晰的,而这种机制也与使用计数的增加结合得非常好,共同构成了 Linux 文件系统管理内存元数据结构的机制。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0