为什么目录的大小报告与其他文件不同?

Utk*_*tku 8 ls filesystems files

我想知道为什么一个空目录占用了 4096 字节的空间,我看到了这个问题。据说空间是按块分配的,因此新目录的大小是 4096 字节。

但是我很确定“普通”文件的分配也是分块完成的。至少在Windows 文件系统中是这样,我猜测它至少在 ext* 中必须相似。

现在据我所知,其他类型文件的大小列表,例如文件、符号链接等,都是根据实际大小完成的。因为当我创建一个空文件时,我看到的大小为 0。当键入几个字符时,我将 <字符数> 字节视为大小等。

所以我的问题是,虽然其他文件的分配也是分块完成的,为什么报告目录和文件大小的策略不同?

澄清

我认为这个问题已经足够清楚了,但显然不是。我将尝试在这里澄清这个问题。

1)我认为一个目录是:

我将尝试通过以下示例来解释我认为目录是什么。看完后,如果有不对的地方请告知。

假设我们有一个名为mydir. 假设它包含 3 个文件,分别是:f0,f1f2. 假设每个文件的长度为 1 个字节。

现在,什么是mydir?它是一个指向包含以下内容的 inode 的指针:字符串“f0”和f0指向的 inode 编号。字符串“f1”和f1指向的 inode 编号。和字符串“f2”和f2指向的inode编号。(至少这是我认为的目录。如果我错了,请纠正我。)

现在可能有两种计算目录大小的方法:

1)计算mydir指向的inode的大小。

2) 将内容mydir指向的 inode 的大小相加。

尽管 1 更违反直觉,但我们假设它是正在使用的方法。(对于这个问题,哪个方法是实际使用的方法并不重要。)然后,mydir计算的大小如下:

2 + 2 + 2 + 3 * <space_required_to_store_an_inode_number>
Run Code Online (Sandbox Code Playgroud)

2 是因为每个文件名的长度为 2 个字节。

2)问题:

现在的问题是:假设我认为目录是正确的,报告的大小mydir应该远小于 4096,无论使用方法 1 还是方法 2 来计算其大小。

现在,你会说它报告 4096 字节的原因是因为分配是在块中完成的。因此,报告的大小那么大。

但是我会说:对于常规文件,分配也是以块为单位完成的。(请参阅thrig 的回答以供参考)但是,它们的尺寸以实际尺寸报告。(如果它们包含 1 个字符,则为 1 个字节,如果它们包含 2 个字符,则为 2 个字节等)

所以我的问题是,为什么报告目录大小的策略与报告常规文件大小的策略如此不同?

更多说明:

我们知道,为非空文件和空目录分配的初始块数都是 8 个块。(请参阅thrig 的回答)因此,即使为常规文件和目录分配了相同数量的块,为什么报告的目录大小要大得多?

mad*_*lao 12

我认为您感到困惑的原因是因为您不知道目录什么。为此,让我们退后一步,检查 Unix 文件系统是如何工作的。

Unix 文件系统对磁盘上的数据寻址有几个不同的概念:

  • 数据块是磁盘上具有文件内容的一组块。
  • inode是文件系统上的特殊块,在该文件系统中具有唯一的数字地址,其中包含有关文件的元数据,例如:
    • 权限
    • 访问/修改时间
    • 尺寸
    • 指向数据块的指针(可以是块列表、范围等)
  • 文件名是映射到 inode 的文件系统根上的分层位置。

换句话说,一个“文件”实际上由三个不同的东西组成:

  1. 文件系统中的 PATH
  2. 带有元数据的 inode
  3. inode指向的数据块

大多数时候,用户将文件想象为“与文件名关联的实体”的同义词——只有当您处理低级实体或文件/套接字 API 时,您才会想到 inode 或数据块。目录是那些低级实体之一。

您可能认为目录是包含一堆其他文件的文件。这只是对了一半。目录是将文件名映射到 inode 编号的文件。它不“包含”文件,而是指向文件名的指针。把它想象成一个包含如下条目的文本文件:

  • . - 索引节点 1234
  • .. - 索引节点 200
  • 文档 - inode 2008
  • README.txt - inode 2009

上面的条目称为目录条目。它们基本上是从文件名到 inode 编号的映射。目录是包含目录条目的特殊文件。

这当然是一种简化,但它解释了基本思想和其他目录怪异之处。

  • 为什么目录不知道自己的大小?
    • 因为它们只包含指向其他东西的指针,你必须遍历它们的内容才能找到大小
  • 为什么目录永远不是空的?
    • 因为它们至少包含 . 和...条目。因此,一个合适的目录将至少与可以包含这些条目的最小文件大小一样小。在大多数文件系统中,4096 字节是最小的。
  • 为什么重命名文件时需要对父目录的写权限?
    • 因为您不仅要更改文件,还要更改指向该文件的目录条目。
  • 为什么 ls 会显示一个目录的奇怪数量的“链接”?
    • 一个目录可以被它本身、它的父目录、它的子目录引用(链接到)。
  • 硬链接有什么作用,它与符号链接有何不同?
    • 硬链接添加指向相同 inode 编号的目录条目。因为它指向一个 inode 编号,所以它只能指向同一文件系统中的文件(inode 是文件系统的本地节点)
    • 符号链接添加了一个指向单独文件名的新 inode。因为它引用了一个文件名,所以它可以指向树中的任意文件。

可是等等!奇怪的事情正在发生!

ls -ld somedirectory始终显示文件大小为 4096,而ls -l somefile显示文件的实际大小。为什么?

混淆点1:当我们说“尺寸”时,我们可以指两件事:

  • 文件大小,这是一个存储在 inode 中的数字;和
  • 分配的大小,即与 inode 关联的块数乘以每个块的大小。

一般来说,这些不是同一个数字。尝试stat常规文件上运行,您会看到这种差异。

当文件系统创建一个非空文件时,它通常会急切地按组分配数据块。这是因为文件有任意快速增长和收缩的趋势。如果文件系统只根据需要分配尽可能多的数据块来表示文件,那么增长/收缩会更慢,碎片将是一个严重的问题。因此,在实践中,文件系统不必为小的更改不断重新分配空间。这意味着磁盘上可能有很多空间被文件“占用”但完全未使用。

文件系统如何处理所有这些未使用的空间?没有。直到感觉需要为止。如果你的文件系统优化器工具——可能是一个在后台运行的在线优化器,可能是你的 fsck 的一部分,可能是你的文件系统本身内置的——感觉像它,它可能会重新分配你的文件的数据块——移动使用过的块,释放未使用的块块等。

所以现在我们来看看常规文件和目录之间的区别:因为目录构成了文件系统的“骨干”,您希望它们可能需要经常访问或修改,因此应该进行优化。所以你根本不希望它们支离破碎。创建目录时,它们总是所有数据块的大小最大化,即使它们只有这么多目录条目。这对于目录来说是可以的,因为与文件不同,目录的大小和增长率通常是有限的。

4096 报告的目录大小是存储在目录 inode 中的“文件大小”数字,而不是目录中的条目数。它不是一个固定的数字——它是适合目录分配的块数的最大字节数。通常,这是为具有任何内容的文件分配的 512 字节/块乘以 8 个块 - 顺便说一句,对于目录,文件大小和分配的大小是相同的。因为它是作为单个组分配的,所以文件系统优化器不会四处移动它的块。

随着目录的增长,更多的数据块被分配给它,并且它也会通过相应地调整文件大小来最大化这些块。

因此lsstat将显示目录 inode 的文件大小字段,该字段设置为分配给它的数据块的大小。