自己动手从零写桌面操作系统GrapeOS系列教程——22.文件系统与FAT16
学习操作系统原理最好的方法是自己写一个简单的操作系统。
新买的硬盘和优盘在第一次使用时需要格式化,有时候还需要分区。这是为什么呢?分区和格式化到底是干啥呢?本讲将为大家解开这些疑惑。
一、文件系统
1.分区
首先说一下分区,我们平时看到的C盘、D盘等就是一个个分区。硬盘第一个扇区的一部分固定空间叫做分区表,划分分区就是在这个分区表中记录一下各分区的信息,包括各个分区从哪个扇区开始,到哪个扇区结束等。由于GrapeOS所用虚拟硬盘的空间大小只有4MB,没必要分区,所以我们在MBR中也没有填写分区表。
2.格式化
格式化是在某个分区上做的。如果一个盘没有做分区,那就将整个盘作为一个分区看待,GrapeOS就是这样的。大家如果对硬盘或优盘做过格式化就会知道,格式化的时候会让你选择一种文件系统,常见的选项有NTFS、FAT32、exFAT等。所谓格式化就是将某种文件系统的信息写入到这个分区的一部分扇区上。那什么是文件系统呢?下面来简单介绍一下。
3.文件系统
计算机一开始是没有文件和文件系统概念的。前面我们学习了对硬盘的读写,我们知道对硬盘的读写是按扇区为单位进行的。在读写硬盘的时候我们有用到文件的概念吗?没有。到目前为止,我们用的虚拟硬盘上并没有任何文件系统,就和刚买的新硬盘一样,但并不影响我们读写硬盘。但是在没有文件系统的情况下实际使用会非常麻烦。比如你将多个文件写入到硬盘上,你需要记录每个文件存放在了那些扇区上;如果为一个文件增加了一些内容,需要多占用一些扇区,你需要知道哪些扇区是空闲的。这些问题都是需要文件系统处理的。早期的计算机之所以没有文件系统也能用是因为当时的每个外部存储器上的数据都是为某一件事专用的,有配套的程序做处理,并不能像现在随意往硬盘里存放各种文件。总之,文件系统是为了方便在硬盘或其它存储设备上存储数据而抽象出来的一种数据管理方式。只说是抽象出来的,这个不好理解,文件系统有很多种,需要结合一种具体的文件系统讲解才能明白。下面我们介绍一下GrapeOS中用的文件系统FAT16。
二、FAT16
1.FAT16空间分布
首先大家需要明白两个概念,文件属性和文件内容。文件属性一般包含文件名称、大小、修改日期等信息。文件内容是指文件内具体包含的东西,比如一个文本文件,它的内容就是里面的文本信息。
在FAT16文件系统中,一般会将硬盘或某个分区划分为5个部分:引导扇区、FAT1表、FAT2表、根目录区、数据区。如下图所示:
- 引导扇区就是硬盘或分区的第一个扇区。
- 根目录区存放的就是根目录中文件和文件夹的属性信息。
- 数据区存放的是所有文件和文件夹的内容。
- FAT1表和FAT2表存放的是文件内容的簇号,也就是记录每个文件的内容存放在了哪些扇区中。簇是FAT16数据区中的一个空间单位,每个簇等于若干个扇区,具体等于多少个扇区,需要在引导扇区中设置。在GrapeOS中每个簇设置等于一个扇区。簇是FAT16中用来存放文件数据的基本单位,不可分割,一个簇内的空间不能一部分属于一个文件,而另一部分属于另一个文件。比较特殊的一点是数据区中的簇号不是从0开始的,而是从2开始的。
FAT2表是FAT1表的备份,大小完全相同,正常情况下里面的数据也完全相同。如果发生不正常的情况可以用FAT2表中的数据恢复FAT1表。在GrapeOS中我们不考虑这种不正常的情况,所以舍弃了FAT2表。如下图所示:
2.FAT16引导扇区
FAT16文件系统引导扇区结构表:
名称 | 偏移 | 长度 | 内容 | GrapeOS的值 |
---|---|---|---|---|
BS_jmpBoot | 0 | 3 | 一个短跳转指令 | jmp boot_start nop |
BS_OEMName | 3 | 8 | 厂商名称 | GrapeOS |
BPB_BytsPerSec |
11 | 2 | 每扇区字节数 | 0x0200 |
BPB_SecPerClus |
13 | 1 | 每簇扇区数 | 0x01 |
BPB_RsvdSecCnt |
14 | 2 | 保留扇区数(引导扇区的扇区数) | 0x0001 |
BPB_NumFATs |
16 | 1 | FAT表的份数 | 0x01 |
BPB_RootEntCnt |
17 | 2 | 根目录可容纳的目录项数 | 0x0200 |
BPB_TotSec16 |
19 | 2 | 扇区总数 | 0x2000(4MB) |
BPB_Media | 21 | 1 | 介质描述符 | 0xf8 |
BPB_FATSz16 |
22 | 2 | 每个FAT表扇区数 | 0x0020 |
BPB_SecPerTrk | 24 | 2 | 每磁道扇区数 | 0x0020 |
BPB_NumHeads | 26 | 2 | 磁头数 | 0x0040 |
BPB_HiddSec | 28 | 4 | 隐藏扇区数 | 0x00000000 |
BPB_TotSec32 | 32 | 4 | 如果BPB_TotSec16是0,由这个值记录扇区数。 | 0x00000000 |
BS_DrvNum | 36 | 1 | int 13h的驱动器号 | 0x80 |
BS_Reservedl | 37 | 1 | 未使用 | 0x00 |
BS_BootSig | 38 | 1 | 扩展引导标记 | 0x29 |
BS_VolID | 39 | 4 | 卷序列号 | 0x00000000 |
BS_VolLab | 43 | 11 | 卷标 | Grape OS |
BS_FileSysType | 54 | 8 | 文件系统类型 | FAT16 |
引导代码及其它 | 62 | 448 | 引导代码、数据及其它填充字符等 | |
结束标志 | 510 | 2 | 0xAA55 | 0xAA55 |
从上表中可以看到,FAT16引导扇区中前62个字节是有固定格式的,FAT16的格式化就是将上表中的格式数据写入到引导扇区中。上表中的数据并非每一行都有用,我们用到的有:BPB_BytsPerSec、BPB_SecPerClus、BPB_RsvdSecCnt、BPB_NumFATs、BPB_RootEntCnt、BPB_TotSec16、BPB_FATSz16。根据这些信息就能推断出GrapeOS的硬盘FAT16扇区分布:
3.FAT16目录项
文件夹也叫目录,文件和文件夹的属性都存储在目录项中,在根目录和其它目录中存放的是一个一个的目录项,也就是说文件夹的内容就是目录项列表。每个目录项有32个字节,具体结构如下:
名称 | 偏移 | 长度 | 描述 |
---|---|---|---|
DIR_Name | 0 | 11 | 文件名8字节,扩展名3字节 |
DIR_Attr | 11 | 1 | 目录项属性(0x10代表文件夹,0x20代表文件) |
保留位 | 12 | 10 | 保留位 |
DIR_WrtTime | 22 | 2 | 最后一次写入时间 |
DIR_WrtDate | 24 | 2 | 最后一次写入日期 |
DIR_FstClus | 26 | 2 | 起始簇号 |
DIR_FileSize | 28 | 4 | 文件大小 |
每个扇区可以存放16个目录项。
4.FAT表和FAT表项
前面我们讲到,在FAT16的数据区中是以簇为单位编号的,簇号从2开始依次递增。FAT16的簇号是用16位二进制数表示的,这也是FAT16中16的含义。除了前2个簇号不用,最后的16个簇号有特殊用途,FAT16最多可以管理216-2-16=65518个簇。
在目录项中,DIR_FstClus存放的是起始簇号。如果一个文件或文件夹的内容在一个簇里放不下,需要多个簇,其它簇号需要记录到FAT表中。在FAT16的FAT表中,每两个字节是一个FAT表项,每个FAT表项代表一个簇,从第一个FAT表项开始依次代表簇0、簇1、簇2、簇3、簇4等等,用来表示每个簇是否已被占用或下一个簇。对于每个FAT表项,如果它的值是0表示该簇未使用,可以用来存放新数据,对于一个刚格式化完的硬盘,FAT表中除了前2个表项,其它表项应该都是0。如果FAT表项的值不为0,表示该簇已被占用,而且这个值就是文件内容下一个簇的簇号,这样就实现了文件内容在数据区中的链式存储。从目录项中拿到文件的起始簇号,在起始簇号对应的FAT表项中的值就是存放文件内容的第二个簇号,在第二个簇号对应的FAT表项中的值就是存放文件内容的第三个簇号……举个例子,比如一个文件的起始簇号是5,在第5个FAT表项中存放的是文件内容的第2个簇号,假设第2个簇号是8,则会在第8个FAT表项中存放第3个簇号,以此类推,就像链表一样,直到下一个簇号大于等于0xfff8,表示文件内容结束,请见下图。
上图中表示这个文件共占用3个簇的空间,簇号分别是5、8、9。我们只要把这个簇链表中每个簇的数据从数据区里读取出来,并按簇链表的顺序存放在一起就是文件的完整内容。
FAT表项取值说明:
FAT表项 | 实例值 | 描述 |
---|---|---|
0 | 0xfff8 | 磁盘表示字(实际无用,设为0即可。) |
1 | 0xffff | 第一个簇不可用(实际无用,设为0即可。) |
2 3 …… |
0x0003 0x0004 …… |
0x0000:可用簇 0x00020xffef:已用簇,标识下一个簇的簇号<br>0xfff00xfff6:保留簇 0xfff7:坏簇 0xfff8~0xffff:文件的最后一个簇 |
前面我们已经提到,GrapeOS的FAT表有32个扇区,每个扇区有256个FAT表项,则共有8192个FAT表项。由于数据区簇号是从2开始的,FAT表中的前2个FAT表项不使用,也就是最多能管理8190个簇。我们这里一个簇等于一个扇区,所以这里的FAT表最多能管理8190个扇区。而我们这里的数据区共8127个扇区,FAT表大小够用了。
视频版地址:https://space.bilibili.com/1688387238
配套的代码与资料在:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS操作系统交流QQ群:643474045
博客主页:http://www.cnblogs.com/chengyujia/
欢迎转载,但请保留作者和本文链接,谢谢!
欢迎在下面的评论区与我交流。