| 核心结论 | 细节 |
|---|---|
目录软链接删除区分带 / 与不带 / | rm <软链接>/ 触发目标目录的删除逻辑,rm <软链接> 只删除软链接文件本身 |
| 硬链接无法跨分区 + 不能对目录创建 | 跨分区不共享 inode table(机制决定);目录硬链接可能破坏树形结构(硬性规定),但 . 和 .. 本身就是目录的硬链接 |
| 硬链接共用同一个 inode | 删除硬链接只是 link count -1,直到 link count = 0 文件数据才被回收 |
| 软链接靠路径字符串查找目标,非 inode | 相对路径的软链接不能随意移动,绝对路径可以 |
1.简介
某次重装软件时,对软链接理解存在偏差导致误删目录。问题复现:
- 因为未移除干净软链接,导致安装失败;
- 尝试删除软链接
/root/bin -> /bin; - 执行
rm /root/bin/,报错Is a directory; - 想当然执行
rm -rf /root/bin/,导致其 link 的目录/bin被删除。
目录软链接的正确删除方式应为
rm /root/bin,而非rm -rf /root/bin/。见 §2.2.5 详细说明
本文由这次踩坑引出对链接相关概念的梳理。
2.说明
2.0 前置概念:inode 与文件系统
inode(索引节点):注1
- 每个文件占用一个 inode,inode 指向 block 获取文件内容;
- 跨文件系统(磁盘分区)时 inode 不唯一;
- inode 与文件名为一对多关系——多个文件名可以指向同一个 inode。
系统读取文件的步骤:注1
- 根据文件名找到对应的 inode 编号
- 通过 inode 编号获取 inode 信息
- 根据 inode 信息找到文件数据所在的 block,读出数据
链接:UNIX 文件系统提供的一种将不同文件名链接至同一个文件的机制,是 POSIX 概念,主流文件系统都支持。
查看 inode / 链接信息的命令(ls -li、stat)
| |
要点:
- 第一列:inode 号
- 第三列:硬链接数(多少个文件名指向这个 inode)
- 第一字符:
-普通文件、l软链接、d目录
| |
stat 直接显示文件类型(symbolic link)、inode、链接数。
2.1 软链接 vs 硬链接
| 功能项 | 软链接 | 硬链接 |
|---|---|---|
| 使用对象 | 文件、目录 | 仅文件 |
| inode 是否相同 | 不同 | 相同 |
| 跨文件系统 | 可以 | 不可以 |
| 原文件删除后 | 软链接不可用 | 硬链接不受影响 |
| 关联方式 | 记录原文件路径 | 共用同一个 inode |
| 创建命令 | ln -s <src> <file-sl> | ln <src> <file-hl> |
软链接:新建一个独立文件,内容存储的是原文件的路径。大小很小,权限显示 777,但实际权限由原文件决定。
- 删除软链接 → 不影响原文件
- 删除原文件 → 软链接仍存在,但指向失效(
cat提示 “No such file or directory”)
硬链接:为原文件创建一个别名,共享同一个 inode。
- 创建硬链接时 inode 的
link count+1 - 删除硬链接时
link count-1 - 系统调用检查
link count ≥ 1则不回收 inode,文件内容不被删除
2.2 引申问题
2.2.1 软链接跳转靠的是文件名而非 inode
结论: 软链接记录的是原文件的路径,而非 inode。用相对路径创建的软链接,移动到其他目录后会失效——它找的是相对于自身新位置的路径,而非原位置。用绝对路径则不会。
验证实验(5步)
创建软链接时原文件参数用相对路径,再将软链接移动到其他目录,观察是否失效。
准备测试环境
1 2 3mkdir /tmp/ln-test/ && cd /tmp/ln-test/ echo "source file" > test ln -s test test-sl查看状态
1 2 3$ ls -li 60822147 -rw-rw-r-- 1 hex hex 12 Oct 25 17:36 test 60822175 lrwxrwxrwx 1 hex hex 4 Oct 25 17:36 test-sl -> test移动软链接到上级目录
1mv test-sl ../进入上级目录,cat 软链接 → 报错
1 2 3$ cd ../ $ cat test-sl cat: test-sl: No such file or directorystat显示File: test-sl -> test——软链接仍在找当前目录下的test,但那里并没有。在新位置创建同名
test,cat 恢复1 2 3$ echo "Other test file" > test $ cat test-sl Other test file
软链接自身 inode 始终为 60822175,未改变;但前后找到的 test 的 inode 从 60822147 变成了 60822202——软链接依赖的是路径字符串,而非 inode。
2.2.2 硬链接无法跨分区,软链接可以
硬盘格式化时,操作系统将硬盘分为两个区域:
- 数据区:存放文件内容
- inode 区:存放 inode 信息(inode table)
硬链接通过与原文件共用同一个 inode 访问 block 读取文件内容。跨分区无法共享 inode,所以硬链接无法跨分区。软链接记录的是原文件的路径,而非 inode,因此可以跨分区。
2.2.3 硬链接不支持目录的原因
系统限制对目录进行硬链接是硬性规定,并非逻辑上不允许。ln -d 命令允许 root 创建目录硬链接,且 . 与 .. 本身就是目录的硬链接。限制原因有二:
Linux 目录是树状结构,对目录的硬链接可能破坏这种结构,甚至形成循环(如
/usr/bin -> /usr/),使用ls -R等遍历命令时会陷入无限循环。如果对目录做硬链接,链接的数据需要连同被链接目录下所有数据都创建链接——未来在任一目录下创建新文件时,连带另一目录也要同步创建硬链接,复杂度极高。
举例:如果用硬链接把
/etc映射为/etc_hd,则/etc_hd下的所有文件名都要与/etc下的文件名一一创建硬链接。未来在/etc_hd下新增文件时,/etc下也要同步创建硬链接。
2.2.4 . 与 .. — 目录的硬链接
创建目录时,默认生成两个目录项:. 和 ..。
.相当于当前目录的硬链接..相当于父目录的硬链接
硬链接计数规则:
- 空目录的硬链接总数 = 2(目录名自身 +
.) - 有子目录时,硬链接总数 = 2 + 子目录数量(每个子目录中的
..都指向当前目录)
2.2.5 目录软链接删除:软链接/ ≠ 软链接
rm <软链接>删除的是软链接文件本身;rm <软链接/>删除的是原目录的内容。
验证:
| |
| |
关键对比:
| 命令 | 目标 | stat 显示 | inode | 类型 |
|---|---|---|---|---|
stat test-dir-sl | 软链接本身 | symbolic link | 60822339 | 软链接 |
stat test-dir-sl/ | 原目录 | directory | 60822338 | 目录 |
删除验证:
| |
2.2.6 应用场景
软链接:
- 版本切换:
python -> python3.5,升级时只需改软链接目标 - 动态库版本管理:
libfoo.so -> libfoo.so.1.3,升级库文件后更新软链接即可,旧版本保留便于回退 - 路径快捷方式:将深层目录链接到浅层位置
硬链接:
- 同一个文件从不同路径分类索引
- 多人共享同一文件而不占额外空间
- 文件备份——rsync 使用硬链接实现
--link-dest增量备份
3.总结
- 目录软链接删除时注意区分带
/与不带/——rm <软链接>/触发的是目标目录的删除逻辑,rm <软链接>只是删除软链接文件本身; - 硬链接无法跨分区由实现机制决定(跨分区不共享 inode table),无法创建目录硬链接是硬性规定(防止树形目录结构被破坏),但
.和..本身就是目录的硬链接; - 硬链接共用同一个 inode,增加硬链接相当于给文件建一个新别名,删除一个硬链接只是 link count -1,直到 link count = 0 时文件数据才真正被回收;
- 软链接通过原文件路径字符串查找目标,而非 inode——相对路径的软链接不能随意移动到其他目录,绝对路径的可以。
4.参考
[注1]: linux-inode说明
[注2]: “醉卧沙场:计算机专业性文章及回答总索引-存储和文件系统”
[注3]: “鸟哥私房菜-链接ln说明”