FAT12是DOS时代就开始使用的文件系统,直到现在仍然在软盘上使用,FAT12软盘的被格式化后为:有两个磁头,每个磁头80个柱面(磁道),每个柱面有18个扇区,每个扇区512个字节空间。所以标准软盘的总空间为:

2 * 80 *18 * 512=1474560B=1440K=1.44M

下面是FAT12的结构图:

Untitled

引导扇区

操作系统之所以认识FAT12格式的磁盘,其秘密就在于逻辑0扇区这512B上。如果这512字节的最后两个字节的内容分别是55和AA(0xAA55低字节在前,高字节在后)的话,BIOS在启动时会将这个扇区读取到0:7C00h-0:7DFFh处,然后跳转到0:7C00h处继续执行指令,操作系统即用此来达到引导系统的目的,而这个磁盘就称为引导磁盘。

操作系统标识FAT12文件系统是因为在逻辑0扇区(即引导扇区)处还存储着一个特定的数据结构,此结构有固定的格式,在操作系统将此磁盘格式化时自动生成,具体数据结构如下表所示:

Screenshot_20220116_114137

BS_jmpBoot:是跳转指令,偏移0处的跳转指令必须是合法的可执行的基于x86的CPU指令,如:jmp start,这样可以生成3字节长的指令,(加关键字short的短跳转指令的长度是2字节),指向操作系统引导代码部分。Windows和MS-DOS生成的FAT12启动扇区中的跳转指令是短跳转,如:jmp short LABEL_START,然后加一个nop的空指令来保持3字节的长度。

BPB_BytsPerSec:每扇区的字节数,类型是双字节长,标准分区上的每扇区字节数一般是512B, FAT12的格式下设置为512(0x200h)。

BPB_SecPerClus:每簇扇区数,偏移13处,类型是字节,簇是数据存储的最小单位,在FAT12格式下一般为1,即每簇只有1个扇区(512字节)。

BPB_RsvdSecCnt:Boot记录占用多少扇区,即在FAT1之前的 引导扇区,一般情况下,引导扇区占用1个扇区。

BPB_NumFATs:共有多少个FAT表,默认情况下此字段的值为2,也就是有两个FAT表,FAT1和FAT2的内容相同,当FAT1表出错的时候可以使用FAT2来恢复文件分配表。

BPB_RootEntCnt:根目录文件数最大值,默认为224,每个目录条目占用32B的空间,因此根目录的大小为:224*32/512=14,即占用14个扇区。

BPB_TotSec16:扇区总数=0xB40=2880,这里的扇区总数包括保留扇区(含引导扇区)、FAT表、根目录区以及数据区占用的全部扇区数,如果此域值为0,则BPB_TotSec32必须是非0值。

BPB_Media:描述存储介质类型。对于不可移动的存储介质而言,标准值是0xF8;对于可移动的存储介质,常用值为0xF0。此域的合法值为0xF0、0xF8、0xF9、0xFA、0xFB、0xFC、0xFD、0xFE、0xFF。无论往该字段写入什么值,必须向FAT[0]的低字节写入相同的值。

BPB_FATSz16:每个FAT占用的扇区数=0x9=9,即FAT1占用1—9逻辑扇区,FAT2占用10—18逻辑扇区。

BPB_SecPerTrk:每磁道扇区数=0x12=18,即标准FAT12文件系统中,每个磁道的扇区数就是为18。

BPB_NumHeads:磁头数=0x2=2,该磁盘包括2个磁头,也就是面数是2。

BS_VolLab:指定卷标。

BS_FileSysType:指定文件系统类型。这个类型只是字符串,操作系统不使用该字段来鉴别FAT类文件系统类型。

结构如下所示:

Screenshot_20220122_092852

FAT表

FAT1和FAT2是两个完全相同的FAT表,每个FAT占用9个扇区。其中FAT1占用1—9扇区,FAT2占用10—18扇区。

FAT12文件系统以簇为单位分配数据区的存储空间(扇区),每个簇的长度为BPB_BytsPerSec * BPB_SecPerClus,数据区的簇号与FAT的表项是一一对应的关系。即使文件只有一个字节没FAT12文件系统也会分配一个簇的空间存储它。此种设计方法可以将磁盘存储空间按固定的存储片(页)有效管理起来,进而可以按照文件偏移,分片段访问文件内的数据,就不必将文件里的数据一次性读取出来。

FAT表项的位宽与FAT类型有关系,如FAT12的表项位宽为12bit,FAT32的表项位宽为32bit。当一个文件体积增大时,其所需的磁盘空间也会增加,随着时间推移,文件系统将无法确保文件中的数据存储在连续的扇区中,文件往往会被分为若干个片段,借助FAT表,将不连续的文件片段按簇号连接起来,这个连接与C语言中的单链表极为相似。

首先12个二进制数字表示这个簇指向的下一个簇。FAT表从零开始编号。如果2号簇储存的数字为3,那么说明2号簇指向3号簇。3号簇的12位数字储存的是5的话,那么说明3号簇指向5号簇。这就形成了一个链表,链表的空指针NULL(结尾标志),使用0xFFF表示。

Screenshot_20220123_163149

其中FAT[0]的低8位在数值上与BPB_Media字段保持一致,其余位全部为1。

现在大部分操作系统直接跳过这两个表项的检索,使它们不再参与计算。所以FAT[0]FAT[1]的值页不那么重要了。

根目录区

根目录区的开始扇区号是19,它是由若干个目录条目(Directory Entry)组成,条目最多有BPB_RootEntCnt个,由于根目录区的大小是依赖于BPB_RootEntCnt的,所以长度不固定。

FAT12中,因为BPB_RootEntCnt=0xE0=14*16+0=244,即条目最多为244个,又因为每个条目占用32个字节,故244*32/512=14,即该根目录区占14个扇区,即19—32。

根目录区中的每个条目占用32字节,它的格式如下图:

Untitled1

这里主要定义了文件的名字,属性,最后写入的时间和日期,文件的开始簇数以及文件大小。

数据区

数据区的某一个扇区如果是一个目录,那么这个目录被称为子目录。子目录分为一级、二级……子目录,他们的组织方法和根目录基本一样。

子目录中一定要包含两个目录项:...