inode 是 Linux 操作系统中存储文件时用到的一种数据结构,它包含了与文件系统中各个文件相关的一些重要信息。

一、几个概念

1.扇区(Sector)

硬盘的最小存储单元叫做扇区(Sector),每个扇区大小为 512 字节(相当于 0.5KB)。

Linux 查看扇区大小:fdisk -l /dev/sda

$ sudo fdisk -l /dev/nvme0n1
Disk /dev/nvme0n1: 953.87 GiB, 1024209543168 bytes, 2000409264 sectors
Disk model: YMTC PC300-1TB-B                        
Units: sectors of 1 * 512 = 512 bytes        # 1 个扇区大小为 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 9F0C9D9F-7225-4652-A3F7-C4E0312F138D

Device              Start        End   Sectors   Size Type
/dev/nvme0n1p1       2048    2000895   1998848   976M EFI System
/dev/nvme0n1p2    2000896   64501759  62500864  29.8G Linux swap
/dev/nvme0n1p3   64501760  357470207 292968448 139.7G Linux filesystem
/dev/nvme0n1p4  357470208  357502975     32768    16M Microsoft reserved
/dev/nvme0n1p5  357502976  650438655 292935680 139.7G Microsoft basic data
/dev/nvme0n1p6  650438656  943407103 292968448 139.7G Linux filesystem
/dev/nvme0n1p7  943407104 1250606322 307199219 146.5G Linux filesystem
/dev/nvme0n1p8 1250607104 1557805055 307197952 146.5G Linux filesystem

2.块(Block)

Windows 文件系统的最小逻辑存储单元叫做簇(Cluster),而Unix/Linux 系统则将最小逻辑存储单位叫做块(Block),每个块或簇可以包括 2、4、8、16、32、64(2 的 n 次方)个相邻的扇区,Linux 文件系统中块的大小一般为 8 个相邻的扇区,也就是 4KB(这个值可以在格式化分区的时候修改)。系统读取数据时是一个块一个块的读取,而不是一个扇区一个扇区的读取。

3.数据区和 Inode 区

Linux 在格式化硬盘时,linux 系统在格式化硬盘时,会将硬盘分区分成 2 个区域。一个是数据区(data area),以块为最小存储单元,用于存放文件数据;另一个是inode 区(inode table,也叫 inode 索引表),以 inode 节点为最小存储单元,用于存放文件存储大小、权限、时间戳、链接数等关键的元信息。

文件名并不属于 inode 信息,因此不会存放到 inode table,而是存放在目录表(见目录树)中

二、文件读取方式

先看一张图:

1.目录树

首先,Linux 文件系统是以目录树的形式来存储和读取的,根节点是”/”,因此所有的文件都必须处于目录内,目录中存放的数据实际上是一个目录表。而目录表中包含 2 个元素,一个是 inode 号码,一个是目录内文件名。

2.inode 表

Inode 表中存储着所有文件的元信息,其中包含 inode 号码,并有一个指针指向文件的数据存储位置,这个数据存储位置就在数据区中。

3.数据区

数据区中存放着文件的真实数据,因为数据区最小存储单元是块,每个块的大小固定(8 个扇区),如 Linux 系统块的大小一般为 4KB,因此一个块只能存放一个文件的内容,无论这个文件有多小,也就是说一个文件占用的空间必然是 1 个或多个块的大小,也就是 4K 的倍数。

先创建几个目录和文件进行演示:

hy@hollowman:~$ mkdir inodes
hy@hollowman:~$ cd inodes/
hy@hollowman:~/inodes$ ls -alih    # 总计有 8KB
总计 8.0K
2650835 drwxr-xr-x  2 hy hy 4.0K 12月25日 12:54 .
2359298 drwxr-xr-x 36 hy hy 4.0K 12月25日 12:54 ..
hy@hollowman:~/inodes$ touch file1
hy@hollowman:~/inodes$ ls -alih    # 创建空文件,数据区没数据,总计还是 8KB
总计 8.0K
2650835 drwxr-xr-x  2 hy hy 4.0K 12月25日 12:54 .
2359298 drwxr-xr-x 36 hy hy 4.0K 12月25日 12:54 ..
2650921 -rw-r--r--  1 hy hy    0 12月25日 12:54 file1
hy@hollowman:~/inodes$ echo 'this is file' > file1
hy@hollowman:~/inodes$ ls -alih    # 写入了数据,有内容,文件大小只有是 13B,但占据了一个块的大小(4KB),总计有 12KB
总计 12K
2650835 drwxr-xr-x  2 hy hy 4.0K 12月25日 12:54 .
2359298 drwxr-xr-x 36 hy hy 4.0K 12月25日 12:54 ..
2650921 -rw-r--r--  1 hy hy   13 12月25日 12:55 file1

