linux文件——文件系统——文件系统深度理解、学习inode

CSDN 2024-09-01 08:37:01 阅读 56

        前言:本篇内容讲解文件系统的细节问题。 在本篇内容中, 我们在学习文件系统的过程中, 我们可以理解inode的原理, 理解如何在文件系统的概念下新建文件, 删除文件, 查找文件, 修改文件等等问题。 下面开始我们的讲解吧!

        ps:本节内容适合了解一些磁盘知识的友友们进行观看。

目录

磁盘逻辑结构与分区分组概念

boot block

操作系统的对磁盘的管理方式——分而治之

组里面的内容

data blocks

inode table

寻址的细节

inode bitmap

block bitmap

group descriptor table

Super block

磁盘的初始化

文件操作细节

新建一个文件

删除一个文件

查找一个文件

修改一个文件

理解目录文件

如何找到目录文件的inode


磁盘逻辑结构与分区分组概念

本片的内容我们都以一个800G大小的磁盘为例, 下面是这个磁盘的逻辑结构

这800G磁盘因为太大, 所以操作系统就可以把他们分成一块一块的, 就如同下图:

        上面的一块一块的空间, 就相当于我们使用的电脑上面的C盘D盘。 这些分区对于操作系统来说, 如何分?

其实就是一个结构体, 如下:

struct partion

{

int start;

int end;

}

        这是一个分区的结构体, 指向一个分区的起始地址和末尾地址。

        假如想要分5个区, 那么就可以定义一个数组: struct partion part[5]。 这个数组就代表着分成了五个区。 

        在本篇内容中, 我们使用的例子(开头说过的那个磁盘)被我们分成了四个区——250G, 150G, 250G, 150G

ps:我们的分区的概念其实和上面讲解的CHS寻址方法并不冲突——CHS寻址看的是盘面个数, 每个盘面的磁道个数, 每个磁道的扇区个数。

        另外, 每个分区也有更加详细的划分,如下图:

boot block

        理论上boot block是在磁盘第0面的第0磁道上的第1号扇区上面。 其他分区的第一个扇区也可能有。——而为什么会这样呢? 是因为如果有一天, 我们的boot block挂掉了, 那么我们的操作系统, 计算机整个地就起不来(图中已经写到, boot block中包含着操作系统启动的相关信息, 以及开机相关的字段), 本质上就是文件系统坏掉了, 那么它就要拷贝几份在多个分区中。

操作系统的对磁盘的管理方式——分而治之

        上面的每个分区会分成一个一个地小分区, 然后每一个分区都是一个block group。 那么, 以前大分区是250GB, 那么分成小分区后, 每个小分区可能就是10GB。那么管理好一个小分区10GB, 就能管好所有的小分区, 管理好所有的小分区, 就代表着能管理好一个大分区250GB, 那么就能管理好所有的大分区。 那么就变成了对整个磁盘地管理。

        如此, 我们就将对整个磁盘800GB的管理转化成了对10GB的管理。

        这种思想就叫做“分而治之”。

组里面的内容

        现在, 我们看一下上面的block group里面具体有什么

data blocks

        保存数据的区域, 以块的形式呈现。最常见的就是4kb——文件系统的块大小——这也就是说, 如果我们想要在磁盘中写1字节的数据, 我们就要在某一个分区里面找一个组, 然后在里面申请4kb大小的块空间。

        这个规则如果不想明白可能会觉得非常浪费空间。 但是真的会非常浪费空间吗?——答案是不会浪费很多空间, 因为每一个文件都只会浪费一个块, 为什么? 这里可以想一下, 如果一个文件几百mb, 那么文件里面的数据将会占满许多的块空间, 然后在最后一个块空间可能会浪费一点空间。 如果是一个1字节的文件, 那么也只会浪费一个块空间。 

