前言
在上篇博客当中,我们对 文件系统 和 inode 做了初步了解,本博客将在上篇博客的基础之上,对于 文件系统当中的目录进行进步一阐述。
Linux - 进一步理解 文件系统 - inode - 机械硬盘-CSDN博客
目录
一个文件有一个 inode,每一个 inode 都是有自己的 inode 编号(这个inode 编号只在自己当前所在分区当中有效)。
inode 的划分是以 分区为单位的,也就是说,各个分区当中的 inode 是独立的,inode 编号也是独立的。
虽然,inode 当中存储了 这个文件的所有属性,但是在这个inode 当中不会存储 文件名的。也就是说,文件名压根就不属于 文件属性。
换言之,如果我们想要访问一个文件,如果这个文件名是在 inode 当中存储的,那么对于操作系统来说,用户就要告诉这个 操作系统 ,inode 是多少,才能拿到 文件名。
但是,如果是 小白用户,压根就不知道 inode 的存在,他知道 文件名,而且,我们日常在访问文件,修改文件,查找文件 的基本都是通过 文件名来操作的。
使用者从来没有关心过 inode 这个是什么,使用者对于操作文件都是通过 文件名来操作的。
而,文件名肯定是有重复的,操作系统如何识别这些重复的文件,其实靠的就是 目录,我们知道,有绝对路径 和 相对路径来找到某一个文件。
所以,其实我们是通过 目录来找到各个文件的。
那么目录是什么呢?
其实目录本质上也是一个文件:
目录也是文件,目录也有自己独立的 inode。也就是说,目录也有自己的 属性。
那么,在目录当中有内容吗?
答案是有的。那么目录这个文件当中存储的是什么呢?
目录的数据块当中,存储的是 目录当中的文件的 文件名 和 各个文件对应的 inode 的映射关系。
所以,一个文件的文件名不是存储在 这个文件的属性(inode)当中的,文件的文件名不是这个文件的属性,这个文件的文件名 和 这个文件对应的 inode 的映射关系 是存储在当前文件所以在目录的内容当中的。
所以,比如 ls 这个命令,在查找当前目录下的 文件和 目录的话,其实就是在当前目录的内容当中找到 本目录下的 文件名 和 各个文件映射的 inode 关系,根据命令行参数选项,打印出这个文件对应的信息即可。
所以,如果我们想要进入某一个目录当中,那么这个目录就要有 x 权限;当我们在某一个目录当中 创建一个新的文件,或者是 要删除某一个文件,需要这个目录有 w 权限。
因为,就算我们在一个没有 w 权限的目录 执行路径 当中创建一个了一个新的文件,但是,这个文件的 文件名 和 这个文件对应的 inode 映射关系是不能再 保存在 这个 目录文件对应的数据块当中。
同样,如果是这个目录是没有 r (读)权限的,那么这个目录当中的文件是不能访问的,因为 要像访问一个文件,或者是修改一个文件,那么就要拿到这个文件的 inode 。但是,因为目录文件是不给读的,所以拿不到想访问文件的inode,那拿不到 inode 怎么访问文件呢?
而像是 绝对路径和相对路径,也是同一个 根目录 或者是 当前目录 文件的数据块当中,一层一层递归的方式来寻找的。
所以,如果我们要想访问 当前目录当中的某一个目录的话,就需要找到这个目录的 inode,才能访问到这个目录文件。
但是,要先找到这个 目录的 inode ,就要在这个目录的上一层的 目录文件的数据块当中找到这个目录文件的 inode映射关系。
到这你可能就会想,那么这不就递归了吗?我们要想找到这个目录的 inode 就要一直往上去递归式的去寻找。
是的,是递归式的寻找,但是,不是无穷无尽的递归,因为 我们从任何路径当中 往上来递归式寻找的话,一定可以找到一个目录 --- 根目录。
所以的目录都是从 根目录 衍生出来的。
所以,绝对路径就是 先递归式的返回遍历到 根目录,再根据 给出的绝对路径 来找到 对应文件。
相对路径就更简单了,只要是当前目录路径已经被找到了,只需要按照相对路径当中给出的路径来进行查找即可。
得出结论:
- 在Linux 当中,访问任何一个 路径都需要带上路径,可能你在使用 ls 等等这些命令的时候,没有带上路径,但是同样也访问到了 目录文件,或者是文件当中的内容。其实这些命令访问文件也是要 路径的,只不过,我们可以通过设置一些环境变量来 提前保存一些路径,这些我们称之为 -- 默认路径,系统就会默认从 这个路径当中来访问文件,但是其实本质上也是 通过路径来访问到内容的。
而,像上述要像递归到根目录的方式,来查找 文件,这种方式太慢了,所以,在 Linux 当中,会把我们曾经访问过的,或者是经常访问的 若干目录, 已经这些目录当中的若干信息(比如 文件映射的 inode,文件名等等信息),缓存一份。---- dentry缓存。
当我们需要访问 缓存当中存在的文件之时,就可以直接从 缓存当中读取到 这个文件的 inode 等等信息,直接访问到这个文件了,不需要再去递归式的寻找 文件位置。
软链接 和 硬链接
我们先来看是如何创建一个 文件的 软链接 和 硬链接的:
软链接;
上述就是创建一个 软链接,此时就有一个 text_link 指向 text.c 文件了。
如果你查看这个 text_link 的属性,你会查看到 这个 text_link 有 inode ,说明这个 text_link是一个文件,而且,在这个文件的后面还有一个 数字,你可以看到是1:
你可以发现这个 text_link是有 inode的 ,说明这个 text_link是一个 文件,而且,在 这个 text_link 和 text.c 两个文件的访问权限 后面 还有一个 数字1 ,这个数字1 我们在后面 说到 硬链接的时候再叙述。
所以,此时也就是相当于是 有一个 text_link 软链接文件指向了 text.c 这个文件。
硬链接:
像上述就生成一个 硬链接文件。
同时,这个 新生成的 text_link 硬链接文件,也是有 inode 的,但是这个 inode 是和 text.c 文件是一样的,说明这个 硬链接不是一个独立的文件,而且,此时,在 text_link 这个文件的 访问权限符 后面的 数字,变成了2。
而且,相信你还注意到 ,我们对应生成目标文件的 硬链接文件的目标文件,也就是 text.c 这个文件。在文件访问权限符之后的数字,在生成 硬链接文件之前,本来是 1 的,但是在生成 硬链接文件之后就变成了2。
对于上述的结果,我们先不做阐述,我们下来看看 生成 软链接文件 和 硬链接文件之间的语法是什么:
其实,都是使用 ln 这个语法,但是吗,如果是软链接文件,需要带上 -s 这个选项参数,其实这里的 -s 就是 Soft 软的这个单词的缩写。
如不带上 -s 这个选项那么,默认就是 硬链接文件的生成方式。、
创建软链接和硬链接的语法:
ln -s 被指向文件名 生成的目标指向文件的软链接文件名
ln 被指向文件名 生成的目标指向文件的软链接文件名ln -s text.c text_link #生成一个text_link 软链接文件指向 text.c 文件
ln text.c textlink #生成一个text-link 硬链接文件指向 text.c 文件
我们先来说说,上述所说的 在文件访问权限符之后的 数字代表的是什么意义?
其实这个数代表的意义是代表这个文件当前的硬链接个数。(其实就是 当前 inode 的使用文件的引用计数)
其实,此时,如果我们把同一个文件的 软链接文件 和硬链接文件 ,都创建出来:
你会发现,硬链接文件(上图的text-link文件) 和 链接的文件(上图的text.c文件) 的 inode 是一样的。
但是,软连接文件的 inode 和 其他两个文件的 inode 是不一样的。
硬链接不是一样的独立的文件,因为硬链接文件 没有独立的 inode。
理解硬链接
因为硬链接文件的 inode 和 链接的文件的inode是一样的。所以,这两个文件的属性应该是一样的。
这里也侧面的证明了 ,文件的文件名是不在 文件的inode 当中存储的,而是在目录当中存储的。
而,所谓的建立硬链接,本质上其实就是在特定的 目录的数据块当中,新增 文件名 和 文件和inode 的映射关系。
简答来说,就是在 目标文件所在目录的内容当中(也就是在目录的数据块当中),把 新按照目标文件生成的 硬链接文件的 文件名和 对应的 inode 映射关系,保存到 目标文件所在目录的内容当中。
如果此时,我们在 硬链接文件存在的情况下,删除 这个硬链接文件链接的 目标文件的话,会出现什么结果呢?
上述是结果,下述是删除 text.c 文件之前的结果:
发现,就算我们删除了text.c 这个文件,但是,硬链接文件并没有失效,inode 还是和之前一样,跟删除的 目标文件的 inode 保持一致,并没有发生改变。
但是,引用计数 变成了1,因为此时 硬链接个数又变成了1个。
像上述这种,先创建一个文件的 硬链接文件,然后删除掉这个文件,保存这个文件的硬链接文件,这个操作被称之为 -- 取别名。
所以,在每一个 inode 内部,都有一个 作用于 当前 inode 硬链接个数的 引用计数。
而,在目录当中,保存了 每一个 文件名 对应 映射的 inode 的信息。
可以存在 不同的文件名,映射到同一个 inode 当中。
所以,现在我们可以有一个更好的对于 引用计数的概念,不在是 硬链接文件个数了,而是 有多少个 文件名 映射该 inode。
理解软链接
软链接文件当中,你可以发现,其实软链接文件是一个新创建的独立的文件。因为在创建之后,有独立的 inode。所以,软链接不会影响 链接的目标文件的 引用计数。
那么既然有 独立的 inode ,也就意味着有独立的 数据块,也就是说有独立的存储空间,在这个文件当中要存储什么呢?
存储的是 指向的目标文件的路径。
其实你可以理解为 在软链接当中存储的是 ,指向目标文件的 指针,通过这个指针可以访问到 这个软链接指向的目标文件。
发现,在text.c 当中重定向的字符串 aaaaa ,通过 text-link 这个软链接文件也可以访问到。
所以,既然存储的是指针,那么就会有 野指针的情况,当我们把软链接指向的 目标文件 删除之时,那么这个 软链接文件当中存储的指针就会失效、
如上所说,从蓝色的软连接文件名,变成了红色的,此时代表的意思就是已经出现连接错误了。
其实,这个软链接特别像 windows 当中 程序的快捷方式。
当我们查看这些 程序的快捷方式的属性的话,可以看见一个 目标的属性:
其实这个就是我们上述所说的,软链接当中存储的是 指向目标文件的 路径。 其实是一样的 。
为什么要有 软硬链接
我们日常使用的程序软件,其实都不简简单单是一个 xx.exe 这种直接点击就能运行的程序。整个程序一般还是有自己的 配置文件,或者是程序运行所需要的文件数据信息,所以,往往一个程序的 xx.exe 是藏在一个较深的路径当中:
而,如果我们想在当前目录下(不在这个程序的 xx.exe 文件下)直接运行这个程序的话,就必须要带上绝对路径或者是 相对路径。
但是类似 D:/ProgramFiles(x86)/Huorong/Sysdiag/bin/xxx.exe 这行来运行这个可执行程序就太麻烦了,所以,可以使用 软连接的方式 创建快捷方式,来调用这个可执行程序:
同样,按照上述的方式,我们可以在Linux PATH 环境变量当中创建出我们自己,或者是第三方的 程序的 软链接文件,这样就可以直接 输入 文件名,不用的现去在当前目录下创建 对应的 软链接文件,都可以直接 调用 我们想安装的 程序的可执行文件了:
此时我们就可以直接,在任何路径下调用这个 我们刚刚安装的 程序了:
所以,这个软件安装到哪里都可以,只要在 PATH 环境变量 指向的 系统默认的 路径目录当中创建了 这个程序的 软链接程序,就可以随时随地调用这个程序了。
其次,当你创建了一个新的目录,那么你会发现,这个目录的 硬链接个数是 2 个。其实你应该已经猜到为什么了:
在上图当中,就有一组 inode 和 文件名 的关系,就是当前的dir 这个目录文件名 和 这个文件的inode 的映射关系。现在我们进入到这个目录当中,查看这个目录当中所有文件,包括隐藏文件:
有一个 "." 作为文件名的 文件,很多读者应该知道,这个 "." 文件,代表的是当前所在 所在目录的这个目录的文件。
也就是说,在 dir 这个目录当中的 "." 这个文件,和 dir 这个目录文件 所映射的 inode 是一样的,两个映射的是同一个 inode,代表的是同一个文件。
而且如上图所示,你可以发现 dir 和 dir当中的 "." 这两个文件名的 inode 是相同的。
所以,这里你就可以理解了 ,为了 每一个目录当中的 "." 文件,都可以代表的是 当前目录文件。
上述也提到了 ".." 两个点的文件名,这个文件代表的是当前目录的 上级目录文件,和上级目录文件共用的是同一个inode。
所以,按照这个推理的话,在上述创建 dir 这个目录 所在目录,应该就有 3 个 硬链接个数了,因为在 dir 当中还有一个 ".." 文件是映射的 这个目录:
如上图所示,ln_text 目录就是 dir 所在的目录,是三个 硬链接个数。
同样你可以查看 "/" 根目录 的 硬链接个数:
发现是一个很大数字,因为在 "/" 目录当中已经创建了很多个 目录了,每一个目录当中都有一个 ".." 硬链接上 "/" ,所以才会有这么多。
所以,往后,如果想知道某一个 目录当中有多少个 有效目录(也就是想上述 dir 这样目录)其实可以像上诉一样查看这个目录的 硬链接个数,该目录的 硬链接个数 - 2 就是这个 目录当中的有效目录。
Linux 当中的目录结构是一个多叉树,Linux 当中的文件系统是利用 上述的 硬链接方式,来 维持多叉树当中每一个 结点当中的有 parent指针 和 指向当中结点的指针,这样一个关系。我们称之为 -- 路径定位。实现目录间的切换。
然后使用在目录当中存储的 文件名 和 文件名所映射的 inode 来位置当前结点的孩子结点直接的关系。
在上述你可能有疑问,为什么可以直接用一个目录的 硬链接个数来确定其中的 有效目录个数?
就不怕 用户自己使用 ln 这个指令来对 这个目录文件进行 硬链接吗?
其实不怕。因为 目录是不能使用 ln 创建硬链接的。
如上所示,我们发现报错了,报错信息是 链接不允许是 目录。
但是,目录是可以建立 软链接的。
这里需要注意是:如果你想删除某一个 软链接的话,可以使用 rm 命令,但是有的时候是删不掉的,这个时候更多的使用的事 unlink 软链接文件名 这样的方式来进行删除软链接文件的。
为什么Linux 不给 目录建立硬链接?
我们可以假设一下,然后来考虑为什么。其实很简单,就是一个循环引用计数的问题。
如上图所示,dir 目录文件是 ln_text 目录文件的硬链接,dir 就相当于是我们在某一个目录下 创建的一个ln_text 目录文件的 硬链接文件。
那么在上述 多叉树文件系统当中,如果我想从 ln_text 找到 dir 这个目录的话,从上图当中的红色路径就可以找到,但是,当我们找到 dir 之后,它的 inode 又是 2(和 ln_text 是一样的),所以此时就会回去找到 ln_text 目录, 又回到 最初的起点,但是还是没有找到,所以又会像之前一样往下递归找到 dir 目录·······
这不就是一个循环了吗?
而,上述说过 "." 和 ".." 两个文件不也是建立了 硬链接吗?
这两个文件不是用户建立的,是操作系统自己建立的,它在建立之初,因为 这两个文件和自己硬链接的文件 不会像上个例子一样 隔个十万八千里,人家就是表示的当前目录和 上级目录,所以这个在操作系统内部可以实现。但是如果放在很长的 文件系统多叉树结构当中就不好实现了。
换句话说,操作系统自己能够 实现目录的硬链接,本质上其实是 操作系统只相信自己,不相信任何人,包括 root 。
同样你也可以发现,关于 "." 和 ".." 这两个文件是不能做 搜索操作的吗,只能在 路径定位当中使用。
换言之,操作系统之所以要 才上 循环引用的坑也要 弄出 "." 和 ".." 这两个文件,就是为了引出 相对路径这个概念,让我们更好的使用 文件系统。