突破教材,简单聊聊《文件系统》

前言:

​ 我们在前面的学习中学习诸如:C语言打开操作文件、通过系统调用打开操作文件甚至说是对文件的重定向操作。对于上述的操作,我们本质是先将文件打开,再对其进行处理。打开文件的操作本质是在内存中申请空间,创建文件描述符,再创建struct file结构体用来管理磁盘中的文件内容,最后通过映射关系,可以对一个文件进行读和写的操作。
​ 那么我们之前学习的都是对打开的文件进行处理的,那对于那些没打开的文件呢?要知道,打开的文件是少数,而哪些没被打开的文件可是大量的!而这些没被打开的文件均在磁盘或者说是硬盘中存储着呢(硬盘我们今天不谈),所以我们以前要打开一个文件其实是要先在磁盘中通过文件路径 + 文件名找到。那到底磁盘是怎么存储的呢?怎么构建整个文件系统的呢?所以本章我们要来好好聊聊一个文件在未被打开的时候,都在磁盘中做了什么呢?又或者说哪些“家伙”对这些文件做了什么呢?

文件系统的引入:

认识物理磁盘:

image-20240902154416860

俯视着看只能看个大概的情况,而整个磁盘最核心的部分其实就是磁盘和和磁头那一块像CD一样的结构,他其实不止含有一个磁头也不仅仅含有一个磁盘片。
image-20240902154818864

我们的在磁盘中存储的未打开的文件,就存储于该结构上,具体的位置就是盘片上的每一个小小的连续的扇区(sector)中
image-20240904083646274

其实我们那些未打开的文件,在磁盘中存储就是在每一个盘片上对应的扇区中,进行数据的存储一般都是用一个个连续的扇区来进行存储,通过二进制的方式来进行写入
那为什么是二进制呢?————因为计算机只认识二进制
我们都知道,二进制里面的数字不是0就是1的,那究竟什么是0、1呢?
————其实0和1只是一个个代表,真正的体现就体现在物理层面的不同。

而在磁盘中,对于扇区(sector)中,编写二进制的本质其实是通过磁铁来改变每个扇区每个bit位的南北极,可能1就是北极,0就是南极。通过这种来实现数据的存储,所以我们的数据约等于是被刻在上面的,然后需要查找时候就去找对应的那个磁片,磁片的哪个面,磁片中的哪一个磁道(柱面)(cylinder),再在柱面中找到对应的一串扇区(sector)

对于磁盘更详细的介绍,可以观看视频:
磁盘是如何工作的
你的硬盘是如何存储数据的

所以我们上述所讲解的,如何找到一个指定位置的扇区:

  1. 找到指定的磁头 —— Header
  2. 找到指定的磁道(柱面) —— Cylinder
  3. 找到指定的扇区 —— Sector

这就是我们的 CHS定址法

对磁盘的存储进行逻辑抽象:

​ 我们都知道,对软硬件资源做管理的就是操作系统,那么磁盘属于硬件,那么操作系统又该如何管理呢?谈及管理永远六个字 —— “先描述, 再组织”
​ 既然是管理,那你的意思就是我可以直接用OS来实现CHS定址法,然后在磁盘中找到文件咯?当然这是不对的,磁盘有着很多不同版本和型号,磁盘一旦更新,操作系统对应管理的软件也会进行更新,一两个软件还好,但是一旦涉及到成千上万个软件那就没有必要了,耦合度太高。所以其实操作系统是通过**LBA(逻辑区块地址)**来进行管理的。
image-20240904092912396

相信我们都见过磁带,他们可以卷在一起形成圈,也可以散开形成一条线,所以,我们抽象就是将这几面盘面的大小抽象成一个线性数组。每一个磁道(cylinder)都有很多个扇区,将同一个磁道上的全部扇区“拉”成一条:

image-20240904093453571

这样我们可以暂时抽象理解为查找扇区的位置,就是通过下标来进行查找。

