我在教科书中读到,Unix/Linux 不允许目录的硬链接,但允许软链接。是因为,当我们有循环时,如果我们创建硬链接,并且在一段时间后我们删除原始文件,它会指向一些垃圾值?
如果循环是不允许硬链接的唯一原因,那么为什么允许到目录的软链接呢?
答案1
这只是一个坏主意,因为无法区分硬链接和原始名称。
允许目录的硬链接会破坏文件系统的有向非循环图结构,可能会创建目录循环和悬空目录子树,这将使fsck
任何其他文件树遍历器容易出错。
首先,为了理解这一点,我们来谈谈索引节点。文件系统中的数据保存在磁盘上的块中,这些块由索引节点收集在一起。您可以将索引节点视为文件。不过,索引节点缺少文件名。这就是链接发挥作用的地方。
链接只是指向 inode 的指针。目录是保存链接的索引节点。目录中的每个文件名只是一个索引节点的链接。在 Unix 中打开文件也会创建链接,但它是不同类型的链接(不是命名链接)。
硬链接只是指向该 inode 的额外目录条目。当您 时ls -l
,权限后面的数字是指定链接计数。大多数常规文件都有一个链接。创建一个新的文件硬链接将使两个文件名指向同一个 inode。笔记:
% ls -l test
ls: test: No such file or directory
% touch test
% ls -l test
-rw-r--r-- 1 danny staff 0 Oct 13 17:58 test
% ln test test2
% ls -l test*
-rw-r--r-- 2 danny staff 0 Oct 13 17:58 test
-rw-r--r-- 2 danny staff 0 Oct 13 17:58 test2
% touch test3
% ls -l test*
-rw-r--r-- 2 danny staff 0 Oct 13 17:58 test
-rw-r--r-- 2 danny staff 0 Oct 13 17:58 test2
-rw-r--r-- 1 danny staff 0 Oct 13 17:59 test3
^
^ this is the link count
现在,您可以清楚地看到不存在硬链接之类的东西。硬链接与常规名称相同。在上面的例子中,test
or test2
,哪个是原始文件,哪个是硬链接?到最后,你无法真正分辨出来(即使通过时间戳),因为两个名称都指向相同的内容,相同的 inode:
% ls -li test*
14445750 -rw-r--r-- 2 danny staff 0 Oct 13 17:58 test
14445750 -rw-r--r-- 2 danny staff 0 Oct 13 17:58 test2
14445892 -rw-r--r-- 1 danny staff 0 Oct 13 17:59 test3
该-i
标志ls
显示行开头的 inode 编号。请注意test
和test2
具有相同的索引节点号,但test3
具有不同的索引节点号。
现在,如果允许您对目录执行此操作,则文件系统中不同点的两个不同目录可能指向同一事物。事实上,子目录可以指向它的祖目录,从而创建一个循环。
为什么这个循环值得关注?因为当您遍历时,无法检测到您正在循环(在遍历时不跟踪 inode 编号)。想象一下,您正在编写du
命令,该命令需要递归子目录以了解磁盘使用情况。如何du
知道它何时进入循环?du
为了完成这个简单的任务,它很容易出错,并且需要做大量的簿记工作。
符号链接是一种完全不同的野兽,因为它们是许多文件文件系统 API 倾向于自动遵循的特殊类型的“文件”。请注意,符号链接可以指向不存在的目标,因为它们是按名称指向的,而不是直接指向 inode。这个概念对于硬链接来说没有意义,因为“硬链接”的存在就意味着文件存在。
那么为什么可以du
轻松处理符号链接而不是硬链接呢?我们可以从上面看到硬链接与普通目录条目没有区别。然而,符号链接是特殊的、可检测的且可跳过的!
du
注意到符号链接是一个符号链接,并完全跳过它!
% ls -l
total 4
drwxr-xr-x 3 danny staff 102 Oct 13 18:14 test1/
lrwxr-xr-x 1 danny staff 5 Oct 13 18:13 test2@ -> test1
% du -ah
242M ./test1/bigfile
242M ./test1
4.0K ./test2
242M .
答案2
除了挂载点之外,每个目录都有一个且唯一的父目录:..
。
一种方法是pwd
检查 device:inode 中是否有“.”和 '..'。如果它们相同,则您已到达文件系统的根目录。否则,在父目录中查找当前目录的名称,将其压入堆栈,然后开始比较“../.”先是“../..”,然后是“../../.”与“../../..”等。一旦找到根,就开始从堆栈中弹出并打印名称。该算法依赖于这样一个事实:每个目录都有且只有一个父目录。
如果允许到目录的硬链接,那么多个父目录应该..
指向哪一个?这是不允许目录硬链接的一个令人信服的原因。
目录的符号链接不会导致该问题。如果程序愿意,它可以lstat()
对路径名的每个部分执行 an 操作并检测何时遇到符号链接。该pwd
算法将返回目标目录的真实绝对路径名。事实上,某处有一段文本(符号链接)指向目标目录,这一事实几乎是无关紧要的。这种符号链接的存在不会在图中创建循环。
答案3
我想就这个问题补充几点。 Linux 中允许目录的硬链接,但方式受到限制。
我们测试这一点的一种方法是,当我们列出目录的内容时,我们会发现两个特殊目录“.”。和 ”..”。据我们所知 ”。”指向同一目录,“..”指向父目录。
因此,让我们创建一个目录树,其中“a”是父目录,目录“b”作为其子目录。
a
`-- b
记下目录“a”的索引节点。当我们从目录“a”执行操作时,ls -la
我们可以看到“.”。目录也指向同一个 inode。
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 a
在这里我们可以发现目录“a”有三个硬链接。这是因为 inode 797358 有 3 个名为“.”的硬链接。在“a”目录中,名称为“..” 在目录“b”中,其名称为“a”。
$ ls -ali a/
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 .
$ ls -ali a/b/
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 ..
所以在这里我们可以理解,硬链接只是用于目录与其父目录和子目录的连接。因此,没有子目录的目录将仅具有 2 个硬链接,因此目录“b”将仅具有两个硬链接。
阻止自由目录硬链接的原因之一是避免无限引用循环,这会使遍历文件系统的程序感到困惑。
由于文件系统被组织为树并且树不能具有循环引用,因此应该避免这种情况。
答案4
目录上的硬链接创建将是不可恢复的。假设我们有:
/dir1
├──this.txt
├──directory
│ └──subfiles
└──etc
我将它硬链接到/dir2
.
所以/dir2
现在还包含所有这些文件和目录
如果我改变主意怎么办?我不能只是rmdir /dir2
(因为它不是空的)
如果我递归地删除/dir2
...,它也会被删除/dir1
!
恕我直言,这在很大程度上是避免这种情况的充分理由!
编辑 :
评论建议通过对目录执行操作来删除该目录rm
。但rm
在非空目录上会失败,并且无论该目录是否硬链接,此行为都必须保留。所以你不能只是rm
取消链接。它需要一个新的参数rm
,只是说“如果目录 inode 的引用计数 > 1,则仅取消链接目录”。
这反过来又打破了另一个最不令人惊讶的原则:这意味着删除我刚刚创建的目录硬链接与删除普通文件硬链接不同......
我将重新表述我的句子:如果没有进一步的开发,硬链接创建将是不可逆转的(因为当前命令无法在不与当前行为不一致的情况下处理删除)
如果我们允许更多的开发来处理这个案例,陷阱的数量和数据丢失的风险如果您对系统的工作原理不够了解,那么这样的发展意味着,恕我直言,这是限制目录硬链接的充分理由。