0

文件在硬盘看来就是一堆二进制数据而已
你准备把这些文件存储在硬盘上,并在需要的时候读取出来。
要设计怎样的软件,才能更方便地在硬盘中读写这些文件呢?

1

首先我不想和复杂的扇区,设备驱动等细节打交道,因此我先实现了一个简单的功能,把硬盘按逻辑分成了一个个的块,并可以以块为单位进行读写。
每个块就定义为两个物理扇区的大小,即 1024 字节,就是 1KB。
硬盘有 1024 个块,即 1MB。

准备一个文件,随便选个块放进去

2

再存一个文件!
发现问题了,万一这个文件也存到了 3 号块,不是把原来的文件覆盖了吗?得有一个地方记录,哪些块可用,哪些块不可用?
位图!

我们给块 0 起个名字,叫块位图,之后这个块 0 就专门用来记录所有块的使用情况,不再用来存具体文件了。

当我们再存入一个新文件时,只需要在块位图中找到第一个为 0 的位,就可以找到第一个还未被使用的块,将文件存入。

3

下面,我们尝试读取刚刚的文件。
怎么找到刚刚的文件?根据块号吗?这也太蠢了,就像你去书店找书,店员让你提供书的编号,显然不合理。
因此我们给每个文件起一个名字,叫文件名,通过它来寻找这个文件。
那必然就要有一个地方,记录文件名与块号的对应关系。
既然都要选一个地方记录文件名称了,不妨多记录一点我们关心的信息吧,比如文件大小,文件创建时间,文件权限等。

我们将这 128 字节的结构体,叫做 inode
之后,我们每存入一个新的文件,不但要占用一个块来存放这个文件本身,还要占用一个 inode 来存放文件的这些元信息,并且这个 inode 的所在块号这个字段,就是指向这个文件所在的块号。

如果一个 inode 为 128 字节,那么一个块就可以容纳 8 个 inode,如果觉着 inode 数不够,可以用多个块来存放 inode 信息。

同样,和位块图管理块的使用情况一样,我们也需要一个 inode位图,来管理 inode 的使用情况。我们就把 inode 位图,放在 1 号块吧!
同样,我们把 inode 信息,放在 2 号块,叫做 inode 表。

4

如果文件需要多个块存储?
很简单,我么只需要采用连续存储法,而 inode 则只记录文件的第一个块,以及后面还需要多少块,即可。
这种办法的缺点就是:容易留下大大小小的空洞,新的文件到来以后,难以找到合适的空白块,空间会被浪费。

扩展 inode

原来在 inode 中只记录了一个块号,现在扩展一下,记录 8 个块号!而且这些块不需要连续

5

如何知道 inode 数量不够了?需要在 inode 位图中找?
同样,对于块数量不够,也是如此。
要是有个全局的地方,来记录这一切,就好了
我们就再占用一个块来存储这些数据吧!我们把它放在最开始的块上,并把它叫做超级块

现在,块位图,inode 位图,inode 表,都是固定占据 块1,块2,块3 这三个位置。
加入之后 inode 的数量很多,使得 indoe 表或者 inode 位图需要占据多个块,怎么办?
我们选在紧跟在超级块后面的 1 号块来记录这些信息,并把它称之为块描述符

6

怎么表示目录?
inode 结构再增加一个属性来表示文件类型

如果是普通文件,则这个 inode 所指向的数据块仍然和之前一样,就是文件本身原封不动的内容。
如果是目录文件,则这个 inode 所指向的数据块,就是一个一个指向不同 inode 的紧挨着的结构体。

通过目录,找到所在的数据块,这个数据块里面是 inode,然后根据 inode 找到文件数据。

7

查询目录下的所有文件
需要把结构体指向的 inode 从 inode 表中取出,再把文件名和文件类型取出,很浪费时间。
不如和文件名和文件类型这种常见的信息,放在这个数据块中的结构体里。

后面的一些优化就不细说了-_-
最后来欣赏下我们的文件系统架构。

总结

超级块:描述 indoe 数量,空闲 indoe 数量,块数量,空闲块数量等等
块描述符:块位图,inode 位图,inode 表所在的块号
块位图:哪些块可用,那些块不可用
inode 位图:和块位图作用类似
inode 表:inode 数组
inode:存储文件的信息,文件类型,大小,时间,权限,所在块号[]等
数据块:真实的数据