这个只是我们逻辑抽象的概念,那其实操作系统想要实现管理但又不能直接使用CHS定址法,操作系统又是通过什么算法使得硬件实现CHS呢?
假设一个磁盘上面的磁有1000个扇区和10个磁道。那么1000 / 10 = 100 就代表着1个磁道有100个扇区
但是如果我们的操作系统知道了某个文件target的下标(index),我们就可以知道这个文件他是在哪个面、磁道和扇区

  1. 因为一个面有1000个扇区,那么第一个面扇区我们可以抽象理解为一个下标从 0 ~ 999 的数组,第二个下标就是 1000 ~ 1999 的数组。所以假设我的index == 3456,我们 index / 1000 == 3,就可以得知该文件是在哪个磁片(也就是找到了磁头Header)

    image-20240904102102349

  2. 对应该磁头找到对应的磁片后,我们需要来确定我们的index在哪个磁道上。现在我们知道一个磁面上有10个磁道,所以最后的磁道肯定是1号磁道到10号磁道[1, 10]的,而一个磁道有100个扇区,所以最后你的扇区下标肯定是在[0, 99]之间的。所以我们先来确定磁带:3456 % 100 == 456,这个是排除磁面后的数据,再 456 / 100 == 4得到再第4号磁道上,这一步是依据你的磁道只能是在[1, 10]上的。
    image-20240904103159171

  3. 接下来我们就要确定扇区位置,其实现在我们就很好理解了,那么扇区的位置只要对我们刚刚处理好的456在模上100就好了,因为扇区的下标只能在[0, 99]之中,这样456 % 100 == 56,就能确定扇区是在56号下标开始。
    image-20240904103801562

LBA逻辑区块地址:

​ 现在我们知道了磁盘对文件数据存储的逻辑抽象,其实文件里面的内容和属性 == 很多个扇区(sector)的数组的下标。一般而言,OS未来和磁盘交互的时候,基本单位:4KB
​ 而一个sector的大小默认为512字节,所以一个OS在从磁盘读取文件的时候,是基于8个连续的扇区(sector)来读取的!

所以我们就可以将上述逻辑抽象出来的数组进行分区块来描述
image-20240904105617379

而这些分好出来的几号几号“块”,这就是我们的**LBA(逻辑区块地址)**。

假设像上述一样给每个“块”进行编号,就有如上0号块、1号块、2号块等等…
那么通过编号我们就能知道对应”块“的起始下标:0 * 8 == 0,1 * 8 == 8,2 * 8 == 16。像0、8、16就是对应0号块、1号块、2号块的起始下标。

1、所以现在,我们只要知道了你当前的文件处于哪一个“块”,那我们就一定能找到这个块的位置,块找到了那么在这个连续的数组上,我对应的扇区(sector)在哪里就知道了
而你的一个一个的磁道是同心圆,那么你一条路一直走下去,你肯定可以走到头的,所以你这个磁道有多少个扇区(sector)你就肯定可以知道!因此只要你能找到磁道,一个磁道有多少个扇区你肯定可以知道

2、假设你知道一个磁片上全部扇区有多少,那么你又由上述知道了一个磁道上有多少个磁片,那么你当然可以知道一个磁面上有多少个磁道。

所以只要你知道了一个块号和磁盘的总扇区大小(一共有多少个块),就可以知道全部信息,包括有多少个磁道啦、多少个扇区啦,甚至说每个扇区的对应的每个块号,以及对应的CHS地址!

🚀文件系统的理解:

​ 基于上面了理解,可以总结出操作系统对于文件的管理是分组区块进行管理的!但是你看像我们的磁盘,它可是800GB左右甚至更高的容量来存储的,对于这么大的数据量,你还在那8个sector8个sector的数,那你得多累?而且更重要的是,对于这么大的数据操作系统也不好管理呀!

于是我们的操作系统就会告诉用户,你可收到以进行磁盘分区啦!
image-20240904132026248

这就是我们在windows上进行分区之后的结果,分为了C、D、E盘。不仅仅可以对数据进行区别,也可以方便操作系统进行更方便的管理
image-20240904132407111

现在我们拿到了所谓的区块,但是我们想要管理好每一个分区,我们还需要对分区进行分组:
image-20240904133840592