inode table

        单个文件的所有的属性, 128字节, 一般而言, 一个文件一个inode, inode table里面有很多个inode, inode有唯一的编号。 

        一般而言, 每一个块只有自己的数据。

        我们知道, 我们的文件,可能有很多的块, 但是只有一个inode文件的属性包含在inode里(inode table就是一个inode数组, 里面是一个个inode结构体, 保存的内容就是inode编号以及文件的各个属性和文件的内容)。 文件的内容保存在块里。 而文件的内容也保存在inode里, 所以inode和块一定有某种连接关系。

        这里我们需要知道的是, 在linux中, 文件的属性中, 不包含文件的名称!!!那么我们如何找到一个文件的属性呢? 在linux系统里面表示文件用的是inode编号!!!

        那么inode里面有什么内容呢? inode的结构体定义如下:

#define NUM 15

struct inode

{

//inode number;

//文件类型

//权限

//引用计数

//拥有者

//所属组

//ACM时间

//int blocks[NUM]

}

下面是blocks[NUM];

        假如上面的15个元素中, 里面的前12个是直接索引, 后面3个事间接索引。 如果间接索引12或者13中存储的是200, 那么这个序列号为200的块中存储的就不是数据, 而是其他块的索引编号。 这可以成为二级索引。 

        我们的第十四个元素, 就可能不仅仅是二级索引, 可能是三级索引, 多级索引。

        在上面这些块里面, 我们如何知道那些快是被使用的, 哪些块是没有被使用的呢?

        所以我们就有了一个block bitmap。 有多少个块, 就有多少个比特位。 有15000个块, 就有15000 / 8个字节来开辟block bitmap。 而15000 / 8等于1875字节。 不到4kb, 也就是一个块也不到。

        比特位的位置和块号映射, 比特位的内容, 表示该块有没有被使用!!!比如该比特位为1, 代表该块被使用, 该比特位为0, 代表该块没有被使用。 ——删除一个文件的时候, 用不用把块(文件内容清空呢?)——答案是不用, 等到删文件的时候, 我们只需要找到文件对应的inode, 然后找到对应的block, 把里面的编号在位图的映射全部清空就可以了。

寻址的细节

        我们需要知道的是, 无论是inode, 还是data block, 到最后一定可以被操作系统解释成为一个个的扇区编号, 因为inode table有对应的编号, data block也有对应的编号。 那么根据特定的算法, 就能得到最后的扇区编号。 ——这个过程是操作系统必须做的。 ——寻找的算法:

        一个块4kb, 一个扇区是512字节。 而一个inode是128字节, inode在组里面是连续分布的, 那么就说明一个扇区里面有4给连续的inode, 那么一个块就是32个inode。 那么我如果知道了加载到磁盘当中的inode编号, 那么我们根据——》编号 / 32以及编号 % 32就能知道当前inode在系统的哪个山区当中。 而对于data block, 一个块是4kb也就是8给扇区, 那么知道了块编号, 就可以计算扇区编号。

inode bitmap

        inode table 里面保存了文件的编号, 有1w个文件就有1w个inode。 但是, 对于整个的inode table数组, 我们怎么知道里面的哪个索引处被占用, 哪个索引处没有被占用呢?——这个时候就要用到inode bitmap。 inode bitmap是用来将比特位的位置和inode编号映射起来, 来判断对应的inode是否有效的!!!

block bitmap

        在这张表中, 其实block bitmap的占用空间并不大。 因为假如我们有10w个块, 一个块4kb, 十万个块就是4G, 也就是说, 如果一个区200GB, 分成20个组, 那么一个组对应10G的内容里面最多只有25w个块。 但是这是不可能的, 因为还要给其他的内容分配空间。 这里假如有20w个块, 那么block bitmap就要有20w个比特位映射这20w个块。 20w / 8 = 22500, 也就是22500b。22500 / 1024 ~ 22kb, 也就是说, block bitmap最多也就占用22kb, 五六个块的大小。 inode bitmap会比block bitmap占用的空间更加的小。 因为inode的个数是更少的。 所以block bitmap, inode bitmap占用的空间在整个磁盘中几乎可以忽略不记。

group descriptor table

        描述的是整个分组的使用情况——比如我们block bitmap使用了多少、inode table使用了多少、data block使用了多少等等。 以及整个分组空间, 有多少空间被使用了, 有多少空间没有被使用; 以及我们在分配inode的时候或者分配block的时候, 我们的inode或者data block的编号下一个要从哪里开始分配。

        这里之所以没有使用bitmap来直接计算block和inode的使用情况是因为计算的过程效率不如直接使用统计效率高。 所以这里直接使用了Group Descriptor Table。

