【文件系统】软硬链接

目录

  • [1. 汇总 ext2 文件系统](#1. 汇总 ext2 文件系统)
  • [2. 文件名 与 inode 的关联](#2. 文件名 与 inode 的关联)
  • [3. 软硬链接](#3. 软硬链接)
    • [3.1 理解硬链接](#3.1 理解硬链接)
    • [3.2 理解软链接](#3.2 理解软链接)
    • [3.3 软硬链接的应用场景](#3.3 软硬链接的应用场景)

1. 汇总 ext2 文件系统

  • 新建文件

    一个文件一定是在某个路径下创建的,有了路径,就能够知道该新建文件所在分区,然后再查 GDT,发现 inode 还没分配完,于是遍历 inode Bitmap 找到最近一位没有被使用的 inode 编号(即比特位为0), 再通过 inode Table 找到对应的 inode,把文件属性填充到 inode 中,如果文件中没有写入数据,那么文件的新建,到这一步就完成了。如果后续对文件写入数据,确定了写入数据的大小,接着根据 inode 编号找到文件所在分组,然后在 Block Bitmap 中分配足够的数据块,并且把这些数据块的编号填充到 inode 属性中,最后将数据写入对应的数据块。

  • 删除文件

    与新建文件相反。先根据 inode 找到文件属性,再根据其中的 block 数组,通过 Block Bitmap 映射,将所有数据块编号所对应的比特位置 0,再通过 inode Bitmap 映射到对应的 inode,将该比特位置 0 即可。因此,删除的本质只是允许被覆盖,并不会真正去删除文件的属性和内容这些数据。如果删除文件时,就直接删除数据,更加频繁的 IO 会导致整机效率的降低,并且存储设备的寿命减少。

  • 查找文件

    根据文件所在路径确定分区,再根据文件 inode 编号确定所在分区,查找 inode Bitmap 发现文件有效之后,通过位图索引到 inode Table 中对应的 inode,读取文件属性。如果类似 cat file.txt 读取文件内容,那么就再通过 inode 中的 block 数组找到所有的数据块,把数据块内容做拼接,最后显示打印在显示器上。

  • 修改文件

    不管是 chmod 修改文件权限,还是 touch 修改文件时间,又或者修改文件内容。只要拿到文件 inode 编号,修改属性的话就直接对 inode 中的属性做修改。修改内容,如果原来数据块空间不够用了,那就在 Block Bitmap 中重新找到可用的数据块,分配给该文件inode,最后把新增的数据块映射在 inode 中的 block 数组上不就行了。

2. 文件名 与 inode 的关联

在 linux 系统中,一个文件有一个 inode,每一个inode都有自己的 inode 编号( inode 分配,是以分区为单位的,不能跨分区),inode 中记录了文件的所有属性,但不包括文件名,文件名并不属于 inode 内的属性!

但是,作为操作系统的使用者,我如何得知一个文件的 inode 编号?用户可从来不关心 inode,在对文件进行操作时,用的也都是文件名啊! 那我们平时用的文件名到底和 inode 之间有什么关联呢??

目录也是文件,也有 inode,因此目录也有自己的文件属性!

而目录不仅有属性,它也是有内容的,所以目录也需要分配数据块。目录文件的数据块存放的就是该目录下所有文件名 与文件对应的 inode 编号之间的映射关系,是一组 kv 结构的数据。

换言之,如果我想要找到目录下的某一个文件时,必须先找到该目录文件的 inode,然后根据该目录文件的 inode,找到其属性和内容,读取它的数据块,就能够知道该目录下的文件和对应文件的 inode 的映射关系,进而找到文件。

  • 所以我们也就自然能够理解为什么同一目录下,不允许存在相同的文件名,因为一切文件操作是根据文件的 inode 编号执行的,而目录的数据块中,是以文件名作为 key 值映射查找文件的 inode,所以文件名当然不一样,不然怎么映射对应文件的 inode,每个文件的 inode 可是具有唯一性的!
  • 如果目录没有 w 权限,是无法创建文件,删除文件等一切对文件做写操作的行为。这是因为,如果目录权限规定了不具备写权限,那么即便文件在该目录已经被创建出来了,该文件的文件名与 inode 编号的映射关系也无法写入目录的数据块中, 没有映射关系,别说写文件了、删文件了,就连该目录下的文件的属性和数据,你都无法找到。
  • 同理,如果目录没有 r 权限,无法查看文件属性和内容。目录不让用户读取数据,用户就无法读取目录的数据块,因此也就无法拿到该目录下的文件和对应文件的 inode 编号的映射关系,也就拿不到文件的 inode,一切文件操作就无法进行。
  • 目录没有 x 权限,我们就无法进入该目录。当 cd 进入目录时,需要拿到目录的 inode 编号,然后把更新系统中的环境变量的信息,但是这中间会多一条判断,如果发现该目录没有 x 权限,则驳回一切请求。

到这里,大家必须要有一个疑问:

找一个文件时,需要先找到该文件所在目录的 inode,进而根据目录文件的 inode 找到其数据块,才能根据文件名知道我这个文件的 inode,这一点没问题,但是我怎么知道该目录的 inode 呢??一个目录一定是另一个目录的子目录,所以按照这种逻辑,我是不是得一路向上递归,想要找这个文件 inode,就得找当前目录的 inode,想要找当前目录的 inode,就得找上级目录的 inode,这不就是套娃吗?!

好在我们的根目录的名字是固定的,因此根目录的 inode 一定能够找到,因为它是不变的。所以只要我递归找到根目录的 inode,我就能够一层一层把 inode 返回,最终找到我这个文件所在目录的 inode,然后找我的文件 inode。这也是为什么我们在进行文件操作时,一定需要有路径,没有路径是找不到当前文件的 inode 的!

你说的不对!我在执行 ls,touch,mkdir 这类指令时,我压根就没带路径!
不!那只是你觉得没带路径,诸如上述这种系统指令,其程序所在目录早就在系统中的环境变量中配置好了,当你执行这些指令时,操作系统会自动带上环境变量中的路径。任何文件执行起来都会带上路径,包括自己写的可执行程序文件,执行起来变为进程,进程也有当前工作目录 cwd.

可是,如果是按照这种思想,我要找一个文件,需要一直递归找到根目录,再把 inode 层层返回才能拿到我想要的文件的 inode,那岂不是太慢了。

没错的,所以操作系统对此采用了一个 dentry 缓存,操作系统会把常访问的若干目录,以及递归查找目录 inode 时经过的各种文件名,文件对应的 inode 属性信息,包括使用过的路径信息全部缓存起来,这样就不需要每次都递归查找文件 inode 了。

3. 软硬链接

bash 复制代码
ln -s file.txt sotf-link	# 软链接
ln test.txt hard-link		# 硬链接

[outlier@localhost inode]$ ls -li
total 0
#			【硬链接数】/【引用计数】
34150680 -rw-rw-r--  1  outlier outlier 0 Aug 13 16:07 file.txt
34165047 -rw-rw-r--  2  outlier outlier 0 Aug 13 16:07 hard-link
34150681 lrwxrwxrwx  1  outlier outlier 8 Aug 13 16:07 sotf-link -> file.txt
34165047 -rw-rw-r--  2  outlier outlier 0 Aug 13 16:07 test.txt

软链接是一个独立的文件,对 file.txt 建立软链接,sotf-link 的 inode 编号与 file.txt 并不相同!因为软链接具有独立的 inode!

相反,硬链接不是一个独立的文件,因为太没有独立的 inode,它的 inode 编号与 test.txt 是相同的。

3.1 理解硬链接

对 test.txt 做了硬链接,hard-link 与 test.txt 是同一个 inode,所以就注定了它们的文件属性是一模一样的。因为 inode 是同一个,因此在文件系统上,inode Table 里面最终找到的都是同一个 inode,里面的文件属性自然也就一样了,不仅如此,它们所指向的数据块也是一样的。这也就再次印证了,inode 中不保存文件名信息;如果 inode 中保存了文件名,那么就不可能出现同一个 inode 会有两个不同的文件名的现象。

还记得上面讲过,一个文件的文件名与其对应的 inode 编号的映射关系,就存储在所在目录的数据块中!那硬链接做了什么事呢?硬链接的本质就是在特定目录的数据块中新增 文件名 和 硬链接指向的目标文件的 inode 编号的映射关系!

任意一个文件,无论是目录还是普通文件,都有 inode,每一个 inode 内部都有一个引用计数。而目录的数据块保存的是 文件名 与 对应文件的 inode 编号的映射关系,假如有 file1、file2、file3、file4 都与编号为 123456 的 inode 建立了映射关系,换言之,这几个文件名都指向了 inode123456,而引用计数就是记录有多少个文件指向当前 inode!

所以如果这种说法是正确的话,那么当我删除一个文件名时,本质就是 引用计数 -1 的操作,直到引用计数为 0 时,才会清空该文件 inode 所对应的位图结构(Block Bitmap 和 inode Bitmap)

而我们创建出来的普通文件,其引用计数一定是 1,因为在该目录下,只有你创建出来的这个文件名与该文件的 inode 编号做了映射啊!

3.2 理解软链接

软链接与目标文件的 inode 编号并不相同,这就说明软链接是一个独立的文件,也就拥有独立的文件属性和数据块,它的数据块保存的就是指向目标文件的路径!

所以只要我对软链接做读取,是可以读取到目标文件的内容的。

既然软链接是一个独立的文件,所以把软链接删除,不会影响目标文件;反之,软链接文件的数据块存储的是目标文件的路径,因此把目标文件删除了,软链接也就无法使用了。软链接的作用就相当于 windows 平台中的快捷方式!

3.3 软硬链接的应用场景

  • 软链接:

    在发布一个应用程序时,很多都不止是一个可执行程序那么简单,还会有与该应用息息相关的各种配置文件 conf,也有 bin 目录下的各种二进制文件,也包括可执行程序。但一个应用程序发布时,往往都不会把可执行程序直接暴露给用户,而是对其做一个软链接(即快捷方式),供用户使用。

  • 硬链接:

    在一个路径下,创建一个新目录 dir,为什么它的硬链接数就是 2 了??

    因为每个目录都会存在两个隐藏文件,一个 . 当前目录,一个 .. 上级路径,. 不就是指向当前这个目录 dir 吗,加上 dir 自己,一共不就有两个文件名指向 dir 目录吗??并且可以看到 . 这个隐藏文件的 inode 编号与 dir 目录的 inode 编号是一样的,换言之,. 就是目录 dir 的硬链接!所以 dir 目录的引用计数 / 硬链接数自然就等于 2 了。

    再看,所以为什么 .. 的硬链接数位 3 呢?? (上级目录名假设为 inode)

    .. 代表上级目录,上一个目录也得有 ... 这两个隐藏文件吧。所以上级目录 inode 自己 + inode 目录内的 . 文件,再加上 dir 目录内的 .. 指向 inode 目录,一共就有三个文件指向 inode 目录!因此 inode 的硬链接数为 3,也即 dir 目录内的 ... 硬链接数为 3,它们是指向的都是同一个 inode 和数据块。

    所以,如果我在 dir 目录下再创建一个 subdir,那么 dir 的引用计数将会变为 3,原因与上面同理,dir 自己 + dir 目录内的 . 文 + subdir 目录内的 .. ,一共三个文件指向 dir 目录,因此 dir 的引用计数为 3。

    所以我们是能够发现一个规律的,一个目录内部如果没有子目录,那么它的引用计数为 2,多新增一个子目录,那么引用计数就会 + 1,因为新增的目录里面会有一个 .. 文件指向上级目录。因此我们查看一个目录的引用计数,就能够得知该目录下有多少子目录。

    所以硬链接通常用来进行路径定位,采用硬链接,可以进行目录间的切换。但作为用户,linux 不允许用户为一个目录建立硬链接,因为这样会导致文件系统出现环路问题,但目录内的两个隐藏文件的本质也是一个环,只不过那是操作系统的设计者建立的硬链接,操作系统设计者可以通过硬编码完成这件事,但并没有给操作系统的使用者这个权限,root 用户也不具备该权限,所以给目录建立硬链接,我们用户无法完成。

    环路问题:当我们给一个目录 dir 建立链接 hard-link 之后,我们 find 查找一个普通文件时,查找的路径上如果碰到了dir的硬链接,那么就会指向 dir,然后就又回到了 dir 目录开始往下找,这就是环路问题。同样的,假如你 find 查找 .. 这类文件时也会导致环路问题,所以操作系统在搜索时不对目录下的两个隐藏文件做搜索。


如果感觉该篇文章给你带来了收获,可以 点赞👍 + 收藏⭐️ + 关注➕ 支持一下!

感谢各位观看!

相关推荐
虾..2 小时前
Linux 软硬链接和动静态库
linux·运维·服务器
Evan芙2 小时前
Linux常见的日志服务管理的常见日志服务
linux·运维·服务器
hkhkhkhkh1234 小时前
Linux设备节点基础知识
linux·服务器·驱动开发
HZero.chen5 小时前
Linux字符串处理
linux·string
张童瑶5 小时前
Linux SSH隧道代理转发及多层转发
linux·运维·ssh
汪汪队立大功1235 小时前
什么是SELinux
linux
石小千5 小时前
Linux安装OpenProject
linux·运维
柏木乃一5 小时前
进程(2)进程概念与基本操作
linux·服务器·开发语言·性能优化·shell·进程
Lime-30905 小时前
制作Ubuntu 24.04-GPU服务器测试系统盘
linux·运维·ubuntu
百年渔翁_肯肯6 小时前
Linux 与 Unix 的核心区别(清晰对比版)
linux·运维·unix