Linux中inodes简介
1. 简介
我们通常将文件视为具有内容的对象。然而,我们日常接触最多的只是链接。在下面,文件对象实际上是 inode。
**在本教程中,我们将讨论 Linux inode——**它们是什么,以及为什么以及何时使用它们。我们从存储的概念开始。之后,我们讨论文件系统如何应用于存储设备并检查它们的粗略内部工作原理。接下来以inode为中心进行综合讨论。最后,我们探索了一些基于 inode 的文件系统对象,并记录了一些特殊的注意事项。
我们使用 GNU Bash 5.1.4 在 Debian 11 (Bullseye) 上测试了本教程中的代码。它是 POSIX 兼容的,应该可以在任何这样的环境中工作。
2. 储存
大多数机器至少使用主内存和辅助内存。我们最常将主内存称为随机存取存储器 (RAM),而辅助内存或存储器由硬盘、固态驱动器和类似设备组成。
不管它们是什么类型,存储设备通常都有一个控制器。它负责将相应内存模块的接口暴露给机器。控制器将设备呈现为相同大小的块。
我们应该注意到,辅助存储器的一个扇区或最小块最常见的是 512 字节大。这在以后很重要。
为了更好地利用存储设备,操作系统 (OS) 具有与控制器通信的驱动程序。然而,驱动程序仍然呈现出非常原始的内存结构。反过来,该结构通过另一个抽象进一步完善以供使用。
3.文件系统
驱动程序是操作系统查看和控制存储的方式,而文件系统 是它们排序的方式。为此,文件系统通常使用我们可以在书中找到的旧机制——索引。
3.1. 元数据组织
考虑如何列出一本书的章节以及页码和标题等重要属性。所有这些信息都在索引中。另一方面,索引本身整齐地排列在一端,只占本书的一小部分,但它允许快速、轻松地浏览。
即使手头没有所有页面,使用索引,我们也可以了解:
- 这本书有多长
- 哪些部分短,哪些部分长
- 部分是如何排序的
- 哪些部分是哪些其他部分的子部分
- 部分的基本特征(例如标题)
现在想象一下这本书的某些部分是空的。由此产生的结构通常是文件系统的最基本概念。
特别是,索引表示与文件系统关联的*元数据。空闲或分配给文件的部分也称为块,代表内存的物理部分*。文件系统索引将它们组织起来以便快速轻松地浏览,就像书中的页面一样。
3.2. 例子
事实上,大多数文件系统都有某种支持组织,它指向块:
- FAT(文件分配表)有一个文件分配表(因此得名FAT)
- exFAT(扩展文件分配表)基本上扩展了文件分配表
- NTFS(新技术文件系统)使用更高级的主文件表
Linux 支持许多文件系统,但只有其中一些是原生的。一个值得注意的例子是ext (扩展文件系统)系列系统:ext2、ext3 和 ext4。另一个很好的例子是XFS (X 文件系统)。从现在开始,我们将只讨论原生 Linux 文件系统的工作原理,因为它们的广泛使用和它们共享的概念——元数据的关键。
让我们探讨一下这个概念。
4. 索引节点
术语索引 节点来自索引节点。因为我们已经讨论过文件系统组织就像书中的索引,所以我们可以看到 inode 如何很好地适应这个想法。
特别地,我们可以将 inode 等同于 book 中索引条目的行号。这样,inode 允许我们索引主索引。操作系统为每个 inode 分配一个唯一的整数,因此它们可以用作我们已经讨论过的内部文件系统结构的键。
要获取 inode 指向给定文件的大部分信息,我们可以使用stat 命令,它在内部执行syscalls :
$ 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) 的设备标识。当然,我们看到了 inode 号(666)。第四行的最后是这个 inode 的链接数。我们将在下一节讨论链接。
有一整行专门用于所有权和访问权 。最后,我们看到四行专门用于访问、修改、更改和创建(出生)日期。请注意,更改文件意味着修改其元数据。
事实上,许多文件系统对象都有自己独特的索引节点。事实上,不管它们的具体性质如何,它们都是文件。
5. 文件系统对象
到目前为止,我们讨论的是文件系统元数据。但是,所有文件系统的存在都是为了组织我们存储的实际数据。对我们来说,元数据大部分是隐藏的。可见的是文件系统对象。
大多数文件系统中的主要此类对象是文件。在本机 Linux 文件系统中,术语文件表示具有关联 inode 的任何对象。它们可以有许多不同的类型:
让我们探讨一下常见的。
5.1. 普通文件
常规文件是我们通常与单词文件相关联的数据集合。除了 inode 的元数据外,常规文件还有非系统内容,我们通过编辑器和其他工具读取和写入这些内容。
重要的是,我们可以看到文件的内容是如何通过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 目录是文件名到索引节点号映射的列表。用户以树的形式看到这个结构。
事实上,我们可以使用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 文件系统中,每个目录都有以下唯一的关联信息:
请注意,最后一点是目录块包含的内容:包含文件名及其关联的 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
正如我们在上面可以确认的那样,文件名不是 inode 数据的一部分,尽管为了清楚起见, stat命令确实添加了它。
我们可以通过链接制作更复杂的树结构。
5.3. 关联
有时我们可能希望一个文件系统对象同时存在于多个地方而不实际复制数据。在这些情况下,我们使用链接 。
事实上,我们可以把链接想象成另一个对象的快捷方式。有两种类型的链接:硬链接和软链接(符号链接)。
软链接通过其当前名称直接指向原始对象,而硬链接与其 inode 相连,只是同一文件的另一个名称。这是我们之前关于文件名存在位置的声明的一个例外——符号链接也会保留它们。实际上,一旦对象的所有硬链接都被删除,对象就会消失,因为符号链接总是指向一个硬链接。有趣的是,符号链接有自己的特殊索引节点。
此外,我们不仅可以创建文件链接,还可以链接到目录 。这样做可以从目录树中生成复杂的循环结构。
此外,inode 机制会导致一些特定的行为,我们将在接下来讨论。
6. inode 文件系统细节
当使用基于 inode 的文件系统时,我们有时不得不考虑它的内部工作原理。让我们看一些特殊情况。
6.1. 最大 inode
文件系统有可能用完 inode,尽管有可用空间。这通常发生在许多小文件不占用太多存储空间但从总 inode 计数中扣除的情况下。
实际上,我们可以通过df (Disk Free)命令查看 inode 总数信息:
$ df -ih
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda 16M 106K 16M 1% /
上面的输出表明我们已经使用了 1600 万个可能的 inode 中的 106000 个。当然,我们可以释放 inode 以获得更多可用空间。
6.2. 重新分配
当我们删除一个 inode 时,文件系统可以在将来为另一个文件分配它。但是,按名称删除文件与删除 inode 不同。前者只删除一个指针(硬链接或软链接),而后者会破坏 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命令的信息相同,但还是有一些不同。
其中之一是代数。它可以区分两个不同操作系统(例如,客户端和服务器)看到的 inode 号。我们在这些差异有意义的特定场景中使用它。
另一组是片段字段。它们显示块何时使用现已过时的块碎片功能。
我们看到的最后一个字段是 EXTENTS。当我们启用一个功能时,它就在那里,它允许文件系统存储连续的块,只有序列中第一个和最后一个块的标识符。