为什么要文件系统
在这篇【大话存储】学习笔记(一),磁盘我们说到了硬盘的原理,如果要从硬盘里面取数据,需要告诉控制器从哪里取,取多长等关键信息,如果这个步骤由应用来做,则实在太磨人了。
所以操作系统提供了一个中间层
,对我们来说,只需要记住文件名和路径,其他的与磁盘块打交道的事情就交给这个中间层来做。这个中间层即为文件系统
怎么记录文件占有的磁盘块
最容易想到的自然是连续存放。这种方法在随机访问的时候效率最好,只需要知道文件起始位置以及长度,就可以像数组一样访问。
缺点也很明显,如果一个文件删除了,就会留下很多空白的位置,后面的文件再过来填充的时候,如果填不满,则会留下大量的碎片。
所以我们希望一个文件可以切分成若干小块,使用链表
串接起来。
这样资源利用率当然高了,不过我们知道链表它的访问效率并不高,也就是说 每次都得从第一块开始,沿着链表往后找,非常消耗时间。
我们可以想想在图书馆怎么找书的,是不是有类似一张图书——位置的对应表,我们按图索骥即可。于是引入了索引式
,使用专门的一个磁盘块来存放文件属性&文件所占的磁盘块。这个块叫inode
。
怎么记录目录占有的磁盘块
既然每个文件都有一个inode来描述,每个目录当然也需要一个inode,其中存放了目录的属性以及这个目录内容的磁盘块号。
比如要读取/tmp/test.log,查找次序是这样的:
根目录inode->根目录磁盘块->
tmp目录inode->tmp目录磁盘块->
test.log的 inode->读取磁盘块
流程相当复杂,特别在删除的时候,很容易就糟了。
比如想删除/tmp/test.log
需要
- 目录中删除文件
- 释放inode到空闲的节点池
- 将磁盘块释放到空闲的磁盘池
如果某一步出错,就可能出问题。为了解决这种问题,引入了日志
。也就是说在操作之间把规划列出来,形成日志,然后按照列出来的规划进行操作,只有所有的步骤走完了才能擦除日志。
如果在某一步崩溃了,系统重启的时候会再检查日志项,发现哪些没做,则重新来一遍即可。
如何管理空闲块
我们已经解决了怎么存放文件和目录的问题。但是我们还需要知道哪些地方没有使用,也就是空闲的块在那里。也就是说把空闲块管理起来,统一进行空间分配。
我们同样可以把空闲块组成一个链表
,然后分配的时候就遍历一下链表即可。但是存在一个问题,如果磁盘块号是32位,则每个块都得花32位的空间,如果空闲块非常多,则浪费极大。
既然我们只是要知道某个地方是否被占用,而某个地方只存在占用和未占用两种可能,不妨使用一张位图
,对于每个磁盘块,如果使用了,则标记为1,没用就标记为0。这样,每个磁盘块只是使用了一位来标记,非常节省空间。
文件系统
我们以Linux ext2文件系统为例来看一下。
硬盘主要由MBR
与分区构成。
其中MBR中有引导代码与磁盘分区表
- 引导代码
- 分区表:记录每个分区的起始位置,已经哪个磁盘分区是主分区(活动分区)。对于主分区,系统会找到它,然后装载这个分区中的
引导块
引导块里面有什么?每个分区都会一个引导块,如果本分区里面存放有操作系统,则会通过引导块来进行装载。
磁盘分区表只有64字节,而每个分区项占用16字节,则只能容纳4个分区。如果我们想有多于4个分区,则可以把其中一个设为扩展分区,然后继续划分逻辑分区
即可。
每个分区由引导块和块组构成。
每个块组中有:
- 磁盘块位图
- inode位图
- inode表:存放文件和目录的inode
- 数据块
打个比方
打个不太恰当的比方,作为收尾。
可以把硬盘看做一个大仓库,而磁盘控制器就是理货员,没有文件系统之前,外面的人(应用)取货和送货都需要直接于理货员打交道,告诉他应该放那里,或者从那里取,非常的麻烦。
所以我们又请了一个仓管员
(文件系统),由他来打理整个仓库,他需要对仓库里面放了什么东西,有多少空闲的地方了如指掌,所以外面的人只需要告诉仓管员
要取什么文件,以及文件存放的路径即可。
那么仓管员
是如何管理仓库的呢?
首先为了简化管理,他把若干房间(磁盘块)合在一起管理,形成簇
(块组)。
然后在簇里面分一些房间专门来存放每个文件
存放的具体位置,这种专门用来表示“文件——磁盘块”的映射关系的数据结构就叫inode
。所以说如果要取文件的话,则可以先查看目录的inode,在里面可以找到下一级目录的inode号,然后可以去下一级目录的inode里面找,一级一级的下去,最后可以找到文件inode,即可知道文件存在哪些具体的磁盘块呢。
那么怎么存放数据呢?
仓管员
把每个房间用一个格子表示,如果里面放了东西,则格子标1,如果没放,则标0 。这么通过这幅位图
,就可以轻松知道那些房间是空余的了。