Linux inode 索引节点
一、简介
我们通常认为文件是有内容的对象。然而,我们日常处理的大多只是链接。 文件的对象其实是节点。
在本教程中,我们将讨论 Linux inode - 它们是什么,以及为什么和何时使用它们。我们从存储的概念开始。之后,我们将讨论文件系统如何应用于存储设备,并了解其大致的内部工作原理。接下来,我们重点对索引节点进行全面讨论。最后,我们探索一些基于 inode 的文件系统对象并注意一些特殊注意事项。
我们在 Debian 11 (Bullseye) 上使用 GNU Bash 5.1.4 测试了本教程中的代码。它符合 POSIX 标准,应该可以在任何此类环境中工作。
2. 储存
大多数机器至少有主内存和辅助内存。我们通常将主存储器称为随机存取存储器 (RAM),而辅助存储器或存储由硬盘、固态硬盘和类似设备组成。
无论何种类型,存储设备通常都有一个控制器。它负责将相应内存模块的接口暴露给机器。控制器将设备呈现为相同大小的块。
我们应该注意到,辅助内存的扇区或最小块通常为 512 字节大。这在以后很重要。
为了更好地利用存储设备,操作系统(OS)需要使用驱动程序与控制器通信。然而,驱动程序仍然呈现的是非常原始的内存结构。这种结构反过来又通过另一种抽象方法进一步完善,以便使用。
3. 文件系统
驱动程序是操作系统查看和控制存储空间的方式,而文件系统则是操作系统对存储空间进行排序的方式。文件系统通常会使用一种我们在书本中可以找到的古老机制--索引。
3.1.元数据组织
考虑一下一本书的章节是如何列出页码和标题等重要属性的。所有这些信息都在索引中。另一方面,索引本身整齐地排列在一端,只占全书的一小部分,但却可以方便快捷地浏览。
即使手头没有所有的书页,我们也可以通过索引来了解:
- 这本书有多长
- 哪些部分短,哪些部分长
- 章节排序方式
- 哪些章节是哪些其他章节的分节
- 各节的基本特征(如标题)
现在想象一下这本书的某些部分是空的。由此产生的结构通常是文件系统最基本的概念。
其中,索引表示与文件系统关联的元数据。 空闲或分配给文件的部分也称为块,代表内存的物理部分。文件系统索引将这些区块组织起来,就像书中的页面一样,便于快速浏览。
3.2.例子
事实上,大多数文件系统都有某种支持组织,它指向数据块:
- FAT(File Allocation Table)有一个文件分配表(因此得名FAT)
- exFAT(Extended File Allocation Table)基本上扩展了文件分配表
- NTFS(New Technology File System)使用更先进的主文件表
Linux 支持许多文件系统,但只有其中一部分是原生的。一个显著的例子是 ext(Extended Filesystem)系列系统:ext2、ext3 和 ext4。另一个很好的例子是 XFS(X filesystem)。从这里开始,我们将只讨论原生 Linux 文件系统如何工作,因为它们被广泛使用,而且它们有一个共同的概念--元数据的关键。
让我们探讨一下这个概念。
4.索引节点
inode一词来源于index node。既然我们已经说过文件系统组织就像书本中的索引,那么我们就可以知道 inode 是如何很好地融入这一理念的。
特别是,我们可以将 inode 等同于图书中索引条目的行号。这样,inode 就允许我们对主索引进行索引。操作系统会为每个 inode 分配一个唯一的整数,因此它们可以用作我们已经讨论过的文件系统内部结构的键。
要获取一个给定文件的 inode 指向的大部分信息,我们可以使用 stat 命令,它在内部执行 stat 系统调用:
$ stat file.ext
File: file.ext
Size: 166 Blocks: 8 IO Block: 4096 regular file
Device: 810h/2064d Inode: 666 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 1000/ x) Gid: ( 1000/ x)
Access: 2021-11-11 10:00:00.101020201 +0200
Modify: 2021-11-11 10:00:00.101020201 +0200
Change: 2021-11-11 10:00:00.101020201 +0200
Birth: 2021-11-11 10:00:00.101020201 +0200
让我们将这些信息从上到下、从左到右分解为各个组成部分。输出以文件名和大小开始。后者以给定大小(8*512 = 4096 字节)的字节 (166) 和块 (8) 为单位。正如我们已经提到的,物理块通常为 512 字节,但我们可以在使用给定文件系统格式化设备或分区时指定 IO 块大小。该链接不是直接的,但确实存在。在第二行的末尾,我们看到我们在常规文件上使用了stat。
接下来,我们有十六进制 (810) 和十进制 (2064) 的设备标识。当然,我们看到的是索引节点号(666)。第四行最后是指向该 inode 的链接数。我们将在下一节中讨论链接。
有整整一行专门用于所有权和访问权限。最后,我们看到四行专门用于访问、修改、更改和创建(出生)日期。请注意,更改文件意味着修改其元数据。
事实上,许多文件系统对象都有自己独特的索引节点。事实上,无论其具体性质如何,它们都是文件。
5. 文件系统对象
到目前为止,我们讨论了文件系统元数据。然而,所有文件系统的存在都是为了组织我们存储的实际数据。对我们来说,元数据大多是隐藏的。可见的是文件系统对象。
大多数文件系统中的主要对象是文件。 在原生Linux 文件系统中,术语文件表示具有关联 inode 的任何对象。它们可以有许多不同的类型:
让我们探讨一下常见的类型。
5.1.常规文件
常规文件是我们通常与“文件”一词相关联的数据集合。除了索引节点的元数据之外,常规文件还具有非系统内容,我们通过编辑器和其他工具读取和写入这些内容。
重要的是,我们可以通过 debugfs(文件系统调试器)查看文件内容是如何传播的:
$ debugfs /dev/sda
debugfs 1.46.2 (20-Nov-2021)
debugfs: inode_dump -b /file.ext
0000 0af3 0100 0400 0000 0000 0000 0000 0000 ................
0020 0100 0000 0034 1100 0000 0000 0000 0000 .....4..........
0040 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
首先,我们以文件系统块设备作为参数启动 debugfs。之后,我们使用带有 -b 标志的 inode_dump 命令,根据文件的标识符显示文件由哪些块组成。
我们通常将大多数文件存储在目录中。
5.2.目录
目录是文件系统中的容器 - 它们可以存储大多数对象,包括其他目录。 Linux 目录是文件名到 inode 编号映射的列表。用户以树的形式看到这个结构。
事实上,我们可以使用 tree 命令来确认这一点:
$ tree -L 2 -d /etc/systemd/
/etc/systemd/
|-- network
|-- system
| |-- default.target.wants
| |-- getty.target.wants
| |-- multi-user.target.wants
| |-- network-online.target.wants
| |-- remote-fs.target.wants
| |-- sockets.target.wants
| |-- sysinit.target.wants
| `-- timers.target.wants
`-- user
-d 标志仅列出目录,而 -L 后面的数字表示指定路径(最后一个参数)下方的深度级别。
在原生Linux 文件系统中,每个目录都有以下唯一的关联信息:
请注意,最后一点是目录块包含的内容:包含文件名及其关联索引节点号的表。事实上,这是文件系统存储文件名的唯一地方。常规文件索引节点不存储它们的名称,只存储它们的其他元数据:
$ debugfs /dev/sda
debugfs 1.46.2 (20-Nov-2021)
debugfs: stat /file.ext
Inode: 666 Type: regular Mode: 0644 Flags: 0x80000
Generation: 890607921 Version: 0x00000000:00000001
User: 1000 Group: 0 Project: 0 Size: 166
File ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x61a50568:29f85c40 -- Mon Nov 29 18:52:56 2021
atime: 0x61a5055f:8491b840 -- Mon Nov 29 18:52:47 2021
mtime: 0x61a5055f:8491b840 -- Mon Nov 29 18:52:47 2021
crtime: 0x61a5055f:8491b840 -- Mon Nov 29 18:52:47 2021
Size of extra inode fields: 32
Inode checksum: 0x6af32853
EXTENTS:
(0):1064843
正如我们在上面可以确认的,文件名不是 inode 数据的一部分,尽管 stat 命令为了清楚起见添加了它。
我们可以通过链接制作更复杂的树结构。
5.3.链接
有时我们可能希望文件系统对象同时存在于多个位置,而不实际复制数据。在这些情况下,我们使用链接。
事实上,我们可以将链接视为另一个对象的快捷方式。有两种类型的链接:硬链接和软链接(符号链接)。
软链接直接指向原始对象的当前名称,而硬链接则与其 inode 相连,只是同一文件的另一个名称。这与我们之前所说的文件名存在的情况不同,符号链接也会保留文件名。实际上,一旦删除所有硬链接,对象就会消失,因为符号链接始终指向硬链接。有趣的是,符号链接有自己特殊的索引节点。
此外,我们不仅可以创建文件链接,还可以链接到目录。这样做可以使目录树形成复杂的循环结构。
此外,inode 机制可能会导致一些特定的行为,我们将在接下来讨论。
6. inode 文件系统特性
当使用基于 inode 的文件系统时,我们有时必须考虑其内部工作原理。让我们看一些特殊情况。
6.1.最大索引节点数
尽管有可用空间,文件系统也可能会耗尽 inode。当大量小文件不占用太多存储空间,但它们从总 inode 计数中扣除时,通常会发生这种情况。
在实践中,我们可以通过df(Disk Free)命令查看总inode计数信息:
$ df -ih
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda 16M 106K 16M 1% /
上面的输出表明我们已经使用了 1600 万个可能的 inode 中的 106000 个。当然,我们可以释放索引节点以获得更多可用空间。
6.2.重新分配
当我们删除一个索引节点时,文件系统可以在将来将其分配给另一个文件。但是,按名称删除文件与删除 inode 不同。前者仅删除指针(硬链接或软链接),而后者会破坏索引节点表中有关文件的元数据。可以这样想:
- 文件只是链接到 inode 的名称
- inode 由文件描述及其内容块指针组成
- 块保存文件内容
因此,我们可以扫描 inode 表来恢复已删除的文件,但在正常情况下无法对 inode 的元数据执行相同的操作。然而,即使丢失了块指针,在数据被永久重写之前也可以通过扫描和一些检测算法来恢复。
6.3.内联文件
文件系统中有时可用的另一个有用的功能是内联文件。 如果强制 inode 元数据的大小小于 inode 大小,则它们可用:
$ debugfs /dev/sda
debugfs 1.46.2 (20-Nov-2021)
debugfs: stat /file.ext
Inode: 666 Type: regular Mode: 0644 Flags: 0x80000
Generation: 890607921 Version: 0x00000000:00000001
User: 1000 Group: 0 Project: 0 Size: 166
File ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x61a50568:29f85c40 -- Mon Nov 29 18:52:56 2021
atime: 0x61a5055f:8491b840 -- Mon Nov 29 18:52:47 2021
mtime: 0x61a5055f:8491b840 -- Mon Nov 29 18:52:47 2021
crtime: 0x61a5055f:8491b840 -- Mon Nov 29 18:52:47 2021
Size of extra inode fields: 32
Inode checksum: 0x6af32853
EXTENTS:
(0):1064843
额外的空间(上例中的 32 字节)可以包含数据,对于非常小的文件,这意味着我们不需要额外的磁盘空间来存储数据和元数据。该功能是最近才推出的(2016 年 5 月),可能必须通过 e2fsprogs 显式启用。
6.4. stat 和 debugfs stat
虽然 debugfs 中的 stat 中的大部分信息与 stat 命令中的信息相同,但也存在一些差异 。
其中之一是代号。它可以区分两个不同操作系统(例如客户端和服务器)所看到的索引节点号。我们在这些差异有意义的特定场景中使用它。
另一组是片段字段。它们显示了块何时使用现已过时的块碎片功能。
我们看到的最后一个字段是 EXTENTS。当我们启用一项功能时,它就在那里,该功能允许文件系统仅存储序列中第一个和最后一个块的标识符的连续块。
七、总结
在本文中,我们讨论了 inode 及其在文件系统中的主要作用。为此,我们研究了索引节点中的信息至关重要的多个级别。
总之,索引节点是用户看到的文件与系统实际存储的有关这些文件的内容之间的粘合剂。