图解 | 原来这就是文件系统

你手里有一块硬盘,大小为 1Thtml

图解 | 你管这破玩意叫文件系统?

 

你还有一堆文件node

图解 | 你管这破玩意叫文件系统?

 

这些文件在硬盘看来,就是一堆二进制数据而已linux

图解 | 你管这破玩意叫文件系统?

 

你准备把这些文件存储在硬盘上,并在须要的时候读取出来。编程

要设计怎样的软件,才能更方便地在硬盘中读写这些文件呢?数组

1

首先我不想和复杂的扇区,设备驱动等细节打交道,所以我先实现了一个简单的功能,将硬盘按逻辑分红一个个的块,并能够以块为单位进行读写。架构

每一个块就定义为两个物理扇区的大小,即 1024 字节,就是 1KB 啦。并发

硬盘太大很差分析,咱们就假设你的硬盘只有 1MB,那么这块硬盘则有 1024 个块。socket

图解 | 你管这破玩意叫文件系统?

 

OK,咱们开始存文件啦!布局

准备一个文件nuxt

图解 | 你管这破玩意叫文件系统?

 

随便选个块放进去,3 号块吧!

图解 | 你管这破玩意叫文件系统?

 

成功!首战告捷!

2

再存一个文件!

诶?发现问题了,万一这个文件也存到了 3 号块,不是把原来的文件覆盖了么?不行,得有一个地方记录,如今可以使用的块有哪些,像这样。

块 0:未使用

块 1:未使用

块 2:未使用

块 3:已使用

块 4:未使用

...

块 1023:未使用

那咱们就用 0 号块,来记录全部块的使用状况吧!怎么记录呢?

位图!

图解 | 你管这破玩意叫文件系统?

 

那咱们给块 0 起个名字,叫块位图,以后这个块 0 就专门用来记录全部块的使用状况,再也不用来存具体文件了。

图解 | 你管这破玩意叫文件系统?

 

当咱们再存入一个新文件时,只须要在块位图中找到第一个为 0 的位,就能够找到第一个还未被使用的块,将文件存入。同时,别忘了把块位图中的相应位置 1。

完美!

3

下面,咱们尝试读取刚刚的文件。

咦?又遇到问题了,我怎么找到刚刚的文件呢?根据块号么?这也太蠢了,就像你去书店找书,店员让你提供书的编号,而不是书名,显然不合理。

所以咱们给每一个文件起一个名字,叫文件名,经过它来寻找这个文件。

那必然就要有一个地方,记录文件名与块号的对应关系,像这样。

葵花宝典.txt:3 号块

数学期末复习资料.mp4:5 号块

低并发编程的秘密.pdf:10 号块

...

别急,既然都要选一个地方记录文件名称了,不妨多记录一点咱们关心的信息吧,好比文件大小、文件建立时间、文件权限等。

这些东西天然也要保存在硬盘上,咱们选择用一个固定大小的空间,来表示这些信息,多大空间呢?128 字节吧。

为啥是 128 字节呢?我乐意。

图解 | 你管这破玩意叫文件系统?

 

咱们将这 128 字节的结构体,叫作一个 inode。

以后,咱们每存入一个新的文件,不但要占用一个块来存放这个文件自己,还要占用一个 inode 来存放文件的这些元信息,而且这个 inode 的所在块号这个字段,就指向这个文件所在的块号。

图解 | 你管这破玩意叫文件系统?

 

若是一个 inode 为 128 字节,那么一个块就能够容纳 8 个 inode,咱们能够将这些 inode 编上号。

图解 | 你管这破玩意叫文件系统?

 

若是你以为 inode 数不够,也能够用两个或者多个块来存放 inode 信息,但这样用于存放数据的块就少了,这就看你本身的平衡了。

图解 | 你管这破玩意叫文件系统?

 

一样,和块位图管理块的使用状况同样,咱们也须要一个 inode 位图,来管理 inode 的使用状况。咱们就把 inode 位图,放在 1 号块吧!

同时,咱们把 inode 信息,放在 2 号块,一共存 8 条 inode,这样咱们的 2 号块就叫作 inode 表。

如今,咱们的文件系统结构,变成了下面这个样子。

图解 | 你管这破玩意叫文件系统?

 

注意:块位图是管理可用的块,每一位表明一个块的使用与否。inode 位图管理的是一条一条的 inode,并非 inode 所占用的块,好比上图中有 8 条 inode,则 inode 位图中就有 8 位是管理他们的使用与否。

4

如今,咱们的文件很小,一个块就能容下。

但若是须要两个块、三个块、四个块呢?

