为什么是 '。' Unix中的硬链接?


51

对于基于Unix的操作系统,为什么一个空目录的链接计数是2而不是1,我已经看到了很多解释。他们都说这是因为'。目录,每个目录都指向其自身。我了解为什么要使用“。”的概念。对于指定相对路径很有用,但是通过在文件系统级别实现它可以得到什么呢?为什么不只具有路径的shell或系统调用知道如何解释它呢?

对我来说,“ ..”是一个真正的链接-文件系统需要存储一个指向父目录的指针才能导航到它。但是我不明白为什么。成为真正的链接是必要的。似乎也导致实现中出现一个丑陋的特殊情况-您可能认为您只能释放链接数小于1的inode所使用的空间,但是如果它们是目录,则实际上需要检查链接数少于2。为什么不一致?


1
一旦有了..硬链接,您的树步软件就已经需要具有“不要跟随父目录链接上的循环”的例外,因此除了链接之外,它几乎没有增加任何复杂性.
dmckee,2011年

Answers:


37

确实,这是一个有趣的问题。乍一看,我看到了以下优点:

首先,您声明将“ .” 解释为当前目录可以由命令行管理程序或系统调用完成。但是,在目录中包含点条目实际上消除了这种必要性,甚至在更低的级别上强制了一致性。

但是我不认为这是此设计决策背后的基本思想。

从目录中创建文件或从目录中删除文件时,该目录的修改时间戳记也必须更新。此时间戳存储在其inode中。索引节点号存储在相应的目录条目中。

如果点条目不存在,则例程将不得不在父目录中此目录的条目处搜索索引节点号,这将再次导致目录搜索。

幸运的是,当前目录中有点条目。在当前目录中添加或删除文件的例程只需跳回到第一个条目(通常是点条目所在的位置),并立即找到当前目录的inode编号。

点条目还有第三点好处:

fsck检查烂文件系统并且必须处理也不在空闲列表中的未连接块时,很容易验证数据块(当解释为目录列表时)是否具有指向inode的点条目依次指向该数据块。如果是这样,则该数据块可以被视为丢失的目录,必须重新连接。


非常有用的答案。
Navaneeth KN11年

6
关于例程搜索目录inode的注释是虚假的。内核例程无需.在当前目录中查找。除非您能找到一个以这种方式实际起作用的内核(我对此表示怀疑...)
Dietrich Epp 2012年

1
我同意@DietrichEpp; 为系统地看目录条目首先,它必须已经知道了索引节点-因为这是它是如何获取到包含目录项的数据块。
Lqueryvg 2014年

10

(嗯:以下内容是史诗般的...)

UNIX文件系统上的目录设计(通常是,但不一定附加到UNIX OS)是一个很好的见解,实际上减少了所需的特殊情况。

“目录”实际上只是文件系统中的一个文件。文件系统中文件的所有实际内容都以inode为单位(从您的问题中,我可以看到您已经知道其中的某些内容)。磁盘上的inode没有结构-它们只是一大堆带编号的字节blob,像花生酱一样散布在磁盘上。这是没有用的,并且确实对那些有整洁思想的人来说是排斥的。

唯一的特殊的inode是inode编号2(不是0或1,为传统的原因); inode 2是一个目录文件:根目录。当系统挂载文件系统时,它“知道”它必须读取dir inode 2才能启动。

目录文件只是具有内部结构的文件,旨在供opendir(3)和朋友读取。您可以在dir(5)中看到其内部结构(取决于您的操作系统);如果您看一下,您会发现目录文件条目几乎不包含有关文件的信息-全部在文件inode中。关于此文件的一些特殊之处之一是,如果您尝试使用允许写入的模式打开目录文件,则open(2)函数将给出错误。其他各种命令(仅举一个例子hexdump)将拒绝以正常方式对目录文件执行操作,只是因为这可能不是您想要执行的操作(但这是它们的特殊情况,而不是文件系统)。

一个硬链接是什么多也小于在一个目录下的文件的映射中的条目。您可以在这样的映射中有两个(或更多)条目,它们都映射到相同的inode编号:因此该inode具有两个(或更多)硬链接。这也解释了为什么每个文件至少具有一个“硬链接”。索引节点有一个引用计数,该引用计数记录该索引节点在文件系统中某个目录文件中被提及的次数(这是您在执行操作时看到的数字ls -l)。

好:我们现在到了重点。

目录文件是字符串(“文件名”)到数字(索引节点号)的映射。这些索引节点号是该目录中“文件”的索引节点号。该目录“中”的文件可能包括其他目录文件,因此它们的inode编号将在该目录中列出的文件中。因此,如果您有一个file /tmp/foo/bar,则目录文件foo包含一个的条目bar,将该字符串映射到该文件的inode。还有在目录文件中的条目/tmp,对目录文件foo是“在”目录/tmp

使用mkdir(2)创建目录时,该函数

  1. 创建具有正确内部结构的目录文件(带有一些inode编号),
  2. 在父目录中添加一个条目,将新目录的名称映射到这个新的inode(占链接之一),
  3. 将一个条目添加到新目录,映射字符串“。”。到相同的inode(这占另一个链接),并且
  4. 在新目录中添加另一个条目,将字符串“ ..”映射到在步骤(2)中修改的目录文件的索引节点(这说明您将在包含子目录的目录文件上看到大量的硬链接)。

最终结果是(几乎)唯一的特殊情况是:

  • open(2)函数试图阻止您打开要写入的目录文件,从而使您难以用脚射击。
  • mkdir(2)函数通过在新目录文件中添加几个额外的条目(“。”和“ ..”)使事情变得轻松便捷,这仅仅是为了方便在文件系统中移动。我怀疑没有“。”的文件系统是否可以很好地工作。和“ ..”,但使用起来很麻烦。
  • 目录文件是标记为“特殊”的几种文件类型之一-这实际上是告诉open(2)这样的行为略有不同的原因。参见st_modestat(2)。

(复制自stackoverflow原始问题,2011-10-20)


1
您将块与inode混淆了。作为一种特殊情况,对于短文件,文件内容可能位于inode内,但是断言inode是非结构化的是错误的。它们是高度结构化的,包含几乎所有文件元数据,但可以通过其找到文件的文件名除外。索引节点包含指向磁盘上文件内容所在的块的指针(直接,间接,双向间接等)。
2012年

1
不,我不会将块与inode混淆。索引节点是位于块上方的抽象,本文的目的是描述文件和目录及其内容之间的关系:所有文件系统结构都来自目录文件。它已经足够长,而且不会陷入inode实现中!(也就是说,我可能会更清楚地写出前几段)。另外,正如您所看到的,我确实明确声明有关文件的所有信息(文件名除外)都位于inode中,而不是目录文件中。
诺曼·格雷

@NormanGray:即使捍卫自己,也要用脚射击自己。您说:“文件系统中文件的所有实际内容都在inode中。...”这是错误的。  文件的属性 / 属性(例如,所有者,权限,修改时间等)存储在inode中。普通文件的内容存储在数据块中。如果您不希望陷入i​​node的实现中,那么也不要这样做,但是也请不要产生误导性的过度简化。
G-Man
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.