Super block

        文件系统的基本信息——也被称为超级块, 描述的是整个分区的文件系统的情况。 这里要知道, 一个组里面凭什么第一块空间时super block, 凭什么最后一个块空间是data block, 凭什么倒数第二个块空间是inode table。 ——因为super block, super block 规定了整个分区的使用情况 规定了分区里这些组的空间如何分配。 ——这就是文件系统, 不同的文件系统, 对于不同组的规划情况是不一样的。 有的可能没有inode bitmap, 有的可能多出来一个我们不知道的其他东西。 

        但是我们这里学习的文件系统叫做EXT2, 对应的super block描述的里面就必须有上面表当中的五个区域。 并且顺序是不能颠倒的, 必须GDT(Group Descriptor Table)在前, Block bitmap在后等等。 并且空间不能随意的划分, 必须要规定好。 更重要的是super里面描述的是整个分区的情况。 

        super block里面包含的是整个分区的使用情况, 他一共有多少个组, 每个组的带下, 每个组的inode的数量, 每个组的block数量, 每个组的其实inode文件系统的类型与名称等等!!!

        super block不会在每一个组里面都存在, 为什么?——如果我们的整个分区里面的某一个组里面的super block出问题了, 那么会出什么问题呢? 假如对于一个分区的super block正好在block group某某某里面, 而且这个分区里面的super block正好挂掉了, 那么挂掉的可不仅仅只是这一个组, 而是所有的组的边界都不清楚了, 那么整个的200GB的分区就挂掉了。

        我们的文件系统不仅仅要保证自己能工作, 还要保证自己的健壮性。 所以对于这个分区来说, 如果每一个分区里面都有一个super block, 那么就会导致效率过低, 有点浪费。 但是呢, 我们如果super block只保证一份, 就会存在我们上面所述的问题。 

        所以我们一般会在分区里面的所有block group中星星点点的分配一些super block。 这样, 更新的成本会低, 而且一旦superblock挂掉, 其他分组内的super就会对整个文件系统进行修正。

        对于super block, 在文件系统当中, 有一个概念叫做魔数, 魔术就是一个随机值, 这个随机值在super block当中的位置是固定的。 所以我们的操作系统在读取磁盘中的某个块的时候, 只需要在特定一个块的待定位置读取这个数字, 这个数字必须等于我们规定好的这个魔数。 等于的话, 才会认为这个是一个super block。 

磁盘的初始化

        对于这个图里面的信息, 除了inode table 属于文件的, data blocks属于文件块的, 这两个空间只需要预留好即可。 前面的super block、group descriptor table、block bitmap、inode bitmap四个中的super block、group descriptor table是需要提前初始化信息的block bitmap、inode bitmap是要清空的

        每一个分区在被使用前, 都必须提前将部分文件系统的属性信息提前设置进对应的分区中, 方便我们后续使用这个分区或者分组。 ——前面的四个信息, 是对后面两个部分的属性的描述。而这种提前将文件系统的信息写到文件中的过程叫做格式化

        其实我们电脑内部只有一块物理磁盘(硬盘), 而之所以会有C盘、D盘、F盘, 其实这些盘就是硬盘的分区。 而一旦格式化, 就会将两个bitmap全部清零, 并且向super block、group descriptor table初始化文件信息。

文件操作细节

新建一个文件

        新建一个文件, 那么我们知道这个文件一定是在某一个路径里面, 这个路径, 就可以帮助我们确定是在哪一个分区里面。 然后就是确定我们在分区的哪里去创建我们的文件。 ——分区里面的inode假如是连续的, 比如第一个group里面的inode是从0~1w, 第二个的inode是1w~2w, 以此类推。 那么我们创建文件的时候, 加入第一个组有空余的空间, 那么我们这个文件就可以保存在第一个组里。 那么如何确定保存的位置?——只需要查看GDT, 查看inode和块有多少空间占用率, 再查看inode bitmap、inode table扫描位图结构。

        找到一个最近的没有被使用过的编号。 如果找到的是5,那么如果分到了第一个分组就是0 + 5, 那么如果分到了第二个分组里面就是1w + 5。

        未来就可以根据inode范围来确定哪个分组里, 如果确定了5后就把inode bitmap里面5的位置置为1, 再将文件信息放到inode编号5的结构体里, 至此, 这个文件算是创建成功了。 