如此一来,我们对分区的管理就又可以继续往下细分管理了。但是,这只是我们所描述的,我们这么描述只是更方便了些,可是操作系统考虑到的东西可不仅仅是方便喔,操作系统还要对这个分区进行管理喔。所以操作系统肯定也会对磁盘进行分组,但是它考虑的东西就多得多
image-20240904134334998

这才是操作系统对磁盘管理的真正样子,而计算机和人不一样,如果它能将一个分区细分出这么组,那么其它分区也可以细分出这么多组,如果一个组能被操作系统管理好,那么其它组也能被操作系统管理好,所以整个文件系统它终将会被管理好!

但是这是针对于Linux的!因为我们知道文件 = 属性 + 内容,而在linux中,文件内容和文件属性是分开存储的!我们的文件内容就存放于Data blocks中,而文件属性则存放于inode Table中!下面我们就来介绍介绍这几个小玩意。

理解各个区段:

  • Block group:就是我们的分组,文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。

  • Data blocks(数据区):存放文件内容,实际上该区间特别大,有很多很多的数据块(大小为4KB)
    image-20240904135408973

  • Block bitmap(块位图):记录者Data block中那个数据块被占用了,哪个没有被占用。
    image-20240904140111442

  • 🧨inode Table(i节点表):存放文件属性,存放文件属性 如 文件大小,所有者,最近修改时间等

    Linux中文件的属性是一个大小固定的集合体。

    struct inode // 文件属性
    {
      int size;
      int time;
      int inode_number;
      int datablocks[N];
    };
    

    image-20240904140624706

    一个正常的文件都对应着一个inode集合。
    我们可以通过输入指令:ls -li 查看当前目录文件下的inode number

    image-20240904141048669

    在inode内部,我们不包含文件名!从内核层面上来看,每一个文件都要有inode number

  • inode Bitmap(inode位图):每一个bit表示一个inode是否空闲可用,比特位的位置表示第几个inode(inode number),比特位是0还是1表示是否被占用。

  • Super Block (超级块):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,
    未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的
    时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个
    文件系统结构就被破坏了 。
    Super Block 为了使文件系统稳定,会在部分组里出现super block,记录当前盘的全部inode的信息等。这个作用就是假设未来我们出现了文件损毁,系统会提示我们是否要进行修复。本质上是一个租的superblock坏了,要将其它的super block的内容复制到损坏的super block中,所以各个super block中的数据必须要保持一致。

对每一个分区内部分组(这就叫==“格式化”==),为了以后能够写入文件系统的管理和数据

🛹深入理解 inode:

  • 创建新文件时,怎么通过 inode number 定位 inode Bitmap 对应的位置?

​ 我们在寻找文件的时候,都必须先的到文件的 inode在inode Table中的位置号,inode 是以“分区”为单位的,即以各个盘为单位的(不能跨分区访问)!

​ 我们在刚开始创建新文件的时候,super block 和 Group Description table 会确定 inode number 的范围。然后拿着新文件的 inode number 在当前分区中(不可跨分区访问),一个一个比对从而可以发现该文件属于哪个组,找到该组后,通过新文件的 inode number 减去前该组的 start_inode ,得到的数就是对应 inode Bitmap 和 inode Table 的位置。
image-20240904165244736

​ 假设一个新文件的 inode number 是1030,那么该文件就存在于 group B。
​ 为了确定该文件的 inode Bitmap 和 inode Table 的位置,将1030 - 1000 == 30号,该30号就是该组中 inode Bitmap 中对应位图的号,而这个时候OS就会在inode Bitmap的30号位置的bit位设置为1,对应的inode Table的30号也会出现于一个结构体inode,记录了这个新文件的各种信息。不仅仅是创建,我们在查找的时候,OS也是这么去查找的!

  • 那如何定位 blocks Bitmap呢?
    我们在讲解inode的时候,发现inode结构体里面有一个成员变量:int datablock[N]
    image-20240904172243321
    那么这个东西其实就是一个数组了,里面存放着文件内容对应的 Data blocks 中的号码,而这些号码会从block Bitmap中去读取,然后指向对应的号码。
    但是在linux中一般只有15个元素那么一个文件只能存60KB?前12个元素确实如此,但是第13,14个元素并不是单纯的映射,15个更是重量级。
    image-20240904173516507

    蓝色的就代表第12、13、14号元素是一级映射,第15号是二级映射,当然图只是简单的描述,现实肯定更复杂!

    我们是有能力存大文件的!甚至跨组,但是不建议,因为这样磁头与盘片又会旋转重新寻址,造成效率低下!

