目录项dentry
在 Linux 中,目录项(通常称为 dentry,是 "directory entry" 的缩写)是一个核心的内核内存数据结构,属于虚拟文件系统层的一部分。
磁盘上的文件系统(ext4, XFS, Btrfs, FAT, NTFS 等)有自己独特的、优化过的格式来存储目录项信息(文件名、对应的 inode 号等)。例如:
-
ext4:使用哈希树 (HTree) 或传统的线性列表+哈希表存储目录项。
-
XFS:使用 B+树存储目录项。
-
FAT:使用文件分配表簇链和目录项表。
当内核需要读取一个目录的内容时:
- 底层文件系统驱动 (如 ext4.ko, xfs.ko) 负责从磁盘读取原始的目录块/节点。
- VFS 层根据这些原始数据,在内存中创建对应的 dentry 结构(及其关联的 inode 结构)。
- 这些 dentry 结构被链接起来(通过 d_parent, d_subdirs, d_children),形成内存中反映目录树结构的缓存。
- 当创建、删除、重命名文件/目录时,修改首先发生在内存中的 dentry (和 inode) 上,然后由底层文件系统驱动负责将更改同步(写回)到磁盘上的具体数据结构中。
目录项结构(struct dentry)存储的主要信息:
名称 (d_name):
- 存储该目录项代表的文件或目录的名称(例如,"myfile.txt")。
- 包含名称本身和其长度(hash 和 len)。
父目录指针 (d_parent):
- 指向父目录的 dentry 结构。
- 这是构建完整路径名和进行路径遍历(如 ../)的关键。
子项列表 (d_subdirs) 和 子项哈希表 (d_children):
- d_subdirs: 一个链表,链接所有直接位于该目录下的子目录项(文件或子目录)。
- d_children: 更常用的一个哈希表(链表头数组),用于根据子项名称快速查找该目录下的特定子项。内核主要使用这个哈希表进行高效查找。
关联的索引节点 (d_inode):
- 这是 dentry 最关键的指针!
- 指向与该目录项关联的索引节点结构(struct inode)。
- inode 才是真正包含文件元数据(权限、大小、时间戳、数据块位置等)和指向文件实际数据块的结构。dentry 本身不存储文件属性或内容。
- 一个 inode 可以被多个 dentry 指向(硬链接)。
挂载点信息 (d_mounted):
- 如果这个 dentry 代表的是一个目录,并且该目录是另一个文件系统的挂载点,这个字段会被设置(非零)。
- 当遍历路径遇到挂载点时,内核会切换到新挂载的文件系统的根 dentry。
状态标志 (d_flags):
- 包含一些内部状态位,例如:
- DCACHE_DISCONNECTED: 表示该 dentry 目前未连接到父 dentry(例如 NFS 场景)。
- DCACHE_OP_DELETE: 表示该 dentry 正在被删除。
- DCACHE_MANAGE_TRANSIT: 用于自动挂载点管理。
- 等等。这些标志主要用于内核内部管理。
引用计数 (d_lockref 或 d_count):
- 内核使用引用计数来跟踪当前有多少地方正在使用这个 dentry 结构(例如,有进程打开了该文件,或者它正在路径查找中被使用)。
- 当引用计数降为 0 时,内核不会立即销毁 dentry。它会进入一个“负”状态或放入 LRU 缓存,以便在短时间内如果再次访问同一路径可以快速重建,最终会被内存回收机制移除。
操作函数集指针 (d_op):
- 指向一个包含该 dentry 特定操作函数的结构(struct dentry_operations)。这些函数由底层的具体文件系统(如 ext4)提供,用于处理该文件系统特有的 dentry 操作,例如:
- d_revalidate: 检查缓存中的 dentry 是否仍然有效(对网络文件系统如 NFS 尤其重要)。
- d_weak_revalidate: 弱有效性检查。
- d_hash: 计算文件名哈希值的特定方法。
- d_compare: 比较两个文件名的特定方法(例如,是否大小写敏感)。
- d_delete: 当引用计数变为 0 时调用。
- d_release: 当 dentry 被真正释放时调用。
- d_prune: 通知底层文件系统 dentry 即将被释放。
- d_iput: 当关联的 inode 被释放时调用(如果文件系统需要额外操作)。
- d_dname: 为动态生成的 dentry 提供调试名称。
超级块指针 (d_sb):
- 指向该 dentry 所属文件系统的超级块结构(struct super_block)。超级块代表一个已挂载的文件系统实例。
挂载点信息 (d_mounted):这个字段存在的必要性?
- 挂载过程 (mount 命令):
- 当你执行 mount /dev/sdb1 /mnt/data 时:
- /dev/sdb1 是包含文件系统(比如 ext4)的块设备。
- /mnt/data 是宿主文件系统(比如根文件系统 ext4)中的一个空目录。
- 内核动作:
- 读取 /dev/sdb1 的超级块,在内存中创建一个新的 super_block 结构 (s_sdb1) 代表这个新的文件系统实例。
- 为这个新文件系统的根目录 (/) 创建内存中的 inode 和 dentry 结构。这个 dentry 就是新文件系统的 "根 dentry" (mount->mnt_root)。
- 找到宿主文件系统中 /mnt/data 目录对应的 dentry (dentry_mnt_data)。
- 在 dentry_mnt_data 上设置 d_mounted = 1,标记这是一个挂载点。
- 创建一个 struct mount 结构,它是挂载点的核心管理结构。这个结构的关键字段包括:
- mnt_mountpoint:指向宿主文件系统中的挂载点 dentry (dentry_mnt_data)。
- mnt_root:指向新挂载文件系统的根 dentry。
- mnt_sb:指向新文件系统的超级块 (s_sdb1)。
- 将这个 mount 结构加入到全局挂载树中。
- 路径遍历遇到挂载点 (例如访问 /mnt/data/file.txt):
- 内核从进程的当前目录(或根目录 /)的 dentry 开始解析路径 /mnt/data/file.txt。
- 它先找到 / 的 dentry (dentry_root)。
- 在 / 的子项中查找 mnt,找到其 dentry (dentry_mnt)。
- 在 dentry_mnt 的子项中查找 data,找到其 dentry (dentry_mnt_data)。
- 关键步骤: 内核检查 dentry_mnt_data 的 d_mounted 标志。发现它被设置了 (d_mounted == 1)。
- 内核知道 dentry_mnt_data 是一个挂载点。于是:
- 它查找与 dentry_mnt_data 关联的 mount 结构(通过挂载树)。
- 从这个 mount 结构中取出 mnt_root —— 这就是新挂载文件系统 (/dev/sdb1) 的根目录 (/) 的 dentry。
- 内核进行“跳转”:接下来的路径解析将从这个新的根 dentry (mnt_root) 开始,而不是继续在宿主文件系统的 dentry_mnt_data 下查找。
- 接下来的路径分量解析将在 dentry_newfs_root 及其子项中进行,完全处于新文件系统的上下文中。现在内核在新的根 dentry (mnt_root) 下查找 file.txt 的 dentry。
- 找到 file.txt 的 dentry 后,最终找到其对应的 inode。
存在的必要性:
- 保持宿主结构不变:
- 挂载后,宿主文件系统中 /mnt/data 目录对应的原始 dentry (dentry_host_data) 和 inode 保持不变。
- dentry_host_data->d_inode 仍然指向宿主文件系统上代表空目录 /mnt/data 的那个 inode。
- dentry_host_data->d_subdirs 理论上还是空的(宿主目录本身是空的)。
- 关键一步: 内核在 dentry_host_data 上设置 d_mounted = 1。这只是一个标记,表示“此目录是通往另一个世界的门户”,并不改变其本身代表的内容。
- 创建挂载结构 (struct mount):
- 这个 struct mount 对象是连接两个独立文件系统世界的“桥梁”。
- . 和 .. 的正确处理:
- 在新文件系统中 (/mnt/data/ 下):
- .:解析到 dentry_newfs_root (新文件系统的根)。
- ..:因为新文件系统的根是顶级目录,dentry_newfs_root->d_parent 通常指向自己。但在挂载点下,VFS 有特殊处理:
- 当在新文件系统内部解析 .. 时,如果当前目录就是该文件系统的根 (dentry == mnt_root),那么 .. 应该返回到宿主文件系统的挂载点目录 (dentry_host_data) 的父目录(即 /mnt)。这个逻辑由 follow_dotdot() 函数实现,它会检查是否在挂载点边界,并进行相应的反向“跳转”(回到宿主文件系统上下文)。
- 在宿主文件系统中:
- /mnt/data/. 解析到 dentry_host_data (宿主目录)。
- /mnt/data/.. 解析到 dentry_host_data->d_parent (宿主目录 /mnt)。

浙公网安备 33010602011771号