【Linux】不允许你还不会磁盘的结构和 Ext2 文件系统的知识
磁盘的结构和解析、Linux文件系统(ext2)知识、硬链接、软链接

目录
文件分为:被进程打开的文件和没有被进程打开的文件。
那么没有被进程打开的文件是存储在磁盘的,而且磁盘级的文件是由文件系统决定的。
那么要学习文件系统就必须要理解磁盘,
一、理解磁盘



磁盘分为机械磁盘和固态磁盘(SSD),固态磁盘价格贵,但是体积小,容量相对于机械磁盘较小,一般用于大众,而机械磁盘容量大,体积大,价格便宜,一般用于企业的后端开发。上面这两种磁盘都是外设,既然是外设就注定了他们在冯诺依曼体系里面是访问速度是非常慢的。下面我们要谈论的都是机械磁盘;
磁盘里面是密封的,一旦有灰尘基本上就报废了。
磁盘外形像块板砖,里面有个圆形的磁盘,这个磁盘是由多个磁铁构成的,当我们输入数据的时候,本质就是改变磁盘上的多个磁铁的正负极。
国家要求互联网公司必须要把磁盘上的数据清除,防止公民的信息泄漏;那么怎么清除数据呢?
答:消磁(高温消磁)或者通过软件把磁盘上的所有的磁铁的正负极改成0;
圆形的磁盘上面有个磁头,这个磁头在磁盘的上面,他们是不挨着的,因为如果挨着就会高速摩擦生热,此时这个磁盘就报废了;所以我们多年前使用的电脑如果不小心摔了,就会导致磁头震荡,一不小心就会与磁盘接触,导致磁盘高温消磁。
在上面的图片中,一个盘片可以正反面都涂上磁性材料,所以这里一共有6个盘面,对于6个磁头,其中3个是 小磁头。
注意:离圆心越近的扇区和越远的扇区他们的存储数据的大小是一样的,只不过是磁性材料的疏密程度不一样罢了。
因为所有的磁头都连接机械摆臂所以所有的磁头是共进退的(摆动方向一样)。
磁头摆动的意义:本质是访问哪个磁道或者柱面。
盘片旋转的意义:定位到指定扇区。
我们怎么往特定的扇区写入数据?
答:先找到对应扇区所在的柱面,其次再找磁头(相当于是找扇区在哪个盘面上),最后再找到扇区,这称为 CHS地址定位(由于这种方式定位扇区的数字太多所以在OS层面不显示,而是以 LBA地址的方式显示)。
文件 = 内容+ 属性,这些都是数据,我们要想找到文件的数据就是在找文件存储在哪个几个扇区里面。
LBA 地址:
因为磁道是一个个的圆圈,把一个圆圈在任意位置剪开就是一维数组:

这个数组的下标(从1开始)就是LBA。
同一个磁道的不同磁面可以构成二维数组:

磁盘的本质就是一个三维数组,其中的一个一维数组用来表示磁头。CHS就是三维数组的下标。
上面柱面的三维数组的下标和我们学习C语言的不一样,而是把这个二维数组拉成一个一维数组从左到右是从1逐渐增加的,是连续的。

因为这个磁盘是由多个柱面构成的,所以后面的柱面是接着前一个柱面的一维数组的,下标是连续的。

这个一维数组的下标就是 LBA,那么怎么从 CHS 转换成 LBA,这个工作是磁盘的硬件电路和伺服系统的做的。OS只要使用 LBA 就行。
向磁盘指定位置写入数据的完整过程?
答:磁盘是有自己的硬件电路,这个电路有一个伺服系统,这个系统有个磁盘控制器,这个控制器的内部是有几个类似于寄存器的东西,例如 r/w 、LBA、data 寄存器,而OS就可以类似于汇编的操作访问这几个寄存器,往这个几个寄存器里面写就行了,而OS就只要等磁盘控制器来告诉OS写好就行了;伺服系统就会把 LBA 转换成 CHS ,然后往 CHS 扇区里面写数据就行。
二、引入文件系统(ext2)
把磁盘所有的柱面,抽象成一维数组,这个数组里面是存储的扇区(512字节),OS读取数据是不会一个一个扇区读写的,因为效率太慢了,而是一块一块读写的,一块= 8扇区 = 4kb,块是文件存储的最小单位;
为什么操作系统不直接使用扇区来访问磁盘?
答:1、提高效率。2、软硬件解耦(防止磁盘读写按扇区变成其他,导致操作系统也要变也下)。
操作系统把一维数组进行分区,类似Windows的C、D盘,每一个区的最小单位是柱面。
我们要想要在磁盘上存储文件,就必须先把硬盘格式化为某种格式的文件系统,存储存储文件;说人话就是给磁盘一个管理者。文件系统的作用就是组织和管理硬盘中的文件。在Linux系统中,最常见是就是 ext2 系列的文件系统,目前企业用的是 ext3 或者 ext4 ,他们两个的核心设计和 ext2 一样,这里我们以 ext2 为例子。
ext2 文件系统把分区划分成若干个同样大小的块组,只要能管理一个分区就能管理所有的分区,就能管理磁盘使用的文件。