inode和文件名?

问题来了,我们在上层使用的都是文件名啊,可是系统都是用inode编号进行标识,那么怎么进行联系呢?

  • 首先我们来谈谈目录
    目录本身就是一个文件,目录 = 文件属性 + 文件内容(内容包含文件名inode编号的**映射关系**)
    所以我们在上层不用inode编号,使用文件名即可对文件进行各种操作。
    1. 因为一个文件名只有一个对应的inode 和 inode number,所以一个目录下不能建立同名文件。
    2. 我们查文件的信息,靠的是文件名和inode number之间映射关系找到inode,再通过inode number找到对应的组别和当中的inode table的所以属性
    3. 关于目录的r权限,本质就是是否允许我们读取目录的内容,与inode和文件名的映射有关。
      关于w权限也相似
    4. 如何理解一个文件的增删查改
      ▷创建新的inode,再通过 inode bitmap 在 inode table 写入对应的属性,最后在data block中写入数据。
      删除
      ▷删除其实很好理解,我们只需将inode bitmap 和 block bitmap 对应的 ’1‘ 改为 ’0‘即可,属性那一块可以不用处理,因为属性那一块迟早会被覆盖,这也就解释了为什么拷贝文件要很久,而删除文件也就一瞬间就删除完了!

如何确定inode在哪个分区?

为什么路径那么重要?

通过上面的介绍,关于存储文件可以组织成以下:

找到指定的文件 -> 找到该文件的目录 -> 打开目录 -> 找到inode与文件名映射关系 -> 找到inode进而找到属性与内容。

​ 可是我们在这一串关系中仔细一些会发现一个奇怪的现象,找到该文件的目录,我们如果需要实现这一条件也需要找到目录的目录呀。
​ 想要找到某个文件,就要找到这个文件夹,找到这个文件夹就要找到文件夹的文件夹… 如此我们最后只能找到根目录时我们才会停下来。

根目录是系统规定出来的,在我们OS开机时就要知道根目录在哪里,所以我们要找到一个文件需要逆向的文件路径解析—OS自己做的

所以,为什么我们在定位一个文件,无论在什么时候,都需要路径的!

所以我们在ls时本质是这样的!可是有人会说我们在执行ls这样的命令就没有带路径啊
这是因为我们的ls是个进程,进程有自己的cwd(当前路径)

那么这样次次会不会太麻烦呢?
不用担心,我们的系统会缓存常用路径结构。

如果路径太多了是不是也需要管理呢?
那是不是就需要先描述再组织?

所以我们文件还有一个内核结构体struct dentry用来描述文件路径。
所以我们的struct除了有指向方法表与缓冲区,现在又多了一个指向路径解析的结构体,这个结构体是内核层面,而不是磁盘级层面的。

目录和分区的关系?

​ 我们在对以上进行分析的时候,都是以某一个确定的分区为前提,可是inode编号在不同分区内可能会一样啊,我们怎么确定自己是在那个分区呢?
我们可以先来查看我linux系统下的分区: 输入 ls /dev/vda
image-20240904180718053
我们可以看到我们有一个磁盘vda和一个分区vda1.

那我们再看看它们的属性叭,输入df -h
image-20240904180933533

这里有这么一行代码:/dev/vda1 40G 4.4G 33G 12% /
这个意思就是将这个分区 ==挂载==到了根目录,
挂载的意思就是将一个设备(通常是存储设备)挂接到一个已存在的目录上。访问这个目录就是访问该存储设备。

所以我们在根目录下进行操作时就已经代表我们在那个确定的分区了!

  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无双@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值