很简单,咱们只须要采用连续存储法,而 inode 则只记录文件的第一个块,以及后面还须要多少块,便可。

这种办法的缺点就是:容易留下大大小小的空洞,新的文件到来之后,难以找到合适的空白块,空间会被浪费。

图解 | 你管这破玩意叫文件系统?

 

看来这种方式不行,那怎么办呢?

既然在 inode 中记录了文件所在的块号,为何不扩展一下,多记录几块呢?

图解 | 你管这破玩意叫文件系统?

 

原来在 inode 中只记录了一个块号,如今扩展一下,记录 8 个块号!并且这些块不须要连续。

图解 | 你管这破玩意叫文件系统?

 

嗯,这是个可行的办法!

可是这也仅仅能表示 8 个块,能记录的最大文件是 8K(记住,一个块是 1K), 如今的文件轻松就超过这个限制了,这怎么办?

很简单,咱们可让其中一个块,做为间接索引。

图解 | 你管这破玩意叫文件系统?

 

这样瞬间就有 263 个块(多了 256 -1 个块)可用了,这种索引叫一级间接索引。

若是还嫌不够,就再弄一个块作一级间接索引,或者作二级间接索引(二级间接索引则能够多出 256 * 256 - 1 个块)。

咱们的文件系统,暂且先只弄一个一级间接索引。硬盘一共才 1024 个块,一个文件 263 个块够大了。再大了不容许,就这么任性。

好了,如今咱们已经能够保存很大的文件了,而且能够经过文件名和文件大小,将它们准确读取出来啦!

5

但咱们得精益求精,咱们再想一想看这个文件系统有什么毛病。

好比,inode 数量不够时,咱们是怎么得知的呢?是否是须要在 inode 位图中找,找不到了才知道不够用了?

一样,对于块数量不够时,也是如此。

要是有个全局的地方,来记录这一切,就行了,也方便随时调整,好比这样

inode 数量

空闲 inode 数量

块数量

空闲块数量

那咱们就再占用一个块来存储这些数据吧!因为他们看起来像是站在上帝视角来描述这个文件系统的,因此咱们把它放在最开始的块上,并把它叫作超级块,如今的布局以下。

图解 | 你管这破玩意叫文件系统?

 

咱们继续精益求精。

如今,块位图、inode 位图、inode 表,都是是固定地占据这块 一、块 二、块 3 这三个位置。

假如以后 inode 的数量不少,使得 inode 表或者 inode 位图须要占据多个块,怎么办?

或者,块的数量增多(硬盘自己大了,或者每一个块变小了),块位图须要占据多个块,怎么办?

程序是死的,你不告诉它哪一个块表示什么,它可不会本身猜。

很简单,与超级块记录信息同样,这些信息也选择一个块来记录,就不怕了。那咱们就选择紧跟在超级块后面的 1 号块来记录这些信息吧,并把它称之为块描述符。

图解 | 你管这破玩意叫文件系统?

 

固然,这些所在块号只是记录起始块号,块位图、inode 位图、inode 表分别均可以占用多个块。

好了,大功告成!

6

如今,咱们再尝试存入一批文件。

  • 葵花宝典.txt
  • 数学期末复习资料.mp4
  • 赘婿1.mp4
  • 赘婿2.mp4
  • 赘婿3.mp4
  • 赘婿4.mp4
  • 低并发编程的秘密.pdf

诶?这看着好不爽,全部的文件都是平铺开的,能不能拥有层级关系呢?好比这样

  • 葵花宝典.txt
  • 数学期末复习资料.mp4
  • 赘婿
    • 赘婿1.mp4
    • 赘婿2.mp4
    • 赘婿3.mp4
    • 赘婿4.mp4
  • 低并发编程的秘密.pdf

咱们将葵花宝典.txt 这种称为普通文件,将赘婿这种称为目录文件,若是要访问赘婿1.mp4,那全文件名要写成

赘婿/赘婿1.mp4。

如何作到这一点呢?那咱们又得把 inode 结构拿出来讲事了。

图解 | 你管这破玩意叫文件系统?

 

此时须要一个属性来区分这个文件是普通文件,仍是目录文件。

缺什么就补什么嘛,咱们已经很熟悉了,专门加一个 4 字节,来表示文件类型。

图解 | 你管这破玩意叫文件系统?

 

若是是普通文件,则这个 inode 所指向的数据块仍然和以前同样,就是文件自己原封不动的内容。

但若是是目录文件,则这个 inode 所指向的数据块,就须要从新规划了。

这个数据块里应该是什么样子呢?能够是一个一个指向不一样 inode 的紧挨着的结构体,好比这样。

图解 | 你管这破玩意叫文件系统?

 

