理解 Linux 中的 inodes

提出此问题的契机是因为我发现zabbix监控项中有一项是free inodes < 20%,所以我就想inodes 的储存空间难道是有固定大小的吗?于是搜索了一下相关文章及视频想要了解一下关于inodes储存空间耗尽的问题。

inodes 对于Linux是一个很重要的概念,它是理解文件系统和磁盘存储的关键,理解了 inodes,常见的文件系统相关的问题就会迎刃而解。

磁盘存储结构

安装操作系统或格式化磁盘分区的时候,操作系统会自动把磁盘分区分为两个区域 :

Block存储区 和 inodes 存储区

Block存储区主要存储文件的内容

inodes存储区是由许多的inode组成的列表,每个 inode 中存储文件元信息(文件大小,创建者,创建时间等)

以下是 磁盘分区中 Block存储区 和 inodes 存储区 图示:

inode 是什么

inode 是一种数据结构(数组),用来存储文件以下的信息:

1、文件大小

2、文件类型(常规文件、目录、软连接等)

3、权限(读写执行权限)

4、属主(所属用户)

5、属组(所属用户组)

6、链接数(有多少个文件名指向这个inode)

7、文件创建时间

8、文件最近访问时间

9、文件最近修改时间

10、文件内容所在Block位置

可以通过以下命令查看文件的 inode 信息

[root@ecs-centos-7 ~]# stat t.txt 

  File: ‘t.txt’

  Size: 70              Blocks: 8          IO Block: 4096   regular file

Device: fd01h/64769d    Inode: 2498227     Links: 1

Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)

Access: 2020-07-19 16:50:14.370189948 +0800

Modify: 2020-07-19 16:50:12.302176601 +0800

Change: 2020-07-19 16:50:12.302176601 +0800

  Birth: -

inode 编号

每一个 inode 都有一个编号,系统根据 inode 编号可以快速的计算出 inode 信息在磁盘 inodes 存储区的偏移,然后从中获取 inode 信息,再根据 inode信息中记录的 Block 块位置,从Block存储区读出文件内容。

inode 编号在一个文件系统中是唯一的,多个文件系统之间可能会出现相同的编号,前面的磁盘存储结构示意图中 /dev/vda1/dev/vda2 在各自的文件系统中 inode 编号是唯一的。

创建一个新文件的时候,文件名和对应的 inode 编号会存储在目录文件的Block块中(关于目录文件后面会讲到)

文件的 inode 信息中记录了文件 Block 块的位置,Block块中存储着文件的内容,可以使用 ls -i 命令查看文件的 inode 编号

[root@ecs-centos-7 ~]# ls -i t.txt 

2498227 t.txt

inode 大小

下面的命令是查看每个 inode 节点的大小, 单位是字节

[root@ecs-centos-7 ~]# dumpe2fs -h /dev/vda1 | grep "Inode size"

dumpe2fs 1.42.9 (28-Dec-2013)

Inode size:               256

从例子中可以看出,每个 inode 节点大小为 256 字节。通过 df -i 命令可以查看每个文件系统中 inode 的使用情况:

[root@ecs-centos-7 ~]# df -i

Filesystem      Inodes  IUsed   IFree IUse% Mounted on

devtmpfs        482393    339  482054    1% /dev

tmpfs           484984      1  484983    1% /dev/shm

tmpfs           484984    433  484551    1% /run

tmpfs           484984     16  484968    1% /sys/fs/cgroup

/dev/vda1      2621440 157202 2464238    6% /

tmpfs           484984      1  484983    1% /run/user/0

字段解释:

Filesystem:文件系统

Inodes: 文件系统中 inodes 总数量

IUsed: inodes 已经使用了的数量

IFree: inodes 可供使用的数量

IUse%: 已经使用了的 inodes 百分比

Mounted on: 文件系统的挂载点

文件系统中的 inodes 数量在安装系统或格式化磁盘分区的时候已经分配好了,也就是说 inodes 数量是有限的,所以 inodes 数量有可能耗尽的,耗尽之后就会出现磁盘还有空间,但是无法创建新文件的情况。

tips:docker的overlay文件系统会创建出许许多多零散的文件,所以这种情况下是有可能会耗尽inodes空间的。

目录文件

Linux 中所有的一切都是文件,包括进程、线程、目录等。每个文件都有对应的 inode 编号

当打开目录时,实际上是打开一个目录文件,目录的存储结构是目录子项列表,每个目录子项由 文件名、文件名对应的inode 编号组成。

