inode与链接
文件的元数据(权限、属主、时间戳、数据块位置)存储在 inode 中,文件名只是目录里指向 inode 的一条记录。理解这一点,才能解释硬软链接的差异、"磁盘没满却写不进文件"等经典问题。
inode 是什么
一个文件由两部分组成:inode(元数据)和数据块(内容)。inode 记录权限、属主、大小、时间戳,以及内容存放在哪些数据块上,但不存文件名。文件名存在目录文件里,目录本质是一张「文件名 → inode 号」的映射表。
ls -i file.txt # 查看文件的 inode 号
df -i # 查看 inode 使用量
stat file.txt # 查看完整元数据这也解释了为什么 mv 同一文件系统内是瞬时操作(只改目录记录),而 cp 要复制数据。
硬链接
硬链接是同一个 inode 的多个文件名。ln target link 创建后,两个文件名指向同一 inode,地位完全对等,没有"原件"与"副本"之分。inode 里有一个链接计数,每删一个文件名计数减一,计数归零且无进程打开时数据块才真正释放。
两条硬性限制,都源于 inode 的机制:
| 限制 | 原因 |
|---|---|
| 不能跨文件系统 | inode 号只在单个文件系统内有意义,跨分区无法指向 |
| 不能链接目录 | 允许会形成目录环,破坏文件系统树的遍历 |
ln file.txt hard.txt # 创建硬链接
ls -li # 两者 inode 号相同
rm file.txt # 删除一个名字,hard.txt 内容仍在软链接(符号链接)
软链接是一个独立的小文件,内容是目标的路径字符串。访问软链接时内核按路径重新查找目标,因此它可以跨文件系统、可以指向目录,但目标被删除后软链接就成了悬空链接(红色闪烁)。
| 对比项 | 硬链接 | 软链接 |
|---|---|---|
| 本质 | 同一 inode 的别名 | 存路径的独立文件 |
| inode | 与目标相同 | 拥有自己的 inode |
| 跨文件系统 | ❌ | ✅ |
| 指向目录 | ❌ | ✅ |
| 目标删除后 | 仍可访问(计数未归零) | 悬空失效 |
| 权限 | 与目标一致 | 自身权限通常为 lrwxrwxrwx,实际生效的是目标的权限 |
ln -s /etc/nginx/nginx.conf conf # 创建软链接
readlink -f conf # 解析真实路径
ls -l conf # 显示 conf -> /etc/nginx/nginx.conf实际用途
软链接的典型场景:程序版本切换(/usr/bin/python3 -> python3.11)、把配置集中到一处管理(各服务目录软链到统一配置仓库)、兼容旧路径。硬链接现在很少用,Git 等工具内部才依赖它。
inode 耗尽:磁盘有空间却写不进文件
创建任何文件都要消耗一个 inode。inode 用尽时,即使磁盘空间充足,也会报 No space left on device。小文件极多的场景(邮件队列、缓存碎片、海量日志切片)最容易触发。
df -h # 空间没满
df -i # IUse% 100% → inode 耗尽
# 定位小文件最多的目录
for d in /var /tmp /home; do echo "$(find $d -xdev | wc -l) $d"; donedf 与 du 不一致的两种原因
df 读文件系统元数据,du 遍历目录统计。两者不一致通常是:① 文件被删除但仍有进程持有文件句柄,空间未释放(用 lsof +L1 查找,重启对应进程即可释放);② 挂载点遮盖——某分区挂载前写入的数据被挂载动作挡住,du 看不到但占着底层分区。
文件权限同样存储在 inode 中,rwx 与 SUID 等机制见 Linux文件权限。