三、查看文件的 inode 信息

可通过命令:stat filename来查看文件的 inode 信息

hy@hollowman:~/inodes$ stat file1 
  文件:file1
  大小:13              块:8          IO 块大小:4096   普通文件
  设备:259,3     Inode: 2650921     硬链接:1
权限:(0644/-rw-r--r--)  Uid: ( 1000/      hy)   Gid: ( 1000/      hy)
访问时间:2024-12-25 12:54:58.699715938 +0800
修改时间:2024-12-25 12:55:35.854387988 +0800
变更时间:2024-12-25 12:55:35.854387988 +0800
创建时间:2024-12-25 12:54:58.699715938 +0800

打印出的信息中,除了文件名不属于 inode,其他都是 inode 中包含的元信息。下面为各个元信息的说明:

文件大小(size): 13 字节,这是文件的实际大小
分配的区块数(Blocks): 8,也就是分配 1 个块=连续的 8 个扇区,这是文件实际占用空间大小:8 * 512 bytes = 4KB
IO 块(IO Block): 4096,表示该文件系统的最小寻址单元,对应 Blocks 也就是 4096 bytes(4KB)
文件类型: 普通文件 regular file
所在设备(device): 259,3
Inode 号: 2650921
硬链接数(Links): 1
权限(Access): 0664/-rw-rw-r--,文件的读取权限
Uid: 1000/hy,文件的属主为 hy 用户
Gid: 1000/hy,文件的属组为 hy 用户组
最近访问时间(access time): 2022-04-24 08:30:10.001781375 +0800
最近修改时间(modify time): 指的文件内容变动时间
最近变更时间(change time): 指的 inode 变动时间
创建时间(birth): 文件创建时间

在 Linux 系统中仅仅通过 inode 号码来标识不同文件,而文件名只是文件的一个别称,在 Linux 系统中还可以通过硬链接的方式用不同文件名指向同一个 inode 号码,这样两个文件名的数据完全相同(见下方的硬链接)。

四、inode 的大小

既然在格式化硬盘的时候,系统会将硬盘分成两个区域:数据区(data area)和 inode 区(inode table),那 inode 也是需要占用硬盘空间的,数据区和 inode 区中任意一个区存储满了,硬盘也就无法再存储文件了,哪怕另一个区中还有空间。

每个 inode 的大小,一般是 128 字节或 256 字节。而 inode 的总数则在格式化时就会给定,一般每 1KB 或每 2KB 或 4KB 设置一个 inode。例如一块硬盘分区时,每分配 4KB 给 data area,就分配一个 inode(假设每个 inode 大小为 128b),那么 inode table 与 data area 的比例就是 128:4096=1:32。

可以用df -i命令查看硬盘分区的 inode 总数和已经使用的数量

hy@hollowman:~/inodes$ df -i /
文件系统         Inodes   已用I    可用I  已用I%  挂载点
/dev/nvme0n1p3  9158656  373253  8785403      5%  /

五、硬链接

用户找到和打开文件的步骤:

  • 找到这个文件名对应的 inode 号码(目录表)
  • 通过 inode 号码获取 inode 信息(inode table)
  • 根据 inode 信息找到文件数据所在的 block 并读取数据(data area)

虽然文件读取都是通过 inode 号码,但我们日常却是对文件名来操作的,对文件名的操作则用到了硬链接。

硬链接是文件名直接指向 inode 号码,一个 inode 号码可以对应多个硬链接,创建文件的过程其实也是一个硬链接的过程,刚创建的文件硬链接数量是 1。

创建硬链接用ln命令