Inode tabe 里面存储的是多个文件的属性,Data Block 存储的是多个文件的内容。 Block bitmap 记录Data Block 的使用情况,他是一个位图操作;inode bitmap 里面记录是文件属性有多少个,也是位图操作。GDT是记录整个块组的信息。Super Block 存放⽂件系统本⾝的结构信息,描述整个分区的⽂件系统信息。记录的信息主要有:bolck 和 inode的 总量,未使⽤的block和inode的数量,⼀个block和inode的⼤⼩,最近⼀次挂载的时间,最近⼀次写⼊数据的时间,最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。Super Block的信息被破坏,可 以说整个⽂件系统结构就被破坏了;Super Block不是每个块组都有,只有2——3个之间,2—3个是为了备份Super Block ,防止 Super Block 的丢失。
注意:文件名不属于文件的属性,因为文件名不在 inode 内部保存。
struct inode 是描述一个文件的属性的结构体,这个结构体的大小是固定的:128字节。这个结构体在 inode Table 里面存储。
每个文件的 inode 编号在struct inode 结构体里面,inode 编号是分区内有效,而 LAB 是整个磁盘(所有分区)有效;inode 编号通过 inode 结构体里面的 i_block (指向Data Blocks里面的块)映射到 LAB 指向的扇区。
inode Table 里面也是许多的块,每个块都是 4kb ,那么一个文件的属性struct inode大小为128字节,也就是说一个块可以保存 4kb = 4096字节 / 128字节 = 32个文件的属性。
GDT 块组描述表是每个组都有的,他是描述这个块组的每个块被使用的情况,和从哪个块是 inode Table ,从哪里是 Data Blocks,空的 inode 和数据块的还有多少个等。
Super Block 是存放文件系统本身的结构信息,描述整个分区的文件系统信息。记录整个分区的block 和 inode 的总量,和未使用的 inode 和 block 单独个数等等
问题:假设我们要读取某文件的内容,怎么读取?
答:先根据文件的 indode 编号找到文件的属性,文件的属性里面有个 i_block ,他是用来和Data Block 进行映射的,记录文件内容在该文件在 Data Block 的某个位置,根据 i_block 就能找到文件存储在那个块里面,然后把文件的。
问题:如何理解文件的创建、删除、修改、查找?
答:文件的创建:我们 touch 文件时,因为 touch 是一个进程,他会在内存里为你填写该文件的属性,然后刷新到磁盘上,此时他会查找 inode Bitmap ,不是在磁盘上查,而是把 inode Bitmap 以4kb 加载到内存里面,然后在内存里面遍历 inode Bitmap ,就是遍历比特位,当遍历到0时,就会把这个0变成1,代表着文件属性多了一个,然后根据这个0的位置(inode编号)到 inode Table 来找没有存储文件属性的位置,把文件属性拷贝到这个位置上,因为此时文件内容为空,所以 touch 没有向 Data Block 申请数据块。当我们往这个文件上写点内容时,此时 touch 就会把Block Bitmap 加载到内存里面,遍历他,找到比特位为0,把他变成1,再根据这个0的位置到 Data Block 找没有被填写的位置,然后把文件的内容填写进行。
删除文件:先根据inode遍历 inode Bitmap 查看文件是否存在,再根据 inode 编号到 inode Table 找对应的文件属性内容,找到文件属性的 i_block 的编号,根据 i_block 编号到对应的 Block Bitmap 里面找对应数字 ,把数字变成0,然后再拿着 inode 编号到 inode Bitmap 里面找到对应的数字把他变成0。删除工作完成;从始至终没有把文件的属性和文件的内容删除,只是把对应的位图数字变成0就行,因为当我们创建一个新的文件时就会覆盖我们原来文件的属性和内容。
修改文件:先根据 inode 编号到 inode Bitmap 看文件是否存在,再根据 inode 到 inode table 找对应文件的属性,在文件属性里面找到 i_Block 的编号,根据这个编号到 Data Block 找到对应文件的数据块,把这个数据块的所有的文件加载到文件的缓冲区,最后就是语言层面的问题了,根据文件描述符找到文件等操作。
查找文件:先根据 inode 编号到 inode Bitmap 查看文件是否存在,再到 inode Table 里面找到文件的数据,再根据该文件的属性里面的 i_black 找到对应的的块,把这个块里面的所有文件加载到文件的缓冲区里面,再根据文件描述符找,这里就是语言层面的操作了。
问题:inode 编号是什么?
答:inode 是文件的属性,inodeTable 是 inode 集合,相当于inode Table 是存储多个的 inode 的数组,而 inode 编号就是数组的下标,也是位图操作时我们创建该文件时查看0,把0变成1的相对位置。inode 编号就是该文件某个分区的LBA。
我们查找文件或者修改文件时,都是通过文件名的进行操作的,根本没有什么的 inode 编号;所以这背后的原理到底是什么?
答:Linux下一切皆文件,目录也是文件也有自己的 inode 编号,文件=内容+属性(inode),目录文件的内容是当前目录下包含的所有文件的文件名和每个文件对应的 inode 编号;目录文件的内容也是数据,所以保存在对应的数据块;所以进程查找或者修改文件,进程是知道自己的当前路径的,所以就能找到当前目录下对应我数据块,就能根据文件名到该数据块找和文件名对应的 inode 编号。
问题:目录的 rw 权限问题
答:因为当前目录保存的文件名和对应的 inode number ,如果进程没有 r 和 w 权限,对应的文件名和 inode number 的映射关系就建立不了。
接下来我们重新谈一下 inode 编号和块号:
inode 编号和块号不是组内有效而是分区内所有的组都有效(不是所有的分区),有效指的是块组和块组之间的编号不是都是从0开始的,而是连接的,也就是说假设: inode 编号在第一组,但是第一组的 Data Block 用完了,他是可以到第二组的 Data Block 里面存储文件的数据的。
在一个分区内部 inode 和数据块的数量是固定的。
假设我们要创建一个文件,这个文件在Inode Table只占一个 inode ,但是这个文件非常大,也就是说把 Data Block 全占了,或者说创建多个文件 Inode Table 全用完了,这些文件全是小文件,此时 Data Block 还没有用完,不管是上面的那种情况,此时再创建文件,是有可能创建不出来的。
我们要访问一个文件,就必须通过当前路径的目录下拿到文件的 inode 编号,多少目录也是文件啊,我们也要拿到目录的 inode 编号,怎么拿?到上一级目录拿,上一级目录也是文件所以往上上级目录拿,知道从根目录那里拿到下一级目录的 inode 编号,也就是说当我们要访问任何文件的时候,Linux内核要为我们做路径解析,这个过程不是像我们刚刚说的那样从后往前解析,而是从根目录开始解析。结论:访问文件必须要有路径。
注意:根目录的 inode 编号开机就知道了。
也就是说我们要访问一个文件就要做一次路径解析,如果多次访问是不是要多次路径解析?
答:不是。对于用户访问过的路径,Linux是会做路径缓存的。
如果打开多个不同路径的文件,Linux缓存多个路径,要不要对这些路径进行管理?
答:当然要,因为Linux对于哪些很久都没有打开过文件的路径是要出缓存的,不然缓存爆满了怎么办。
怎么管理路径缓存?
答:先描述,再组织。用 struct dentry 结构体描述一个路径结点,这个结构体包含他自己 inode。组织使用数据结构多叉树来组织。就是当我们打开一个文件时就会创建一个 strcut dentry 结构体插入到这个多叉树里面。;如果某个路径结点使用的次数太少了,就会释放掉这个路径结点的结构体。
问题:只有目录文件才有 dentry 吗?
答:不是,每一个被被访问的文件,都要自己的 dentry ,普通文件只不过是那颗多叉树的叶子结点吧了。
我们打开一个文件,描述文件的结构体 file 里面有个 struct dentry* 指针指向该文件的路径缓存,也就是指向路径缓存多叉树的 dentry 结点,这个 dentry 里面有自己的 inode ,这样就能访问文件属性和内容了。
那么相对路径怎么弄?
答:当前路径如果存在路径缓存多叉树,到当前路径的上级路径开始往后找,如果不在那个树,我们根据进程的绝对路径,从头开始往后边找边缓存。
问题:存储大文件问题:一个分组保存不下来这个文件的数据怎么办?
答:inode 结构体中有个 i_block[ EXT2_N_BLOCK],其中EXT2_N_BLOCK = 15,也就是说一个组最多存储:15 * 4kb = 60kb,然而这个 i_block 的前 12 个指向的块也就是下标0——11指向的块是用来直接存储文件数据的,也就代表着 i_block 前12个保存的是具体的块号,而下标12也是保存具体的块号,不过他指向的 Data Block 里面的块保存的是你这个文件要缩影的相关的数据块的其他块号,也就是说因为这个块是4kb = 4096字节,假设一个块号占4个字节,那么下标12指向的那个块可以保存1024个块号,也就是说一个 inode 可以访问 12 + 1024 个块;总结:i_block[ 0-11 ] = 块号,指向 Data Block 里面块用来存储文件的数据,而 i_block[ 12 ] = 块号,指向 Data Block 里面块保存的是其他的块号。而 i_block[ 13 ] = 块号,他指向的 Data Block 里面的块保存的也是其他的块号,这里面保存的块号的指向的那个块保存的也是其他的块号,此时这里的块号指向块才能存储数据,而 i_block[14] 就是在 i_block[ 13 ] 的基础上多套一层。回答问题:如果一个分组的 Data Block 存储不下这个文件的数就使用其他组的 Data Block ,注意只能是同一个分区的组。
问题:你怎么知道你的文件在哪个文件系统中?也就是你怎么知道文件在哪个分区,因为我们要拿到 inode 编号才能找到文件,而 inode 是分区内有效,不能跨分区。这里的不能跨分区是因为假设 A 分区有个文件的 inode 编号为56,而 B 分区也有一个编号为56。
答:我们先了解一下磁盘被管理的过程:磁盘——》被分成一个个的区——》给区进行格式化,格式化的本质就是写入管理信息,也就是磁盘级文件系统。因为分区被格式化之后,这个分区或者文件系统是不能直接被使用的,要把分区或者文件系统挂载到指定目录下,然后我们进入这个目录在这个目录进行文件操作,其实就是在这个分区进行文件操作。因为我们要访问文件是要有路径的,所以而这个分区挂载在某个目录下,只要我们进行文件访问可以通过文件的路径来找到他在哪个分区。你可以理解成Windows的C、D盘,其实C、D盘其实就是目录,我们创建的文件就是在这个文件创建的,所以当我们查看文件的时候,路径的第一个目录就是C或者D盘:

