在 Linux 面试中,文件系统相关的问题几乎是绕不开的。很多候选人能说出“inode 存元数据”“dentry 是目录项缓存”,但一旦面试官追问“它们之间怎么协作”“挂载点到底改变了什么”,回答就开始含糊。其实,inode、dentry 和挂载这三个概念串起来,就是 Linux 虚拟文件系统(VFS)的核心工作流。本文从面试表达的角度,帮你把这套机制讲清楚。
先给一个全局视角
Linux 的文件系统层是分层的:上层是系统调用接口(open、read、write),中间是 VFS 抽象层,下层是具体文件系统(ext4、XFS、Btrfs)和块设备驱动。
VFS 的设计目标是让上层不关心底层是 ext4 还是 NFS。为了实现这个目标,VFS 定义了几个核心对象:superblock(超级块) 代表一个已挂载的文件系统实例,inode 代表一个文件或目录的元数据,dentry 代表路径中的一个节点,file 代表进程打开的文件。面试时如果能先画出这个对象关系图,后面解释就顺理成章。
inode:文件的“身份证”
inode(index node)是文件系统层面的概念,它存储一个文件的所有元数据,唯独不包含文件名。典型字段包括:
- 文件类型(普通文件、目录、符号链接、设备文件等)
- 权限位(rwx)和所有者(uid、gid)
- 文件大小
- 时间戳(atime、mtime、ctime)
- 链接计数(hard link count)
- 指向数据块的指针(直接块、间接块、extent 等)
关键点在于:文件名不在 inode 里。文件名保存在目录文件中,目录本质上是一张“文件名 → inode 号”的映射表。这也是硬链接能存在的根本原因——多个文件名可以指向同一个 inode,只要链接计数不为零,inode 和数据就不会被释放。
面试中常被追问的一个问题是:“删除一个文件发生了什么?” 准确回答是:unlink 系统调用减少目录项和 inode 的链接计数,当链接计数降为 0 且没有进程打开该文件时,inode 和数据块才真正被释放。如果进程还持有打开的文件描述符,文件会变成“已删除但仍被占用”的状态,lsof 里能看到 (deleted) 标记。
dentry:路径解析的加速器
dentry(directory entry)是 VFS 内存中的对象,不是磁盘上的持久结构。每个 dentry 把一个文件名和一个 inode 关联起来,同时维护父子关系,构成一棵 dentry 树。
dentry 存在的意义是加速路径查找。如果每次 open("/home/user/a.txt") 都要从磁盘逐级读取目录内容,性能会非常差。VFS 用 dentry cache(dcache)缓存最近访问过的路径节点,命中时直接在内存中完成解析。
dentry 有几种状态:
- 已使用(used):关联了有效的 inode,且被引用计数持有
- 未使用(unused):仍关联 inode,但引用计数为 0,可被回收
- 负状态(negative):对应的文件名不存在,缓存这个“不存在”的事实,避免重复查询磁盘
这里有一个容易混淆的点:dentry 和 inode 是多对一的关系。一个 inode 可以有多个 dentry(硬链接场景),但一个 dentry 通常只指向一个 inode。
挂载:把文件系统接入全局目录树
Linux 只有一棵全局目录树,所有文件系统都通过“挂载”接入这棵树的某个目录节点,这个节点就是挂载点(mount point)。
挂载的本质是:在 VFS 中创建一个 vfsmount 结构,记录“哪个文件系统(superblock)挂到了哪个 dentry(挂载点)上”。当路径解析走到挂载点时,VFS 会跨越到目标文件系统的根 inode,继续向下查找。
面试中可以用一个具体例子说明:
mount /dev/sdb1 /mnt/data
执行后发生的事:
- 内核读取
/dev/sdb1的超级块,识别文件系统类型(比如 ext4) - 创建对应的
superblock对象 - 在
/mnt/data这个 dentry 上设置挂载标志 - 之后访问
/mnt/data/report.pdf时,路径解析到data这个 dentry,发现它是挂载点,就切换到 sdb1 的根 inode,再查找report.pdf
这里要强调:挂载点本身的旧内容会被“遮蔽”,而不是被覆盖。卸载后原内容重新可见。这也是为什么误挂载到非空目录时,原有文件好像“消失”了。
三者的协作流程
把三个概念串起来,用一次 open("/mnt/data/report.pdf") 来说明:
- VFS 从根 dentry 开始,逐级查找
mnt、data - 查找到
data时发现它是挂载点,切换到 sdb1 的根 inode - 在 dcache 中查找
report.pdf对应的 dentry;未命中则调用具体文件系统的lookup操作,从磁盘目录中读取 inode 号 - 用 inode 号加载 inode(可能命中 inode cache),建立 dentry 与 inode 的关联
- 创建
file对象,返回文件描述符
这个流程体现了 VFS 的分层设计:dentry 负责路径结构和缓存,inode 负责文件元数据,mount 负责跨文件系统的边界切换。
面试中的常见追问
“dentry cache 和 inode cache 有什么区别?”
dcache 缓存的是路径名到 inode 的映射关系,inode cache 缓存的是 inode 本身。两者独立管理,但通过 dentry 指向 inode 关联。
“为什么 df 和 du 结果不一致?”
df 读的是 superblock 层面的块使用统计,du 遍历 dentry 和 inode 累加文件大小。已删除但被进程占用的文件会体现在 df 中但不被 du 统计。
“bind mount 和普通挂载有什么不同?”
bind mount 不涉及新的文件系统,只是把已有目录树的一个子树挂到另一个位置,共享同一个 superblock 和 inode。
总结
回答这类问题时,建议按“对象定义 → 数据结构关系 → 实际流程”的顺序展开。先说清 inode 是磁盘元数据、dentry 是内存路径缓存、mount 是文件系统接入点,再用一次路径查找把三者串起来。这样既展示了概念理解,也体现了对 VFS 整体设计的把握,比孤立地背定义更有说服力。
未经允许不得转载:任鹏个人博客 » Linux 面试中如何讲清楚 inode、dentry 与文件系统挂载