hy@hollowman:~/inodes$ ls -al                           # file1 硬链接为 1 (ls 显示的第二列为硬链接数)
总计 12
drwxr-xr-x  2 hy hy 4096 12月25日 13:51 .
drwxr-xr-x 36 hy hy 4096 12月25日 13:40 ..
-rw-r--r--  1 hy hy   13 12月25日 12:55 file1
hy@hollowman:~/inodes$ ln file1 file_back               # 创建硬链接
hy@hollowman:~/inodes$ ls -al                           # file1 及 file_back 的硬链接均为 2,
总计 16
drwxr-xr-x  2 hy hy 4096 12月25日 13:51 .
drwxr-xr-x 36 hy hy 4096 12月25日 13:40 ..
-rw-r--r--  2 hy hy   13 12月25日 12:55 file1
-rw-r--r--  2 hy hy   13 12月25日 12:55 file_back
hy@hollowman:~/inodes$ echo 'second line' >> file_back  # 给 file_back 追加一行,改变了数据区内容
hy@hollowman:~/inodes$ diff file1 file_back -s          # file1 及 file_back 的文件内容完全一样
文件 file1 和 file_back 相同
hy@hollowman:~/inodes$ ls -ali                          # file1 及 file_back 的文件的inodes 相同 
总计 16
2650835 drwxr-xr-x  2 hy hy 4096 12月25日 13:51 .
2359298 drwxr-xr-x 36 hy hy 4096 12月25日 13:40 ..
2650921 -rw-r--r--  2 hy hy   25 12月25日 13:52 file1
2650921 -rw-r--r--  2 hy hy   25 12月25日 13:52 file_back

六、软链接

除了硬链接,还有一个软链接,软链接也是符号链接的意思,其文件名直接指向的是软链接文件名。软链接创建了一个新的文件,有自己的 inode 号码。
创建软链接用ln -s命令

hy@hollowman:~/inodes$ ln -s file1 file_back_s             # 创建软链接
hy@hollowman:~/inodes$ ls -ali                             # file_back_s 的大小、inode 号码及硬链接数都与 file1 不同
总计 16
2650835 drwxr-xr-x  2 hy hy 4096 12月25日 14:02 .
2359298 drwxr-xr-x 36 hy hy 4096 12月25日 13:40 ..
2650921 -rw-r--r--  2 hy hy   25 12月25日 13:52 file1
2650921 -rw-r--r--  2 hy hy   25 12月25日 13:52 file_back
2650928 lrwxrwxrwx  1 hy hy    5 12月25日 14:02 file_back_s -> file1
hy@hollowman:~/inodes$ echo 'three lines' >> file_back_s   # 追加内容到软链接文件 file_back_s
hy@hollowman:~/inodes$ ls -ali                             # 改变的是 file1 及 file_back 的大小
总计 16
2650835 drwxr-xr-x  2 hy hy 4096 12月25日 14:06 .
2359298 drwxr-xr-x 36 hy hy 4096 12月25日 14:06 ..
2650921 -rw-r--r--  2 hy hy   37 12月25日 14:06 file1
2650921 -rw-r--r--  2 hy hy   37 12月25日 14:06 file_back
2650928 lrwxrwxrwx  1 hy hy    5 12月25日 14:02 file_back_s -> file1

七、目录的创建

linux 系统一切皆文件,目录也是一个文件,只不过目录是一个特殊的文件,创建目录的同时,会同时创建 1 个当前目录以及 1 个父目录的硬链接。

hy@hollowman:~/inodes$ mkdir childir      # 创建目录 childir
hy@hollowman:~/inodes$ ls -al             # childir 目录的硬链接为 2,当前目录的硬链接数也 +1 了
总计 20
drwxr-xr-x  3 hy hy 4096 12月25日 14:10 .
drwxr-xr-x 36 hy hy 4096 12月25日 14:06 ..
drwxr-xr-x  2 hy hy 4096 12月25日 14:10 childir
-rw-r--r--  2 hy hy   37 12月25日 14:06 file1
-rw-r--r--  2 hy hy   37 12月25日 14:06 file_back
lrwxrwxrwx  1 hy hy    5 12月25日 14:02 file_back_s -> file1
hy@hollowman:~/inodes$ ls -ali childir/    # childir 目录下还存在当前目录(.)和当前目录的父目录(..)这2个硬链接
总计 8
2650922 drwxr-xr-x 2 hy hy 4096 12月25日 14:10 .
2650835 drwxr-xr-x 3 hy hy 4096 12月25日 14:10 ..
因此,目录的硬链接数 = 1(本身) + 1(目录内自动创建的硬链接) + 子目录数(每个子目录内会创建其父目录硬链接) = 子目录数 + 2