上图中是列出了 tmp/ 目录中所有文件,其中第一列是 inode 编号

红框中的 .表示当前目录,它是当前目录的一个硬链接,和当前目录拥有相同的inode编号。我们执行一个文件需要输入类似 ./test 的命令,命令中的 .表示的就是当前目录

红框中的 ..表示当前目录的父目录,它是父目录的一个硬链接,和父目录拥有相同的inode编号。还记得返回上一层目录的命令 cd .. 吗?这里的..表示的就是当前目录的父目录

系统如何查看文件内容

[root@ecs-centos-7 ~]# df -i

Filesystem      Inodes  IUsed   IFree IUse% Mounted on

devtmpfs        482393    339  482054    1% /dev

tmpfs           484984      1  484983    1% /dev/shm

tmpfs           484984    433  484551    1% /run

tmpfs           484984     16  484968    1% /sys/fs/cgroup

/dev/vda1      2621440 157202 2464238    6% /

tmpfs           484984      1  484983    1% /run/user/0

上面的图描述的是查看文件的流程,从图中可以看出 tmp/ 目录文件对应的 Block 块中存储的是该目录下文件名以及文件名对应的 inode 编号,而 a.txt、c.txt 文件对应的 Block 块中存储的是 a.txt 、c.txt 文件的内容。

现以查看 a.txt 文件内容为例来说明在文件系统是如何查看文件内容的:

1、根据 tmp/ 目录文件对应的 inode 编号(131098) 找到 tmp/ 目录文件对应 Block 数据块

2、从 tmp/ 目录文件对应的 Block 块中找出 a.txt 文件对应的 inode 编号(131101)

3、从 a.txt 文件对应的 inode 信息 中找到对应的 Block 块

4、读出 a.txt 文件对应的 Block 块中的数据(this is a.txt file)

通过 inode 查找文件

如果知道一个文件的 inode 编号,就可以使用以下的命令找到这个文件

  [root@ecs-centos-7 ~]# find . -inum 2498227 

  ./t.txt

通过 inode 删除文件

知道了 inode 编号, 用下面的命令可以删除链接到此 inode 的文件

[root@ecs-centos-7 ~]# ls -i ta.txt 

2498227 ta.txt

[root@ecs-centos-7 ~]# find -inum 2498227

./ta.txt

[root@ecs-centos-7 ~]# find -inum 2498227 -delete

[root@ecs-centos-7 ~]# find -inum 2498227

[root@ecs-centos-7 ~]# 

上面的例子中, ta.txt 的 inode 编号是 2498227 ,当删除编号为 2498227 的文件后, ta.txt 文件被删除了

拷贝、重命名、移动文件

下面的命令分别对文件 ha.txt 进行拷贝,移动,重命名操作

[root@ecs-centos-7 ~]# ls -il ha.txt 

2498227 -rw-r--r-- 1 root root 6 Aug  1 01:12 ha.txt

[root@ecs-centos-7 ~]# cp ha.txt ha.txt.bak 

[root@ecs-centos-7 ~]# ls -il ha.txt*

2498227 -rw-r--r-- 1 root root 6 Aug  1 01:12 ha.txt

2498236 -rw-r--r-- 1 root root 6 Aug  1 01:13 ha.txt.bak

[root@ecs-centos-7 ~]# mv ha.txt haa.txt

[root@ecs-centos-7 ~]# ls -il haa.txt 

2498227 -rw-r--r-- 1 root root 6 Aug  1 01:12 haa.txt

[root@ecs-centos-7 ~]# mkdir testdir

[root@ecs-centos-7 ~]# mv haa.txt testdir/

[root@ecs-centos-7 ~]# ls -il testdir/haa.txt 

2498227 -rw-r--r-- 1 root root 6 Aug  1 01:12 testdir/haa.txt

上面的例子中 ha.txt 文件的inode初始编号是 2498227

cp拷贝: ha.txt 拷贝到 testdir/ha.txt 之后, inode 编号变成 2498236

mv重命名: ha.txt 重命名成 haa.txt,haa.txt文件的 inode 编号和重命名之前的一样,是 2498227

mv移动:haa.txt 移动到 testdir/ 目录中
,移动之后 testdir/haa.txt 的inode 编号和移动之前一样,是 2498227

从上面的例子可以知道:拷贝命令会重新分配新的 inode, 改名以及移动文件只会修改文件名或文件位置,文件的 inode 并不会改变。