inode定义
inode 是 linux 系统中用作数据索引的标识符。
简单来说,inode 指示了一个文件的基本信息,如inode编号、修改时间、文件的位置等,就如同一本书的目录,会直接告诉你想看的章节是在第几页。不同的是,书是以页为单位的,而 linux 文件存取是以“块”为单位的。
操作系统在读取硬盘的时候,会一次性读取一个“块”(一个“块”的大小往往是4KB,包含了连续8个扇区,每个扇区存储512个字节)。而inode就告诉了文件位于哪个“块”,于是系统就会从这个“块”开始读取内容,我们就可以看到这个文件的内容。
每个文件都有对应的inode,存储着关于这个文件的基本信息。linux 系统不使用文件名,而使用 inode 号来识别文件。对于使用者,我们是通过文件名打开的文件;但是对于系统内部,是分为三步的:
- 系统找到这个文件名对应的 inode 号
- 通过 inode 号,获取 inode 信息
- 根据 inode 信息,找到文件数据所在的 block,读取内容
inode内容
inode 包含了文件的以下基本信息:
- 文件的字节数
- inode 编号
- 文件拥有者的 Uid
- 文件所属group的 Gid
- 文件的读、写、执行权限
- 文件的时间戳,共有三个:
- change:inode 上一次变动的时间
- modify:文件内容上一次变动的时间
- access:文件上一次打开的时间
- 链接数,即有多少文件名指向这个 inode
- 文件数据 block 的位置
我们可以使用 stat
命令来查看文件的 inode 信息,如:
$ stat v0.1.0.zip
File: ‘v0.1.0.zip’
Size: 94267 Blocks: 192 IO Block: 4096 regular file
Device: 811h/2065d Inode: 5659765 Links: 1
Access: (0640/-rw-r-----) Uid: ( 3457/mart_bda) Gid: ( 3457/mart_bda)
Access: 2018-06-12 14:22:18.434027485 +0800
Modify: 2018-06-12 14:18:00.840994081 +0800
Change: 2018-06-12 14:18:00.840994081 +0800
Birth: -
也可以在 ls
后加上 -i
直接获取 incode 编号:
$ ls -i v0.1.0.zip
5659765 v0.1.0.zip
inode大小
inode存储了文件的基本信息,虽然信息很少,但是也会占用空间。
硬盘格式化的时候,操作系统自动将硬盘分为两个区域:
- 数据区:存放文件内容
- inode 区:存放 inode 包含的信息,也叫作 inode table
每个 inode 节点的大小,一般是 128 字节或 256 字节。inode 节点的总数,在硬盘格式化时就固定了。一般,数据区每1KB 或 2KB,inode区就会增加一个 inode。假如在一块 1GB 的硬盘中,每个 inode 节点的大小为 128 字节,那么 inode 表的大小就会达到 128 MB,占整块硬盘的 12.8%。
既然 inode 节点总数是有限的,那么分区的节点数就有用完的时候,一旦 inode 用完了,即使磁盘空间还有剩余,也不能再存放任何数据,因为需要保证每个文件必须有一个 inode。
查看每个硬盘分区的 inode 或者磁盘容量的使用情况,可以使用 df
命令加上参数 -i
或者 -h
,如:
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda5 275436544 801853 274634691 1% /
devtmpfs 8192960 524 8192436 1% /dev
tmpfs 8195307 4 8195303 1% /dev/shm
tmpfs 8195307 765 8194542 1% /run
tmpfs 8195307 13 8195294 1% /sys/fs/cgroup
/dev/sda2 204800 342 204458 1% /boot
/dev/sdb1 11443200 3329257 8113943 30% /data0
tmpfs 8195307 1 8195306 1% /run/user/0
tmpfs 8195307 1 8195306 1% /run/user/3457
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda5 263G 134G 130G 51% /
devtmpfs 32G 0 32G 0% /dev
tmpfs 32G 12K 32G 1% /dev/shm
tmpfs 32G 394M 31G 2% /run
tmpfs 32G 0 32G 0% /sys/fs/cgroup
/dev/sda2 197M 139M 58M 71% /boot
/dev/sdb1 11T 5.2T 5.2T 51% /data0
tmpfs 6.3G 0 6.3G 0% /run/user/0
tmpfs 6.3G 0 6.3G 0% /run/user/3457
关于 df -h -i
的区别,可以参考 Linux df命令。
文件操作对 inode 的影响
要理解文件的操作对 inode 的影响,先要理解目录的原理。目录对外表现是一个容器,存放着子文件和子目录,实际上在系统内部,目录本身也是一个文件,目录文件的内容即是该目录下的文件名与 inode 号的映射表(即一个个的目录项)。因此,linux 访问一个文件时,要先查询到上一级目录,根据目录内容查找到文件对应的 inode 号,然后读取对应的 block。
###cp 命令
系统内部会执行以下操作:
- 分配一个未被使用的 inode 号,在 inode 表中新添一个项目。
如果是覆盖复制,则 inode 号不变,沿用之前同名文件的 inode 号。
- 在目录中新建一个目录项,并指向步骤 1 中的 inode。
- 把数据复制到 block 中。
rm 命令
系统内部会执行以下操作:
- 减少待删除文件名所对应的 inode 的链接数量,如果链接数变为0,则释放 inode,同时数据块放到可用空间中(对外表现为数据已删除,因为随时可以覆盖。如果没有覆盖,数据还可以恢复;一旦覆盖了,那么删除的数据无法恢复。)。
- 删除目录中的目录项。
mv 命令
一、如果目标文件和源文件属于同一个文件系统:
- 在目标文件的目录中新建目录项
- 删除源文件的目录中的目录项
- 目标文件名会指向源文件名的 inode。因此该操作对 inode 没有影响(除了时间戳),对数据的位置也没有影响,不移动任何数据。
二、如果目标文件和源文件属于不同文件系统,则相当于 cp + rm。
ln 命令
一、硬链接
一般情况下,文件名和 inode 号是一一对应,但是也有可能多个文件名指向同一个 inode 号,即硬链接。硬链接可以实现用不同的文件名访问同一个文件;对文件内容修改,会影响到所有的文件名;但是,删除一个文件名,不影响其他文件名的访问。
创建硬链接的命令:
ln [source file] [new file]
如:
$ ll -h -i
total 479M
5659849 -rw-r----- 1 mart_bda mart_bda 479M Jun 13 10:57 test_file
$ ln test_file test_file_hardlink
$ ll -i -h
total 957M
5659849 -rw-r----- 2 mart_bda mart_bda 479M Jun 13 10:57 test_file
5659849 -rw-r----- 2 mart_bda mart_bda 479M Jun 13 10:57 test_file_hardlink
这样,两个文件的 inode 号均为 5659849。具体查看两个文件的 inode 内容:
$ stat test_file
File: ‘test_file’
Size: 501577774 Blocks: 979656 IO Block: 4096 regular file
Device: 811h/2065d Inode: 5659849 Links: 2
Access: (0640/-rw-r-----) Uid: ( 3457/mart_bda) Gid: ( 3457/mart_bda)
Access: 2018-06-13 10:57:13.961409755 +0800
Modify: 2018-06-13 10:57:14.931383436 +0800
Change: 2018-06-13 10:58:11.382851699 +0800
Birth: -
$ stat test_file_hardlink
File: ‘test_file_hardlink’
Size: 501577774 Blocks: 979656 IO Block: 4096 regular file
Device: 811h/2065d Inode: 5659849 Links: 2
Access: (0640/-rw-r-----) Uid: ( 3457/mart_bda) Gid: ( 3457/mart_bda)
Access: 2018-06-13 10:57:13.961409755 +0800
Modify: 2018-06-13 10:57:14.931383436 +0800
Change: 2018-06-13 10:58:11.382851699 +0800
Birth: -
可以看到,两个文件的 inode 内容完全相同,且 Links
变成了 2。修改任何一个文件名的内容,另一个文件名的内容也会同时改变,因为访问的就是硬盘中的同一块数据。
如果再将 test_file_hardlink 删掉,会使得 Links
变回 1。当这个值减到 0 时,说明没有文件名指向这个 inode,系统就会回收这个号码,以及所对应的 block 区域。
另外,对于目录的链接数,创建一个目录时,默认会生成两个目录项:.
和 ..
。前者的 inode 号就是当前目录的 inode 号,等同于当前目录的硬链接;后者的 inode 号是父目录的 inode 号,等同于父目录的硬链接。因此,任何一个目录的硬链接总数,总是等于 2 加上它的子目录总数(含隐藏目录,且除去.
和 ..
)。
二、软链接(符号链接)
软链接也可以通过不同的文件名访问同一块数据,但是与硬链接不同的是,两个文件名的 inode 是不一样的。那如何访问同一块区域呢?比如文件 A 是文件 B 的软连接,那么文件 A 的内容存放的是文件 B 的路径名(可以通过这个找到文件 B 的目录项)。因此访问 A 时,会读取文件 B 的路径,进而读取文件 B 的内容。这样,对外表现来看,文件 A 和文件 B 的内容就相同了。类似于 windows 系统下的快捷方式。
建立软链接的命令:
ln -s [source file] [new file]
如:
$ ll
total 489824
-rw-r----- 1 mart_bda mart_bda 501577774 Jun 13 11:21 test_file
$ ln -s test_file test_file_soft
$ ll -h -i
total 479M
5659853 -rw-r----- 1 mart_bda mart_bda 479M Jun 13 11:21 test_file
5659854 lrwxrwxrwx 1 mart_bda mart_bda 9 Jun 13 11:22 test_file_soft -> test_file
如果是对文件夹建立软链接,则为
ln -s /tmp/test_directory ./
会自动地在当前目录建立一个文件夹 test_directory
,并指向 /tmp/test_directory
可以看到,两个文件的 inode 号是不同的。
既然文件 A 是依赖文件 B 存在的,那么如果删除了文件 B,打开文件 A 就会报错:No such file or directory
;如果删除了文件 A,则对文件 B 的打开无影响,因为只是删除了“快捷方式”而已。
软连接的建立,不会影响到文件 B 的 inode 的任何信息,包括 Links
。
三、硬链接和软链接的不同
- 本质不同:硬链接是指向同一个文件,软链接指向的不是同一个文件。
- 删除时:硬链接不受影响,软链接失效
- 创建链接时:创建硬链接链接数加1,创建软链接连接数不变
- 是否可以跨分区:硬链接不可以跨分区,软链接可以跨分区
- 目录是否可以创建链接:硬链接不可以对目录创建,软链接可以对目录创建
- 硬链接的inode号相同,软链接inode号不同
四、硬链接和软链接的占用空间分析
大家可能注意到了,对于同一个 test_file(大小为 479M
),建立硬链接后,目录的整体空间占用为 total 957M
,而建立软链接后,目录的整体占用空间仍为 479M
。既然硬链接指向的是同一块数据,那么就不会开辟新的空间去复制一份,应该是不占用空间的啊?为什么空间占用会加倍呢?这个问题,可以参考关于硬链接与软连接占用磁盘空间问题的分析研究。