Linux之文件系统前世今生(二)
问题:
- 为啥
cd ..
可以返回上一级目录 ? rm
删除了什么?
Linux在线1
Linux在线2
一、硬盘 Inode 里的链接
在Linux之文件系统前世今生(一)我们提到了 Inode 这个文件索引节点,不知道你有没有发现我们的 Inode 这个字段里竟然没有文件名,那文件名存放在哪里了呢?
1.1 文件名存放位置
- Inode 号唯一标识了一个文件,所以 Inode 号就是文件名,于机器而言。
- 只是这个 Inode 号 太难记忆了,为了便于人类阅读和记忆,允许你给这个 Inode 号 起个名字(正如IP地址太难记忆,才有了域名)。
- 我相信你一定有名字,而且还有其他别名,不同场合,别人对你的称谓不同;
- 所以 对于同一个Inode 也可以有很多别名;不同目录里,可以有不同名字(当然,相同目录里有不同名字也可以),最终都指向同一个 Inode;
综上:
- 文件名(即人类方便记忆的字符序列)存放在了目录的内容(数据块)中,如前文图1.2所示,即目录保存了文件名和 Inode 的映射关系;换言之,直接给你一个 Inode 号码,我们无法知道这个文件的名字是什么。 除非从根目录开始遍历比较 即:
find / -inum 12,659 -print
- 文件名、Inode、文件内容 三者是分开存放的;
- 这使得软件更新变得简单:
- 因为打开一个文件以后,系统就以 Inode 号码来识别这个文件,不再考虑文件名。所以,可以在不关闭软件的情况下进行更新,不需要重启。更新的时候,新版文件以同样的文件名,生成一个新的 Inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向高版本文件,低版文件的 Inode 则被回收。
- 这使得软件更新变得简单:
- 从文件名可以找到Inode,从Inode可以找到文件内容,反向则不行;
1.2 硬链接(hard link)
前文里,我们提到了同一个 Inode 可以有多个不同的名字,我们称之为硬链接,Inode信息里用链接数这个字段来存储有多少文件名指向这个Inode。
- 当一个人的所有名字在这个世界抹除时,这个人也就不存在了,这就是大部分的常人;而圣贤的名字如孔子,孟子却永远存在,所以他们一直活着;
- 同理,只删除一个硬链接并不影响索引节点本身和其它的链接,只有当最后一个链接被删除后,文件的数据块才会被释放。也就是说,文件真正删除的条件是与之相关的所有链接文件均被删除。
硬链接
允许一个文件拥有多个有效路径名,这样用户就可以建立硬链接到重要文件,以防止“误删”
的功能。其原因如上所述,Linux 使用引用计数来进行垃圾回收;- 很多备份工具利用的就是硬链接的功能,包括
git
工具,当克隆本地的一个仓库时,执行 clone 指令:git clone --reference <repository>
,git
并不会把仓库中的所有文件拷贝到本地,而仅仅是创建文件的硬链接;- 通过命令
ln 文件 硬链接名称
即可创建硬链接,对于Inode里的链接数会++
。
1.3 软链接(soft link )
与硬链接相对应的是软链接(soft link , 也即符号连接: symbolic link):它实际上是一个特殊的文件。在软链接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息,即 软链接会生成新的文件,新文件有新的Inode号码。
- 当执行路径非常的深,导致路径非常长,我们可能会忘记路径,所以我们可以通过软链接快速找到。
- 软链接还可以解决文件系统磁盘空间不足的情况。例如某个文件系统空间已经用完了,但是现在必须在该文件系统下创建一个新的目录并存储大量的文件,那么可以把另一个剩余空间较多的文件系统中的目录链接到该文件系统中,这样就可以很好的解决空间不足问题。
- 删除原文件,软链接随之失效;软链接就是个快捷方式;
- 通过命令
ln -s 文件 软链接名称
即可创建软链接。
1.4 软链接 vs 硬链接
在/xiaolingting/month/january
目录下给文件 today.log
建立硬链接 tomorrow.log
,同时建立软链接quick.log
,存储结构如下图所示:
正如上图所示:硬链接不占用磁盘空间,软链接占用的空间只是存储路径所占用的极小空间。
- 软链接的极小空间也需要一个Block啊,可以优化下,即如果目标路径名较短则直接保存在Inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
- 顺便提下:设备文件、FIFO(命名管道)和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在Inode中。
通过命令 ll -ha
和 du
命令来查看目录/xiaolingting/month/january
的大小,神奇的事情发生了,ll
比 du
显示的大?
ll
统计的是 Inode 里的数据,会累计所有硬链接的数据;du
统计的是真实占用磁盘的大小。
注意:硬链接只能在同一文件系统中的文件之间进行链接
1.5 目录链接
新建一个目录 empty
通过命令 ls -ali
来查看链接数和Inode号,如下图所示:
在目录 empty
下通过命令mkdir sub
新建 sub
目录,即目录 sub
位于目录 empty
下:
- 目录
empty
有链接数 2 个; - 目录
empty
和 该目录下面的.
Inode号 一样,均为573
; - 目录
empty
和 该目录下面的.
均指向同一个 Inode,所以这个Inode的链接数为 2; .
是指当前目录,即.
也是一个文件名;- 同理,
..
是指当前目录的上级目录,即..
也是一个文件名,映射的 Inode 就是上级目录的 Inode; - 在创建目录的同时,文件系统会为它创建两个目录:
.
和..
,分别对应当前目录的硬链接、上级目录的硬链接。因此,每一个目录都会包含这两个硬链接。 - 任何一个目录的
硬链接
总数,总是等于 2 加上它的子目录总数。
注意:文件系统会自动为目录创建硬链接(.
和 ..
),该权限未对用户开放,用户无法对目录创建硬链接。因此,硬链接只能对文件创建。
1.6 根目录(/
)硬链接
上面说到,文件系统会自动为目录创建硬链接(.
和 ..
),对于 /
(根目录)同样如此,那/
的上一级目录是谁?
/
没有上一级目录了,所以上一级只能是自己。即/
和/..
和/.
均指向同一个Inode,它们互为硬链接;- 我们称上面这种
.
和..
指向同一个Inode的现象为自引用; - 根文件系统是唯一可以自引用的文件系统。
根文件系统首先是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。
二、Inode 里的进程计数器
在上面我们提到了,当 Inode 里的链接数为0时,就会删除 Inode 了。
问题来了,一个进程正在使用这个文件呢,此时 Inode 里的硬链接数为 0 了,直接删除,那使用这个文件的进程不就挂了?
实际当中,当有进程使用这个文件时,该文件处于active状态时(比如被打开),内存中会保留一份 Inode 的拷贝,我们称之为 Inode 核内拷贝(In-core copy of inode,以下用 “ 核内Inode ” 代替),之前提到的 Inode 暂且称之为 磁盘Inode。
核内Inode就是 VFS 中的 Inode,详见Linux之文件系统前世今生(一)
核内Inode
在 磁盘 Inode
的基础上增加了一个 进程内计数器(i_count),当有进程使用这个文件时,该文件的 核内Inode
的 进程计数器就会++
;
所以,核内Inode 有2个链接数:
- 硬链接数(i_link),即磁盘内对这个文件的链接计数;
- 进程内链接数(i_count),即内存对这个文件的引用计数;
回到开始提到的问题,rm
操作只是将文件的i_nlink
减少了,如果没有其它的链接i_nlink
就为0
了。但是由于该文件依然被进程引用,此时文件对应的i_count
并不为0
,所以执行rm
操作,系统并没有真正的删除这个文件,只有当i_nlink和i_count都为0的时候,这个文件才会被真正的删除。