这样先经过 赘婿 这个目录文件,找到所在的数据块。再根据这个数据块里的一个个带有 inode 信息的结构体,找到这个目录下的全部文件。

完美!

7

不过这样的话,你想一想看,若是想要查看一下赘婿这个目录下的全部文件(好比 ll 命令),将文件名和文件类型都展现出来,怎么办呢?

就须要把一个个结构体指向的 inode 从 inode 表中取出,再把文件名和文件类型取出,这非常浪费时间。

而让用户看到一个目录下的全部文件,又是一个极其常见的操做。

因此,不如把文件名和文件类型这种常见的信息,放在数据块中的结构体里吧。

图解 | 你管这破玩意叫文件系统?

 

同时,inode 结构中的文件名,好像就没啥用了,这种变长的东西放在这种定长的结构中自己就很讨厌,早就想给它去掉了。并且还能给其余信息省下空间,好比文件所在块的数组,就能再多几个了。

太好了,去掉它!

图解 | 你管这破玩意叫文件系统?

 

OK,大功告成,如今咱们就能够给文件分门别类放进不一样目录下了,还能够在目录下建立目录,无限套娃!

8

如今的文件系统,已经比较完善了,只是还有一点不太爽。

咱们访问到一个目录下,能够很舒服地看到目录里的文件,而后再根据名称访问这个目录下的文件或者目录,整个过程都是一个套路。

可是,最上层的目录下的全部文件,即根目录,如今仍然须要经过遍历全部的 inode 来得到,能不能和上面的套路统一呢?

答案很是简单,咱们规定,inode 表中的 0 号 inode,就表示根目录,一切的访问,就从这个根目录开始!

图解 | 你管这破玩意叫文件系统?

 

好了,这回没有而后了!

咱们最后来欣赏下咱们的文件系统架构。

图解 | 你管这破玩意叫文件系统?

 

你是否是以为这没啥了不得的。但这个破玩意,它就叫文件系统


后记


这个文件系统,和 linux 上的经典文件系统 ext2 基本相同。

下面是我画的 ext2 文件系统的结构(字段部分只画了核心字段)

图解 | 你管这破玩意叫文件系统?

 

估计你是看不清了,我说下主要异同点:

1. 超级块前面是启动块,这个是 PC 联盟给硬盘规定的 1KB 专属空间,任何文件系统都不能用它。

2. ext2 文件系统首先将整个硬盘分为不少块组,但若是只有一个块组的话,和咱们的文件系统总体结构就彻底同样了,分别是超级块、块描述符、块位图、inode 位图、inode 表、数据块。

3. ext2 文件系统的 inode 表中用 15 个块来定位文件,其中第 13 个块为一级间接索引、14 个为二级间接索引、15 个为三级间接索引。

4. ext2 文件系统的文件类型分得更多,还有常见的如块设备文件、字符设备文件、管道文件、socket 文件等。

5. ext2 文件系统的超级块、块描述符、inode 表中记录的信息更多,但核心的和咱们的文件系统同样,并且这些字段在后续的 ext3 和 ext4 中不断增长,保持向前兼容。

6. ext2 文件系统的 2 号 inode 为根目录,而咱们的系统是 0 号 inode 为根目录,这个很随意,你设计一个文件系统定一个 187 号 inode 为根目录也没人拦着你。

若是你想了解 ext2 文件系统的所有细节,有三种方式。

1. 看源码,linux1.0 后的源码都有 ext2 文件系统的实现,源码是最准确的。

2. 看官方文档,这里有个 pdf 链接。
https://www.nongnu.org/ext2-doc/ext2.pdf

3. 看优质博客,这里我推荐一个。
http://docs.linuxtone.org/ebooks/C&CPP/c/ch29s02.html

4. 用 linux 的 mke2fs 命令生成一个 ext2 文件系统的磁盘镜像,而后一个字节一个字节分析其格式。

若是看源码和官方文档绝不吃力,我固然主推这两个,由于毕竟是一手资料。

但大多数人可能没法作到,有时也没大必要,所以也能够看一些优质的博客。

介绍思想的,我以为我这一篇就算是很优质的一篇了,它会带你从设计者角度了解为何这样来设计文件系统。

介绍细节的,那些连文件系统的格式和字段都写不对的,就别看了,因此我这里良心推荐一篇,就是上面的方式三,能够放心大胆,逐字逐句地食用。

最后你还能够用方式四,本身将文件系统镜像导出来,进行分析。

最近写这几篇文章,我感受对如何学会一项新的技术,有了点当心得,不知道你们是否感兴趣,改天能够找一篇来专门和你们分享一下。

相关文章
相关标签/搜索