这次博客我们将重点理解Ext2文件系统。
首先我们要理解什么是文件系统。
在之前我们一直理解的文件都是一个被打开的文件,而os为了能够管理这样的文件创建了struct_file这样的结构体对象在内核中描述被打开的文件,这个结构体对象中包含了被打开文件的基本属性,和绝大多数的属性(文件的大小,文件的偏移量,文件的权限,以及和内存块相关联的内容),包括对应的缓冲区。以上都是描述的被进程打开的文件的状态,那么没有被打开的文件?
对于没有被打开的文件(磁盘中储存的文件)我们要关心下面几个关键。
等等。
下面我们的讲解的阶段:
我们之前一直有一个概念那就是文件 = 内容+属性。
同理磁盘上的文件一样是这样的。
在Linux中对于文件的内容使用的块式的储存,而文件的属性在Linux中使用的是inode储存。inode是一个差不多128字节的数据块这里面包含了所有文件的属性。
最终的结论就是在Linux中磁盘中的文件,内容和属性是分开储存的。
具体怎么分开,分开又是怎么关联的后面会说明。
首先我们要回忆一下一个文件的属性有哪些呢?
文件的拥有者,所属组,文件的创建修改时间,文件的名字都是文件的属性。而使用cat <文件名>打印出来的内容就是文件的内容,只不过在Linux中文件的内容和属性是分开储存的,并没有储存在一起。
磁盘的物理结构(CHS地址)
下面我们来学习一个硬件---磁盘。
在大概10多年前的笔记本,使用的是硬盘,其实就是磁盘,而现在的笔记本使用的就是ssd(固态硬盘)。我们现在使用ssd的储存效率比一般的磁盘要高。固态盘比较小,没有磁针等等的概念,ssd是通过硬件电路的方式来进行储存的。固态硬盘虽然小读取效率高,但是存在缺点那就是每次对ssd中的文件进行一次读取和写入,ssd的生命就会缩短一些。所以ssd在写入/读取一定的次数之后,可能会出现存储击穿的问题。但是现在的ssd也足够我们进行正常的去使用了。今天我们这里的主角并不是ssd还是我们的机械磁盘,虽然ssd和磁盘都是永久性的储存介质,但是磁盘便宜并且容量还很大。并且很多大型的互联网公司依旧是使用的磁盘作为服务器的内容储存装置,并且在长期内不会被取代。
当然也存在混盘的电脑(ssd和磁盘共用),这种电脑应该不会特别的多,同价位下,比储存空间比不过全磁盘的,比效率比不过ssd。但是混盘比起ssd要成本要低一些,最低的是磁盘。
首先磁盘在电脑上是唯一的机械设备(这里的电脑指的是10年前的笔记本,后端的服务器,现在的部分台式机)。
大公司虽然使用磁盘作为储存的设备,但是在大公司中还存在自己的储存集群,可能有几百台/几千台电脑没有磁盘全是ssd,而在后端如果还是使用ssd作为储存设备,俺么成本就太高了,所以在建设机房的时候,也会存在基于磁盘的储存集群。大公司的逻辑就是1面向用户的请求的一般使用的就是ssd,而对于有些数据你很多长的时间都不会访问,那么对于那一部分数据就可以迁移到磁盘的储存集上。此时的效率也不慢,公司的成本也处于可控的范围内,并且用户一般是不会访问这些数据的。没必要将这些数据还放到高功耗,的储存集群上,这样的操作在机房建设上被称为储存分级/冷热分离。
下面我们开始正式的认识磁盘。
磁盘是既是一个唯一的机械设备,也是一个外设,更具冯诺伊曼体系结构,本来外设的速度比起内存和cpu来说就很慢,而我们的计算器全程是电子计算机,即在内存内部时,信息是以光速来传递的,所以我们的os就会针对于磁盘做出一些列的算法进行效率的优化。
下面是一个磁盘的基本的构造:
如果没电了磁头就会摆动到磁头停靠点
在运行写入/读取数据的时候磁头会左右摆动,而磁盘会被主轴带动顺/逆时针旋转(为什么会这样运动下面会说明)。除此之外中间是存在还几个盘面的,每一个盘面的上下两层都是具有磁头的。即磁头是一面一个的,虽然磁头是一面一个的但是这些磁头(一面的两个)在移动的时候是整体移动的。
下面这样:
虽然磁头和盘面距离非常非常的近,但是磁头和盘面是没有接触的。如果磁盘和盘面是接触的话,那么刚运行差不多一秒钟整个盘面就被刮花了。这后面的原理我就不说明了。所以在过去的时候,内部搭载磁盘的电脑是不适合我们去搬动电脑的,当电脑在开机或者是运行期间你去移动/抖动你的电脑,磁头就可能因为惯性,和盘面进行了短暂的接触。一旦接触了就可能会刮花我们的盘面,而在盘面上储存的是数据,所以一旦刮花了,就可能在物理上影响了在磁盘上的二进制数的排列情况,进而导致数据的缺失。此时就不是软件问题而是硬件问题了。所以在那个年代,笔记本的使用必须是轻拿轻放,原因就在于这个。
但是笔记本的其中一个优点不就是使用便捷么?所以磁盘从便携式电脑中退出是一个必然的。但是如果你的一直都是不搬的,比如台式机/服务一起一般是使用磁盘的。这就是为什么我们现在的笔记本比较适合ssd(固态硬盘的原因),其实不仅仅是效率的问题,因为效率的问题可以通过软件进行一定的缓解,真正的让磁盘不适合笔记本的原因还是磁盘的技术问题。
除此之外 一般的磁盘是像下面这样被封装了的。
而技术人员在封装磁盘的时候,也是处于一个无尘的环境的,原因是磁头和盘面的接触太近了,当在磁盘 运行的时候,很可能灰尘都会导致磁头和盘面进行了接触,所以磁盘里面的类似光盘一样的东西是在无尘的环境下被包装的。并且包装完之后的密封性也是非常好的。所以当你在有灰尘的区域将磁盘拆开之后,这个磁盘也就不能再使用了,除非你将磁盘重新返回给厂家让厂家帮助你把磁盘的数据读取出来。此时你的这个磁盘数据虽然还在但是基本是不敢使用了,一旦你自己使用那么数据就可能没了。
然后在上图的花圈的那里就是外部的设备会对磁盘进行写/读取数据的操作。当对应的数据传入到磁盘之中后,在磁盘内部对应的二进制数据1,就会通过对应的磁头对盘面进行充放电(怎么进行的充放电下面会进行说明),就会将对应的数据写到盘面上。那么抛开现象看本质我们所有的数据最终都是保存在我们对应的盘面上的二进制序列中。及以前的电脑上的数据,无论是什么形式(游戏数据,音乐,视频)最终都是以二进制序列的形式储存在盘面上。
计算机只认识二进制,但是在计算机中存在键盘,磁盘等等既然计算机只认识硬件那么是不是其它的硬件也只认识二进制呢?是的。原因在于每一种设备最终都是和线进行的链接。例如下面在数据总线或者是IO总线上存在光电信号的时候,我们在电脑上打印出来的光电信号在电线上表现的就是有或者无,强或者弱,或者是波形图的密集或者稀疏,不管是什么方式都是通过这种方式来传达二进制的。所以计算机只认识二进制的。所以对应的盘片也是只认识二进制的。
内存叫做掉电易失存储介质。而硬盘被叫做永久性存储介质。也就是断电之后,内存中的数据瞬间就消失了。所以内存是需要不断的充放电来维持内存中的数据的。而磁盘就不用管,数据一直都是在的。想要磁盘中的数据消失除非你将磁盘直接销毁。
我们可以将磁盘想象成由无数个小的吸铁石构成,然后刚开始,所有的吸铁石表现都是s在上n在下(全0),而当写入数据的时候,磁头会移动到对应的吸铁石上方进行充放电的处理,让这个吸铁石表现为s在下n在上也就让这个吸铁石表现为1了。此时就相当于我们在某一个位置写入了一个二进制的1。如果我们让1w个吸铁石改变了磁级的方向这就相当于我想磁盘中写入了一个01序列。我们可以这么简单的去理解。即磁头可以通过电气特性对盘面上写入我们人为设定好的01数据的。当然不同的硬件对01的表现也是不同的。 所以在磁盘中写入了你要的数就相当于你往磁盘中写入了足够多的01序列。那么如果我们想要销毁磁盘中的数据要怎么做呢?类比到吸铁石上如果这个吸铁石今天我不想要他了,我要如何做到让他消磁呢?磁铁要消磁的方法是将磁铁放到火炉中烧上10分钟,拿出来就没磁了。同理磁盘你也可以用火烧。也可以销毁磁盘中的数据,但是这样做的成本实在是太高了。我们对应的磁盘和ssd是具有使用寿命的,我们设想一下一个大型的互联网公司,有着成百上千万的磁盘如果某一天这个磁盘的使用寿命达到了。此时的这些待淘汰的盘中的数据已经拷贝到新的盘中了。难道之后我们就能够任意的将这些待淘汰的盘中的数据卖了吗?
肯定是不能如果直接售卖可能会导致国内的数据严重的泄漏。所以在我们国家对于大型互联网公司淘汰磁盘是具有标准的。在淘汰磁盘的时候必须对磁盘中的数据进行擦除。直接对磁盘进行销毁的方式是不可行的,因为一块磁盘的价格虽然对比于ssd是低的,但是依旧是具有成本的。所以大型的互联网公司会对淘汰的磁盘进行数据擦除之后在进行售卖。而硬盘的数据擦除的方法,是存在软件方法的。但是使用例如Linux中的rm -根目录的方式是不能将磁盘中的数据做到完全删除的,因为文件系统是不允许你对文件做深度式的二进制写入的。我此时改的只是一些属性数据,让os无法运行了而已。即便你对一块磁盘进行了写0写1了(进行了数据清除),但是如果有人拿到了你的磁盘并且有意恢复磁盘中的数据的话,根据你残留的数据来做到局部数据的二次恢复。而这些在你将磁盘上的数据全部清0之后,还残留的数据我们称之为磁盘上的影子数据。所以最好的方法是和磁盘的厂商去商量,让磁盘厂商提供接口做到自动一键式火化,让磁盘自己给自己写0。
以上我们都是从物理构成的角度去理解磁盘,下面我们就来理解一下磁盘的存储构成,即磁盘是1怎么做到存储数据的。
下面的这些图片是将磁盘中不重要的部分去掉之后的图片:
其实在我们的磁盘中写入和访问数据最重要的就是两个东西一个是磁头一个是盘面。(盘是进行告诉旋转的,磁头是左右摆动的)。
在一个盘面上储存的数据肯定不是乱放的。这里会以主轴为中心往外辐射处很多的同心圆(这些同心圆都是存在宽度的)如下图:
在一个盘面上围绕着中心轴形成的一个同心圆我们称之为一个磁道。
例如上图中黄色的这些就是磁道。总结就是一个同心圆就是一个磁道。
那么我们的数据在哪里储存的呢?
看下图:
就是在上图中的红色划线部分储存着的,就是我们需要的数据。其实同心圆是很多的(这里的一个绿色圆就是一个同心圆),但是为了便于观察,这里就没有显示很多的绿色的圆圈。然后这个红色的储存区域是一个扇形区域(放大了看),然后我们将这个扇形的区域划分成一个又一个小的区域,这些小的区域也就是扇区。
下图:
在硬件上磁盘被访问的最基本的区域是扇区。
然后我们就能在扇区中进行写入了,一般扇区是512字节,但是也有是4kb大小的存在。哪怕你只是想要修改扇区中的一个字节的数据,但是os依旧会将一个扇区中的数据全部读到,这是一种提前读取的操作。
下面如果我们需要找到一个特定的扇区,那么就需要进行下面的步骤。
确定在哪一个扇区需要首先知道在哪一个磁头,在哪一个磁道最后确定在哪一个扇区。定位哪一个磁头对于os。
以上我们是从硬件的角度去看硬盘如何储存数据的。
下面我们看下面的这个图片:
这里存在一个一摞磁盘面,每一个磁面都是一样的大小。我们把同半径的磁道放到一起上右图, 此时我们从侧面去看,此时这个很多个磁盘面的同半径的磁道就形成了一个柱面,这就是柱面的概念。 而这个柱面(上图右边的一个黄圈其实就是磁道 )只不过这里是立体的而已。
从中我们就能够知道对于硬盘而言,有了下面的结论
然后我们之前还知道一个结论:那就是文件 = 文件内容+文件属性->磁盘上储存文件 = 存文件的内容+存文件的属性。
那么磁盘为什么慢呢? 首先确定在哪一个磁头的速度是很快的,但是之后磁头要左右移动寻找在哪一个磁道,当定位到磁道之后,下面就是确认磁道的位置,(通过盘面自转来寻找的),最后根据磁头的电器特性来找到对应的数据,所以如果我们在此面上储存数据的时候乱存那么储存的效率只会更低。
但是磁盘因为设计的原因,查询的速度一定是会很慢的(原因上面),所以对于磁盘中的文件而言,文件的属性和内容储存的位置是很近的。原因如下:对于磁盘储存而言高效和低效的分别:
总结一下如果你想从物理结构的层面去访问一个扇区中的内容你需要找到三个东西,第一个就是磁头的位置,第二个就是磁道的位置,最后通过盘面的旋转你就能够找到你所要寻找的扇区。这种寻址方式我们叫做CHS寻址方式。
这是硬件级别寻找扇区的结论方式。
磁盘的逻辑结构(LBA地址)
下面我们来对磁盘本身进行逻辑抽象即磁盘的逻辑结构。
虽然在os中虽然也用了CHS的概念但是使用的少,主要使用的是下面的这种方式。
首先我们应该知道一种东西磁带。
磁带中间存在两个类似于滚盘的东西,缠上了磁带,使用复读机读取磁带上的数据的时候,一边两边旋转,然后一端上的磁带会越来越少,当读完之后,你翻个面,就可以继续从头开始读取数据了。
那么这个磁带和我们现在正在讲解的磁盘有什么关系呢?
如果我们将所有的磁盘的磁片都想象成一个圈,是不是就和磁带上的一个圈类似。
然后下面我们将这个磁带延展开。
当我们将全部的磁片都看成是一个线性的之后。在这个线性空间中可能某一段位置就是第一面的数据,然后某一段就是第二个盘面的是数据。
这下面的第一个小框就是第一个盘片的数据,然后第二个框自然就是第二个盘片的数据,以此类推。
然后下面我们将第一个盘面在往下看,每一个盘面中都是存在很多个磁道的,这里我就画一个图。
后面就是类推的这样的逻辑图,而在一个磁道中又存在无数个扇区。
这是其中一个磁道,然后后面的磁道一样是这样的。
此时我们就将一个物理状态的磁盘建模成为了一个逻辑状态的磁盘。此时的盘片(磁头),磁道,扇区就被建模到一个线性结构中了,这就是完成对磁盘的建模。
然后我们继续将这个线性的空间全部分解成扇区,此时我们就会发现一个由扇区构成的一个数组
假设这里存在一个10000的扇区数组,现在我们得到了一个扇区数组的下标,但是磁盘只认识CHS啊,所以这里需要进行转化。
那么这里我们能否反向从下标得到在哪一个盘面,哪一个磁道哪一个扇面呢?当然可以。
同时因位磁盘物理半径的原因里面磁道中的扇区数量是少于外面磁道的扇区数量的(不均匀的)。但是现在的磁盘技术是能够做到苏哦有磁道中的扇区数量是一样的(可以控制外面磁道的01序列疏密一些即可)。这里我们假设每一个磁道中的扇区数量是一样的。
现在的技术也能接收这种外面接收这种外面盘面上的扇区数量多一些了,只不过是需要调整一下查找算法而已,
这里举一个转化例子:
现在假设下面的一个条件:
然后我们就可以经过简单的计算换算到下面的结果:
上面换算出来就是在第一个盘面,22号磁道,在88号扇区中了。通过CHS也就能够访问磁盘上的数据了
当让你也可以通过CHS转化成为一个下标。
这里就完成了
而这个数组下标也就是逻辑扇区地址在系统中也被叫做LBA地址,LBA地址和CHS地址是可以进行相互转化的。
这样就有一个什么样的好处呢?
好处就是os不用去管磁盘的物理结构,而是只将磁盘看作是一个线性地址。
访问磁盘
下面我们再回归到硬件:不仅仅cpu中有寄存器,其它设备(外设也有),磁盘也有。磁盘当中存在很多寄存器。
当cpu访问磁盘的时候cpu是需要向磁盘下达指令的,
第一个你需要向磁盘中的控制寄存器中写入r/w代表cpu访问的本次目的是向磁盘中写入和是读取数据。即控制寄存器控制的是IO的方向的问题。(cpu放送的是磁盘能够识别的二进制命令)
如果是要往磁盘当中写入数据的话,那么将要写入的数据也要放到数据寄存器中(可能是具体的数据,也可能是某个数据的物理地址),然后因为你是要往磁盘中写入数据肯定也要具有写入的具体的地址,所以还是存在第四个寄存器(地址寄存器在这个寄存器中储存的是LBA逻辑地址)
然后磁盘就会将这个数据从内存/其它的硬件/软件,写到你对应的LBA转换的物理地址上。此时的这个数据就被写到了某一个扇区中。
而在一开始的时候,在磁盘的状态寄存器中储存的是未就绪状态,而当数据已经被写到磁盘物理地址上的时候,状态寄存器中的状态就会被修改位就绪状态。
。所以当某一个进程在访问磁盘的时候,就可以借助os去检查状态寄存器中的内容,得到磁盘写入的结果是成功还是失败。
总结就是:控制寄存器中是对磁盘进行读还是写的操作,而数据寄存器在写磁盘的时候,储存着要写入的数据,在读磁盘的时候储存着你要读取的数据,地址寄存器储存着你要写入的地址,而最后的状态寄存器中储存着本次读取/写入是否成功,如果成功了os会检测到然后返回给进程,如果们没有成功os也会检测到最后也会返回个给进程。
那么如果你要写入的文件数据很多很大,如果是在很多年前,那么只能通过数据寄存器慢慢的写入,但是现在的磁盘的数据寄存器中是可以储存你要写入数据的地址的开始和结束的,并且现在的cpu已经不在负责IO了(最多只是下达IO任务),现在的计算机中存在一种芯片是DMA这个芯片是专门用于数据IO的工作。虽然如此但是写入数据的过程还是很慢的,但是这个不是最慢的吗,最慢的应该是在往磁盘中写入数据的(磁头通过电气特性往盘面上写数据的过程)过程才是最慢的。
以上我们只需要知道对应的硬件也是可以被访问的。
到这里我们已经完成了对磁盘的物理结构,储存结构,对磁盘的建模,知道了CHS地址和逻辑扇区地址(LBA)。
此时os就能和内存地址一样去管理磁盘上的地址了。
下面我们开始引入文件系统:
文件系统的基本知识
那么现在磁盘就是一个线性的结果假设现在这里的磁盘大小位800GB。这个800GB的大小是很大的那么我们OS要如何去管理这个800GB的大小呢?在解决这个问题之前我们在思考一个问题。
我们知道磁盘中的文件也是具有内容和属性需要被保存的,但是现在我们怎么知道在一个线性的磁盘中哪些扇区中保存的是属性哪些扇区中保存的是内容呢?所以为了解决这个问题必须引入一个概念,这个概念我们待会去说明。
现在我们这里存在一个800GB的线性空间,首先肯定还是需要将这个800GB的空间给他分解开(空间很大是难以管理的,正如国家的股管理)。让其变得好管理。
首先我们将这个800GB的大空间不断分解下来:
然后800GB的空间不好管理我们来管理200GB。
那么这个800GB的空间被划分,OS是如何做到哪一个区是200哪一个区是100的呢?原因在于每一个计算机都机会会存在的功能分区(C盘,D盘)。这个工作就叫做分区,如何分区呢?在os层面上分区是很简单的只需要做下面的事情
首先定义一个结果体,里面存在两个字段开始和结束,然后你要分成5个区域的话,就存在一个5大小的数组,然后将数组中的第一个对象设置起始扇区在哪里结束扇区在哪里,此时第一个结构体对象完成了赋值同时也就完成了第一个分区(这里假设就是200GB,然后后面的100GB,150GB也是这样的分法),这种方法我们在虚拟地址空间的时候就学过,所以在os层面要划分磁盘很简单,只需要设置起始和结束扇区地址就可以了(LBA地址的开始和结束)。此时就算你的这个200GB,前150G在一个盘面上后50G在一个盘面上也没有影响,在LBA地址转化为物理地址(CHS地址)的时候,自己会改变磁头的。此时总结就是只要os那道了这个磁盘的磁头数量是多少,每个盘面存在多少磁道,每个磁道中的扇区数量是多少,就能够完成对于磁盘的数学建模的过程,同时os也能自动的完成对于磁盘的分区。在现实生活中因为很多原因你将某一个省管理好了,但是了另外有一个省你不一定能够管理好因为每个省之间会因为种种的原因出现差别,但是在计算机中你200GB和150GB和100GB是没有区别的,里面都是扇区(只是数量不同),储存的介质都是磁盘。所以此时我们只需要将200GB管理好了,然后将这个管理方式赋值粘贴到其它区域一样能够将其它区域管理好,每个区域管理好了你的整个磁盘就管理好了。
但是200GB也是很大的也不好管理。
此时就会出现一个东西:
上面的蓝色框就是200GB,然后这个蓝色框中的第一个字段也就是Boot Block中里面储存的就是OS启动时的相关信息(例如os在什么位置,你这个磁盘一共被划分成为了哪些分区,起始分区是什么结束分区是什么 )。
也就是这个字段是和开机有关的字段,在首个分区的首个字段中就存在了。理论上这个Boot Block是在0号磁头,0号磁道的1号扇区上放着, 理论上只需要存在一个分区中储存着Boot Block即可,但是在其它的分区也是可能存在的。因为001的扇区是能够被直接访问到的,但是如果有一天这个Boot Block挂掉了那么计算机就起不来了,此时的文件系统就挂掉了(主要就是os无法启动了)此时就可以使用一些修复工具将其它分区中的这个字段的内容拷贝回来,来达到对计算机修复的目的。但是这里我们不需要深入的理解开机关机。
这里我们注意的是200GB也是很大的所以os会将这个200GB继续往下划分。划分成无数个Block group的空间,
这里就是将这个200G的空间继续划分成为一个一个的Block group。那么此时就能够在将200G继续往下划分了,假设这里划分成位了10GB这里就有20个group。
此时我们只需要将这个10G的空间管理好,那么200G就管理好了,200G管理好了那么整个磁盘也就能够管理好了,那么此时整个的任务就从你要管理好整个磁盘变成了你要管理好这10G的空间。管理磁盘的结构(也就是块设备结构,在os中就是一个结构体,里面包含了磁盘的基本信息,和一些其它的信息)。 那么现在我们就要来看一个Block group中有什么,而这个Block group中的东西就叫做文件系统。这个管理方式使用的思想也就是分治的思想。(把大问题分解成小问题)
那么下面我们只需要理解一个Block group的内容就能够理解磁盘了。
首先就是整个分组的构成是分成下面的几个部分的。
ext4文件系统的分区
首先是Super Block里面保存的大多都是和文件系统相关的信息。第二个描述的是整个分组的情况然后剩下的字段就是存数据和属性的字段,最后一个Data blocks是存文件内容的区域。
Data block,inode Table,bit map区域
在上面的这个区域中可能就是存在1w多个扇区,虽然磁盘访问数据是以扇区作为基本的单位,但是文件系统在访问的时候,通常是将多个扇区的内容放到一起作为一个块来读取的,这也就是文件系统的块大小。也就是说,在磁盘中新建一个文件,哪怕你只是往这个文件中只输入1kb的内容,但是在文件系统中也要在某一个分区中,找到某一个快组,然后给你分出一个块(也就是8个扇区大小的空间,每个扇区是512字节)直接给你。磁盘访问数据的基本单位是512字节,而os访问数据是以块位基本单位访问的(4KB),同时写数据也是在往磁盘中写入8个扇区的内容作为一个基本单位(块)。我们待会说明为什么要以块的形式访问/写数据,在某些情况下,我要读取和写入的内容很少,但是给的空间却很大(难道就不能需要多少取多少么?待会说明)。首先任何一个文件只有最后一个块会存在浪费空间的问题,如果你要写/读的文件很大(2GB/1GB),那么前面的块写/读都是没有浪费空间的,只有最后一个块可能会出现浪费空间的情况(除非你写/读的块大小只有一个),所以空间浪费还好。还有一个原因后面会说明。所以cpu访问磁盘中的数据是以块大小为基本单位的,然后在Linux中mke2fs -b选项可以设置块的大小。
假设这个data block中存在15000个扇区那么他存在多少块呢?首先总的字节数就是15000*512字节(扇区一般是512字节),然后求出一个块的字节数就是4096.将两个数相除就能得到这个data block中存在的块的数量。此时的这些块也就存在了编号。
然后下面我们来看inode Table,要理解inode Table就要首先理解什么是inode。
由此可以知道在一个扇区中就至少存在4个inode。这里我们需要知道一个文件的所有的属性就在inode里面存,而所有的内容就在data block里面储存。
那么inode Table也就意味着一定会存在着多个inode。也就是没有打开的一个文件的很多属性都是储存在inode中的。所以当你打开文件的时候,你首先要做的是查看这个文件的属性,也就是要找到这个文件的inode。因为存在很多个文件一定会存在很多的inode,由此不同的inode也就存在了自己的编号。所以当我们新建立一个文件的时候,一定会在inode Table中新建立一个inode里面储存着这个文件的所有的属性。然后如果这个文件需要空间就去data block中取申请块。如果这个文件很大的话,那么就会存在很多个块都储存这个文件的内容(也就是一个文件对应的块中只会存在这一个文件的数据)。然后这个文件具有多少个块也是一种属性,所以inode和块之间也是存在某一种联系的。
这个联系我们后面会说明。但是由此我们也就能够知道了在Linux中属性和文件是分开储存。
这个inode就是一个结构体
当然属性肯定不止有怎么几种,还存在很多的。每一个文件都会存在一个inode对象,这个对象中储存的就是这个文件的所有的属性,然后将这个inode对象放在了inode Table中。所以一个文件存在一个inode和可能存在多个块,以及在inode Table中每一个inode都有自己对应的下标 。
下面我们需要知道一个知识点:
也就是在os层面上这个文件的名字是什么,os不知道,os只会关心这个文件对应的编号是多少。在Linux中使用ls -li选项就能够看到一个文件的inode编号。
例如上面文件夹最左边的数字就是这个文件夹对应的inode编号。
这也就能得到一个结论那就是在Linux系统里面标识文件使用的是inode编号。
现在对于一个文件而言,我们只需要拥有这个文件的inode编号就能够得到这个文件的属性,但是我要如何得到这个文件对应的块呢?所以在文件的inode结构体会存在一个数组(int blocks[NUM]),这个数组根据系统的不同大小也是不同的。
然后在data block中每一个块也是存在编号的,那么在使用的时候,一个文件会含有一个inode对象,同时可能存在多个块。每个块都是具有自己的下标的。然后这个文件对应的块的下标就会保存在blocks这个数组中。此时我们就建立了属性和内容的关系。从此之后我们只需要知道一个文件对应的inode 下标就能够找到这个文件的inode对象,然后在inode对象中就能够找到这个文件对应的块下标,找到了这个文件对应的块的下标也就找到了这个文件对应的内容。但是这中间存在一个问题,这个block数组里面保存的是数据块,那么也就是一个数组最多只能指向15个block,也就是一个文件大小只有15*4 = 60kb大小吗? 难道一个文件最大只有60kb吗?当然不是。
首先这里我们将这个data block这个区域放大一下(即便这里整体的空间大小只有10GB):
中间的这些小框就是对应的一个块(每个块都存在自己的编号)(1个块拥有8个扇区),现在在inode中存在一个数组。在这个数组中存在一些下标,这个下标也就是这个文件对应的块的下标。当要读取文件的时候,能够直接根据数组中的块号下标,找到对应的块。但是实际上这个block中有一个规定 ,这个数组的前面12个空间都都是直接索引(保证这些数据块里面都是文件本身的内容)。然后从下标12开始到14.我们称之为两级索引(里面存的块号这里假设是200和201)。当os将这个数组下标的前12个块中的数据都读取完毕之后,发现后面下标中还存在内容并且是有效的,然后我们根据200和201就能够找到对应的快号。然后我们根据这两个块号中存的不是文件的内容,
即现在我们又拿2个块号(16个扇区也就是16*1024字节去储存文件的块号,一个块号我们按照4字节算就还能够储存16*1024/4个块号),然后就会继续在这个表中进行索引:
此时只是增加了这两个下标我们已经能够保存的文件大小就是8兆b左右了。
然后数组的最后一个位置中储存的内容也就是二级索引。假设里面储存的是400,依旧能够得到一个块号这个块号里面能够得到一些新的块号的下标,同时还规定这些新的块号里面也只能保存另外一些新的块号的下标(不能保存文件内容)此时我们就能够创建更大的文件了,
此时能够保存的文件的大小已经很大了。
下面我们再来看一下Block Bitmap现在我们面临一个问题假设在data block中存在2w个块。我们申请和释放块的操作是很频繁的。那么
由此我们就存在了一个位图,假设这里存在15000个块在Block bitmap中就会存在15000个比特位(1875字节)。
然后
此时通过这个位图我们就能够轻松的知道某一个块是否被使用过了,由此我们就能够知道一个知识点了。
答案是不用了,当我们要删除一个文件的时候,我们只需要通过这个文件的inode编号,将这个文件对应的Block bitmap中的某个比特位给清空即可。就完成了删除。即删除文件我们只需要将这个文件对应的位图中的某个下标内容清空就能够完成删除了。这就是为什么当我们拷贝大文件的时候,很慢而删除却很快的原因了。那么同理inode bitmap也就是一个作用了,一个计算机中会存在多个文件也就存在多个inode,通过inodebitmap也能够快速的判断这个文件的inode是否有效,同理删除时也只用修改inodebitmap中的某一个比特位就可以了。
所以我们要删除一个文件只需要通过文件的编号,通过inodebitmap知道这个文件的inode是否时有效的,有效就去inodetable中读取这个文件的属性,获得inode和数据块的映射关系,读取所有的块号,然后在blockbitmap中将全部的块号对应的比特位清0。在根据inode编号将inode bitmap中对应的比特位清0即可完成文件的删除。所以如果你在Linux中将一个文件删除了,你也是可以通过一些方法将文件恢复的(知道被删文件的inode编号,在inodebitmap中将对应的比特位由0改为1,再在inode table中找到和块号的联系,通过联系找到块号,再在block bitmap中将对应的块号映射的比特位由0改为1)此时文件就被恢复了。所以在Linux中误删了文件之后,可以通过inode编号是可以恢复的,但是恢复一个文件是需要很多专业的工具的。所以一般不推荐恢复。(在Linux中你删除了inode,之后inode编号就没有了)但是在Linux的日志中还是存有这个文件的inode编号的。
Superblock Group Descriptor Table
然后还有两个块:
- 超级块(Superblock):超级块是文件系统中最重要的数据结构之一,它记录了整个文件系统的基本信息,包括文件系统的大小、块大小、inode数量等等。在文件系统挂载时,操作系统会首先读取超级块,然后根据超级块的信息来进行后续的操作。即在这个块中储存了整个文件系统的块是如何分的,不同组间是如何区分的信息。这个超级块不会再每一个组中都有而是零星的在很多组中存在几个,如果出现了问题,从其它的地方拷贝到最前面的那个组的superblock即可。
- 组描述符表(Group Descriptor Table):组描述符表是由多个组描述符组成的表格,每个组描述符对应一个数据块组,它记录了该组的基本信息,包括该组中的数据块数量、inode数量、空闲块数量等等。在文件系统挂载时,操作系统会读取所有的组描述符表,并根据其中的信息来维护文件系统的状态,例如分配和释放数据块、inode等。即这个区域中描述了整个文件系统的使用情况。os会根据这里面的信息判断是否继续在这个文件系统中创建文件。例如这个文件系统中已经写入的文件是20%等等的信息。都会在这个区域中
总之,超级块和组描述符表是Linux的ext2文件系统中非常重要的数据结构,它们记录了文件系统的基本信息,并且在文件系统挂载和使用时发挥着重要的作用。
对于文件系统的剩余知识我会在下一片博客写出。
希望这篇博客能对你有所帮助,如果发现了任何的错误,欢迎指出。