三、硬链接

其中由数字1变成数字2,这两个数字称为硬链接数,就是有多少个文件名指向同一个 inode 编号。硬链接数属于文件的属性。
什么是硬链接?

答:由上图可以知道,他们两个的 inode 编号是一样的,所以他们两是同一个文件;建立硬链接的本质其实就是在当前目录下新建一个新的文件名(字符串)和目标文件的映射关系。
硬链接数的意义是什么?
答:删除文件的时候当硬链接数为2的时候,删除一个文件此时硬链接数为1,代表此时的文件还要被删除,因为还有一个文件指向同一个 inode 编号,此时再删除另外一个也就是硬链接数由 1 变成 了 0,代表着文件才被真正的删除。
硬链接的作用是什么?
答:可以备份重要文件;一般我们备份文件是拷贝一份到另外一个文件,此时就有两个文件保存同样的内容,但是这种方法太占用磁盘空间了,而硬链接相当于被一个取了个别名,大家在磁盘上存储的位置相同,空间也一样。
问题:什么目录的硬链接数是2?


答:我们可以发现当我们进入到这个目录之后,. 这个文件名的 inode 编号和当前目录编号是一样的。而 .. 表示上级目录:
所以当一个目录的硬链接数为 6 ,那么他的里面有多少个目录文件?
答:6 - 2 = 4,2:当前目录、目录里面的.目录;剩下的就是目录文件了。
所以硬链接的作用还有另外一个?
答:用来实现 Linux 的目录结构。
我们不能给目录进行硬链接,为什么?
答:防止用户造成环形路径(死循环);假设路径是:1/2/3/4,此时我们在4目录下对2目录进行硬链接,所以当前的路径是:1/2/3/4/2,那么当我们要查最后一个2目录下的一个文件时,此时路径是:1/2/3/4/2,当我们来到2时,就会打开2目录,2目录存储3目录,进而就重复,此时就造成死循环了。
那么..和.就能和目录进行硬链接呢?
答:操作系统必须这个干,因为没有这个俩个:. 和 .. 目录的话,退出当前目录就要写绝对路径太麻烦了,就是说操作系统是为了完善路径结构。那么这样的话不会造成路径死循环了吗?答:操作系统遇到 . 或者 .. 忽略这两个目录就行,因为这两个目录的名字非常特殊。
四、软链接
创建软链接的指令:ln -s 目标文件 要形成的文件

从上面我们可以看出软连接创建出来的文件和目标文件的 inode 编号是不一样的,所以他们两个是单独的文件,那么软连接形成的文件,里面存储的是什么呢?
答:如果用 vim 打开软链接形成的文件,其实里面的内容和目标文件的一样,但是实际上软链接形成的文件保存的目标文件的路径。
为什么要有软链接?
答:假设目录为:1/2/3/4/5/6/7/8,我们在8目录那里写了个代码,可执行程序也在8目录那里,此时我们在1目录下,那么我们要运行这个程序,路径太长了,所以让软连接保存这个文件的路径,此时我们只要在1目录下./软连接后的文件名,就能运行。类似于我们 Windows 的快捷键/快捷方式。
哦!对了,各位优秀的程序员觉得我博客给你带来帮助或者让你学到了知识,记得给博主一个关注哦~❤️❤️❤️
各位博友,下篇博客见🍁🍁🍁
更多推荐


所有评论(0)