跳到主要内容
浏览器
导航
文件浏览器

inode、硬链接与软链接

文件的名字与数据并不在同一条记录里,中间隔着一层索引节点。本文说明 inode 保存与不保存的内容、目录项如何映射到 inode 编号、硬链接与软链接的结构差异,以及删除时的引用计数语义。

发布于 4 分钟阅读 1422 字

在类 Unix 文件系统上,文件名并不是数据的标识,而是一条指向索引节点(inode)的目录项。多个名字可以指向同一个索引节点,一个名字也可以只是另一段路径的字符串。理解这层结构,是解释链接行为与删除语义的前提。

inode 保存什么

索引节点记录的是描述数据所需的信息,而不是文件名本身。通常包含:

  • 属主与属组的标识,以及权限位
  • 文件类型(普通文件、目录、符号链接、设备节点等)
  • 尺寸、块计数与时间戳(访问、修改、状态变更)
  • 指向数据块的地址结构,例如直接块与多级间接块
  • 硬链接计数
  • 设备号与 inode 号组合,用于在同一系统内标识该节点

不保存在 inode 里的内容是文件名。名字归属于目录:目录本身也是一种文件,其数据块存放的是「名字到 inode 编号」的映射表。因此重命名一个文件通常只改动目录数据,索引节点保持不变。创建时间在部分实现中独立记录,而在另一些实现中并不单独保存,这一点随文件系统而异。

目录项到 inode 的映射

目录的数据区可以看作一张线性表,每一项给出条目名与该条目的 inode 编号,并用一个字段表示该条目的记录长度。名称之后常有填充字节用于对齐,因此实际占用的空间可能大于名称长度。删除条目时,多数实现只是把前一项的记录长度扩大,把被删项并入前一项,从而在没有独立空闲链表的情况下完成回收。

查找一个路径名需要按分隔符逐段解析:在父目录的数据区里匹配名称,取出 inode 编号,再读取该 inode 判断类型,决定是否继续下一段。

硬链接

硬链接的本质是在目录中新增一条映射到同一 inode 编号的条目,并使该 inode 的硬链接计数加一。由于它只是目录项,因此存在若干限制:

  • 不能跨文件系统:inode 编号只在单个文件系统内有意义,跨设备无法引用
  • 通常不能指向目录:若允许,会在目录树上形成环,使遍历与父目录推导失去定义
  • 硬链接与目标完全对等,没有「原始文件」的概念,各名字共享同一份属性与数据
ln target.txt hard.txt      # 新增硬链接,两项指向同一 inode
ls -l                       # 第二列即硬链接计数
stat -f hard.txt            # 查看所属文件系统的标识

多数实现限制硬链接计数为一个定长字段的表达范围,达到上限后创建会失败。

软链接

软链接是一个拥有独立 inode 的对象,其类型标记为符号链接,数据区存放的是一段路径字符串,长度通常受限于 inode 内可内联的空间。因为内容是路径,所以它的行为与硬链接并不相同:

特性硬链接软链接
指向方式目录项直接记录同一 inode 编号自身是独立 inode,内容为路径字符串
是否可跨文件系统不允许允许
是否可指向目录通常不允许允许
目标被删除后数据仍可访问,直到计数归零链接成为悬空链接
权限位与目标共享同一 inode自身有独立权限位,多数实现不据此判定访问
相对路径解释不适用相对链接基于链接自身所在目录解析

悬空链接并不妨碍对它本身做读取属性的操作,只是通过它访问目标时会失败。以相对路径为内容的软链接在移动位置后解释基准随之变化,而以分隔符开头、自根展开的完整路径形式则不受移动影响。

删除语义与引用计数

删除文件名的操作会减少 inode 的硬链接计数。当计数降到零且没有进程持有该文件的打开句柄时,数据块才会被释放;若有进程仍持有句柄,则空间保留到句柄关闭。这一语义解释了为什么删除大文件时占用空间不立即下降。

删除软链接只删除链接自身的 inode 与目录项,目标不受影响。反过来,删除软链接的目标也不会连带删除链接,只会让它悬空。

处理链接时需要区分「作用于链接自身」与「作用于目标」两类调用:

struct stat st;
lstat("entry", &st);   /* 不跟随,取链接自身的类型与属性 */
stat("entry", &st);    /* 跟随,取目标的类型与属性 */

char buf[PATH_MAX];
ssize_t n = readlink("entry", buf, sizeof(buf) - 1);
if (n > 0) buf[n] = '\0';

lstat 用于判断条目本身是否为链接以及它的 inode 信息;readlink 用于取出链接内容这段路径字符串,它不会尝试解析目标,也不会在目标缺失时失败。要在界面上展示链接指向何处,一般先取链接内容,再按需对目标做一次属性访问。

小结

文件名与数据之间隔着一层索引节点:目录保存名字到 inode 编号的映射,inode 保存属性与数据块地址。硬链接共享同一 inode 并依赖计数决定回收,软链接拥有独立 inode 并把路径当作内容。删除、重命名与悬空判定都建立在这层结构之上。

文件浏览器 · 相关文章