删除一个文件

        一个文件, 一定是处于某个目录的, 根据这个目录, 然后我们就可以确定这个文件是哪一个分区中。 然后根据inode范围,确定哪个分组的, 然后把我们这个inode编号减去起始inode编号。 进而, 我们就可以在inode bitmap里进行索引了, inode进行索引的时候, 只要删文件, 那我们就可以根据我们的inode编号, 找到我们对应的inode文件属性, 根据文件属性里面的block, 找到对应的block bitmap里的比特位置, 并将其置为0. 而其中的块则不去动它, 然后在我们的inode位图里, 把对应的1置为0, 将属性干掉即可。

        如果我们不小心删除了一个文件, 那么这个文件如果是一个不重要的文件, 那么就可以不管这个文件了。 但是如果这个文件是一个很重要的文件, 那么此时最好的处理方法是什么都不要做(这些block是可以覆盖的), 可以去找专业的人士进行文件恢复。

查找一个文件

        查找一个文件就是拿到这个文件的inode编号, 然后根据编号确定inode bitmap里面的对应的比特位是否为1, 那么就可以确定对应的inode属性是否有效, 拿到inode属性后, 就找到inode属性里面的block NUM, 然后就能查找到文件了。

修改一个文件

        我们修改一个文件, 比如说创建一个文件, 修改一个文件等等。就会拿到这个文件的inode, 拿到这个文件的inode之后, 就可以根据inode确定bitmap里面的比特位是否为1, 是否有意义。 然后拿到inode的文件信息, 找到里面的block NUM, 然后再找到对应的数据块, 修改这些数据块, 就完成了文件的修改。

理解目录文件

        目录也是文件, 也有自己的inode, 目录也要有自己的属性, 但是, 目录有内容吗? 我们知道, 文件 = 文件内容 + 属性——》这里, 目录有没有内容的本质, 其实就是目录有没有数据块。

        答案是目录也有数据块, 但是这个数据块应该存什么呢? ——存的是该目录下文件的文件名和对应文件的inode的映射关系!!!

       为什么同一个目录下不能有同名文件——因为文件名和对应文件的inode映射关系被保存在数据块中!!!       目录下, 没有w, 我们无法创建文件, 这是为什么——因为即便是创建下来, 但是这个文件的文件名和inode的映射关系无法写到对应的数据块里。       目录下, 没有读权限,我们无法查看文件, 为什么?——因为我们在读取文件的时候, 要读取文件的inode, 然后根据inode读取块数据, 而我们没有读权限, 我们无法读取文件的inode, 更加无法读取块数据。        目录下, 没有执行权限, 我们就无法进入这个目录, 为什么?——因为我们使用inode, 进入这个目录的时候, 要对环境变量做一下更新, 但是今天我们如果没有执行权限, 我们就不让他进行更新了。

如何找到目录文件的inode

        目录是文件, 同样有inode, 我们可以通过拿到目录的inode找到里面的数据块, 进而找到目录里面的各个文件。 那么我们如何找到目录的inode呢? ——我们知道, 我们的目录一定是上一级目录里面的文件那么我们就一定能够通过上一级目录的inode, 找到其中的数据块, 进而找到这一级的目录的inode 而我们一级一级的追溯, 我们最终会追溯到根目录, 而好在我们有根目录的inode, 也就是说, 我们要先递归的找到根目录, 然后再一步步返回。

        但是, 一步步访问太慢了, 所以linux操作系统会一般把我们最常访问的目录文件保存起来, 以及他路径上的所有的inode以及路径信息缓存起来。 这个缓存, 就叫做dentry缓存。 

----------------------------------------------------------以上, 就是本节的全部内容, 下面是本节笔记:



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。