天天看点

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

1. FAT12 文件系统

说起来

FAT12

文件系统的历史相当久远,早在

DOS

系统的时代就使用

FAT12

作为文件系统使用,一直沿用至今仍会在软盘的结构上使用

FAT12

格式。

当软盘以

FAT12

格式组织格式化后将会以如下标准设定:

  • 两个磁头;
  • 每个磁头有

    80

    个磁道;
  • 每个磁道有

    18

    个扇区;
  • 每个扇区大小为

    512

    字节。

标准

FAT12

软盘空间:

2 * 80 * 18 * 512

=

1474560B

=

1440KB

=

1.44MB

因此一个标准的

1.44MB

大小的

FAT12

格式软盘共有

2 * 80 * 18

=

2880

个扇区。这

2880

个扇区被分为

5

个部分,如下:

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

2. MBR (Main Boot Record)

MBR (Main Boot Record)

主引导记录占用大小为

1

个扇区,即

512 B

。在这个扇区里记录了整个文件系统的组织结构信息和引导程序两部分内容。

标识 偏移量 类型 大小 默认值 描述
BS_JmpBoot db 3 - 跳转指令
BS_OEMName 3 db 8 MSWIN4.1 OEM字符串,必须为 8 个字符,不足会以空格填充
BPB_BytePerSec 11 dw 2 0x200 每个扇区字节数
BPB_SecPerClus 12 db 1 1 每簇占用的扇区数
BPB_RsvdSecCnt 14 dw 2 1 Boot占用的扇区数
BPB_NumFATs 16 db 1 2 FAT表的数量
BPB_RootEntCnt 17 dw 2 0xE0 根目录可容纳的目录项数
BPB_TotSec16 19 dw 2 0xB40 逻辑扇区总数
BPB_Media 21 db 1 0xF0 媒体描述符
BPB_FATSz16 22 dw 2 9 每个FAT占用扇区数
BPB_SecPerTrk 24 dw 2 0x12 每个磁道扇区数
BPB_NumHeads 26 dw 2 2 磁头数
BPB_HiddSec 28 dd 4 隐藏扇区数
BPB_TotSec32 32 dd 4 若BPB_TotSec16是0,则在这里记录扇区总数
BS_DrvNum 36 db 1 中断 13(int 13h)的驱动器号
BS_Reserved1 37 db 1 未使用
BS_Bootsig 38 db 1 0x29 扩展引导标志
BS_VolID 39 dd 4 卷序列号
BS_VolLab 43 db 11 - 卷标,必须为11个字符,不足会以空格填充
BS_FileSysType 54 db 8 FAT12 文件系统类型,必须是8个字符,不足以空格填充
BOOT_Code 62 db 448 0x00 引导代码,由偏移0字节(BS_JmpBoot)跳转过来
END 510 db 2 0x55, 0xAA 系统引导标识,引导扇区结束标识

BPB_NumFATs: 描述在存储介质中

FAT

表的数量。此处虽然规定最小设置的值为

1

,但是为了能够起到恢复文件的作用,一般建议设置为

2

,即表示拥有两份

FAT

表。

BPB_Media: 描述存储介质类型,对于不可移动的存储介质,标准值为

0xF8

,对于可移动的存储介质,常用值为

0xF0

。该字段的合法值有

0xF0

0xF8

0xF9

0xFA

0xFB

0xFC

0xFD

0xFE

0xFF

。此处写入的值也必须向

FAT

的第

项的最后一个字节写入同样的值。

3. FAT 表

本文采用两个

FAT

表格式的

FAT12

文件系统,由于

FAT2

是用于数据恢复作用,因此

FAT2

的内容与

FAT1

表的内容完全相同,即是拷贝了

FAT1

一份。

FAT12

文件系统以簇 (

Cluster

) 为单位分配数据区(管理扇区),每个簇大小为

BPB_NumFATs * BPB_RootEntCnt

个字节。

FAT

表中的表项位宽与

FAT

类型有关,

FAT12

文件系统的表项位宽为

12bit

FAT16

的表项位宽为

16bit

,而

FAT32

的表项位宽为

32bit

FAT

表中的表项与数据区的簇是一一对应的关系,即一个表项对应数据区的一个簇大小的内存单元。

FAT

表项的取值如下:

FAT 项 可取值 描述
BPB_Media 磁盘标识字,低字节需与 BPB_Media 数值保持一致
1 FFFh 表示第一个簇已占用
2 ~ N 000h 可用簇
002h~FEFh 已用簇
FF0h~FF6h 保留簇
FF7h 坏簇
FF8h~FFFh 文件的最后一个簇

[注]:FAT[0] 和 FAT[1] 始终不作为数据区的索引使用。

4. 根目录和数据区

根目录区只保存目录项 (

BootEntry

) 信息,数据区的不仅可以保存目录项信息,也可以保存文件数据。目录项是由一个

32B

组成的结构体,目录项本身可以表示一个目录,也可以表示一个文件,其中记录着名字、长度 以及数据起始簇号等信息。其完整结构如下:

名称 偏移 长度 描述
DIR_Name 0x00 11 文件名 8B,扩展名 3B
DIR_Attr 0x0B 1 文件属性
保留 0x0C 10 保留位
DIR_WrtTime 0x16 2 最后一次写入时间
DIR_WrtDate 0x18 2 最后一次写入日期
DIR_FstCtus 0x1A 2 起始簇号
DIR_FileSize 0x1C 4 文件大小

其中

DIR_FstClus

字段描述的是文件在磁盘中存放的具体位置,由于

FAT[0]

FAT[1]

已明确其作用不能用于数据区的簇索引,因此这里的值不能取

1

,有效值将从

2

开始。

根目录占用扇区数的计算方法为:

(BPB_RootEntCnt * 32 + BPB_BytesPerSec - 1) / BPB_BytesPerSec = (224 * 32 + 512 - 1) / 512 = 14

,根目录区的扇区起始号为

MBR + FAT[0] + FAT[1] = 1+ 9 + 9 = 19

,数据区的扇区起始号为

Root + Sizeof( Root ) = 19 + 14 = 33

5. 用实例说明

听了上面这么多的概念性东西,总觉得讲的很虚,让我们用实际的例子来认识这个

FAT12

结构。

5.1. 创建 1.44MB 软盘

需要用到的工具:WinImagne

打开

WinImage

软件,选择 文件 > 新建,选择

1.44MB

大小。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接
【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

5.2. 准备好要放置的文件

需要事先准备好需要放置进去的文件,这里笔者准备了两个。一个名为

imboot.txt

,其中可以看到只有简单的一句话,这段内容长度只有

32 KB

,在文件系统中会占据一个簇的大小。

另一个名为

BPB.txt

的文件,这里的内容是摘抄一篇有关

FAT

文件系统中

BPB

介绍的内容,该文件总长度为

2488 KB

,按照

FAT12

中规定的每簇中仅包含

1

个扇区,即每个簇为

512 KB

,因此按照道理该文件将会在文件系统中为其分配

5

个簇来存储,即

5 * 512 = 2560KB

。这里读者需要自己做实验的话,随便填充文件内容,只要超过

1024KB

即可,目的是为了查看

FAT

表项的索引簇号的原理,因此需要必须超过一个簇大小的文件。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

5.3. 放置文件进根目录

依次选择 镜像 > 加入,找到提前准备好的文件,选择即可。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

5.4. 导出为软盘格式

可以看到我们准备好的文件已经放置进来。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

依次选择 文件 > 另存为,这里的格式选择

vfd

或者

ima

格式均可。(其它格式笔者并未尝试,可以自行选择尝试,记得留言告诉笔者哦❤️)

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

5.5. 打开输出的镜像文件

这里笔者选择的使用

VSCode

软件,当然还有很多其它文本查看工具可以以

Hex

格式阅读文本,根据个人习惯选择即可。

首先简单看一下这里的内容,第一个扇区就不用看了,第一个扇区主要记录文件系统的结构信息以及引导程序,这里直接定位到了第二个扇区的起始位置

200h

,虽然暂时还不明白这串内容是什么,但至少我们已经发现了,第一个扇区的最后两个字节为

0x55

0xAA

,这起码说明我们的文件格式是没问题的。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

5.6. 查看根目录区

根据

FAT12

文件系统的结构,

MBR

FAT[1]

FAT[2]

分别占用

1

个扇区、

9

个扇区、

9

个扇区,即根目录的起始扇区号应该为

1 + 9 + 9 = 19

。十六进制的地址为

19 * 512 = 2600h

。OK,我们直接定位到文件的

2600h

的位置。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

其实你已经从文件右侧的

ASCII

码解码器显示的文本中看到了刚才我们放入的两个文件名。由于

Inter x86

采用的小端存储,即低字节存储在地位,高位字节存储在高位,因此这里看到的字母是顺序的。

我们已经知道了根目录是由一个个目录项组成的,而在

FAT12

结构中,一个目录项长度为

32 bit

,在这里刚好占两行。这里笔者以

C

语言结构体的形式来解读这里的根目录项内容。

将根目录项视为一个

C

语言的结构体:

struct RootEntry {
char DIR_Name[11];			// 前 8B 为文件名,后 3B 为扩展名
char DIR_Attr[1];			// 文件属性
char DIR_Save[10];			// 未使用,保留
char DIR_WrtTime[2];		// 最后一次写入时间
char DIR_WrtDate[2];		// 最后一次写入日期
char DIR_FstClus[2];		// 起始簇号
char DIR_FileSize[4];		// 文件大小
};
           

先来看第一个目录项:

struct RootEntry Entry_01 = {
	.DIR_Name = {0x49, 0x4D, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x20, 0x54, 0x58, 0x54},	// IMBOOT  TXT
	.DIR_Attr = 0x00,	
	.DIR_Save = {0x18, 0x2A, 0x92, 0x7B, 0x0F, 0x55, 0x00, 0x00, 0x00, 0x00},
	.DIR_WrtTime = {0xC3, 0x7B},	// 0x7BC3
	.DIR_WrtDate = {0x0F, 0x55},	// 0x550F
	.DIR_FstClus = {0x07, 0x00},	// 0x0007
	.DIR_FileSize = {0x20, 0x00, 0x00, 0x00}	// 0x00000020 = 32
};
           

从这里我们可以看出这个目录项是对

imboot.txt

文件的描述,文件属性为

0x00

表示普通文件,可任意读写。

DIR_FstClus

的值为

0x0007

,表示该文件在数据区的起始簇号为

7

DIR_FileSize

的值为

0x20

,表示该文件大小为

32 B

再看第二个目录项:

struct RootEntry Entry_02 = {
	.DIR_Name = {0x42, 0x50, 0x42, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x58, 0x54},	// BPB     TXT
	.DIR_Attr = 0x00,	
	.DIR_Save = {0x10, 0x1B, 0x9D, 0x7B, 0x0F, 0x55, 0x00, 0x00, 0x00, 0x00},
	.DIR_WrtTime = {0x7A, 0x7C},	// 0x7C7A
	.DIR_WrtDate = {0x0F, 0x55},	// 0x550F
	.DIR_FstClus = {0x02, 0x00},	// 0x0002
	.DIR_FileSize = {0xB8, 0x09, 0x00, 0x00}	// 0x000009B8 = 2488
};
           

从文件名可以看出,该目录项是对

BPB.txt

文件,同样属性是普通文件。

DIR_FstClus

的值为

0x0002

,表示该文件在数据区的起始簇号为

2

DIR_FileSize

的值为

0x09B8

,表示该文件大小为

2488 B

,可以对比 上图 中文件大小,是一样的。

5.7. 如何根据根目录项查看文件内容

当我们找到一个目录项后,最重要关心的是该文件保存在数据区的起始簇号,根据这里的簇索引便可以在数据区找到该文件对应的第一个簇,紧接着再去查

FAT[1]

表中的对应表项(例,文件起始簇号为

20

,需要查看

FAT

表中的第

20

项),根据表项内容知道需要继续找下一个簇还是已经找完所有簇到了文件结束位置。具体流程大概如下图这样:

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

5.8. FAT 表的查看方式

FAT12

FAT

表的每个表项长度为

12 bit

,加上小端存储的缘故,这里阅读起来并不会那么直观。笔者来解读一下这里应该如何阅读。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

上图正是以文中的

FAT

表为例,可以理解为一个

FAT

表项是由一个字节和另一个字节的一半拼接而成,上图仅是为了容易理解画的示意图。而实际上上图中的这几个字节内容从内存中完全拿出来并排序后会变成这样:

0x00_40_03_FF_FF_F0

,然后再以

12 bit

断之后就会得到这样的效果:

0x004_003_FFF_FF0

,这样正好会与

FAT

表中的数据对应起来,

FAT[0]

0xFF0

表示可移动存储介质,

FAT[1]

0xFFF

表示第

1

个簇已经被占用。

5.9. 查看 IMBOOT.TXT 文件

根据上文分析,

imboot.txt

文件在数据区的起始簇号为

7

,那么首先需要去数据区找到第

7

号簇,根据上文分析,我们知道数据区的起始扇区号为

33

,那么数据区的起始簇的地址为

33 * 512 = 4200h

,这个地址对于数据区来讲是第一个簇,但根目录项中的起始簇有效值是从

2

开始,即根目录中的

2

号簇对应的即为数据区的起始簇,这个内容在上文也提到过,为了避免翻来翻去,笔者将上文截图贴在这里。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

那么这样计算的话,第

7

簇就应该在第

2

簇的基础上再加上

5

个簇的大小,即得到

imboot.txt

在数据区存储位置,

(33 + 5) * 512 = 4C00h

,让我们直接定位到文件的

4C00h

的位置。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

虽然这里我们很直观的看到,该文件的所有内容都被保存在这里,但对于整套检索流程并没有结束,在查阅完第

7

号簇后,应立马去

FAT

表查看第

7

个表项,这里我们只用看

FAT1

表即可,定位到

FAT1

表的位置

200h

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

通过上文我们已经会查看

FAT

表项了,这里的

FAT[7]

的值为

FFFh

,表示该簇为最后一个簇,到这里将不会继续索引下去,文件内容结束。

5.10. 查看 BPB.TXT 文件

已经分析过

imboot.txt

文件,再来看

BPB.txt

将会非常的快了。根据上文分析,

BPB.txt

文件在数据区的起始簇号为

2

,也就是数据区的第一个簇单元,直接定位到数据区的第一个簇位置

4200h

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

每个簇大小为

512 B

,当读完

2

号簇后,将会去

FAT

表中查询第

2

号表项。

【实现操作系统 02】FAT12 文件系统(摆脱术语用实际例子介绍)1. FAT12 文件系统2. MBR (Main Boot Record)3. FAT 表4. 根目录和数据区5. 用实例说明#小结#参考链接

FAT[2]

的值为

003h

,那么这时候表示下一个簇号为

3

号簇,再读完数区的第

3

簇后又会回来查询

FAT[3]

表项,接下来的

4

5

簇以及

FAT[4]

FAT[5]

3

相同,

FAT[5]

中的值为

006h

,在读完数据区的

6

号簇后,来查询

FAT[6]

,发现

FAT[6]

的值为

FFFh

,表示文件结束。

#小结

到这里,相信各位读者已经对

FAT12

文件系统有了清楚的认识,本来是要在接下来介绍使用

FAT12

文件系统实现

Boot

加载

Loader

程序到内存中的内容,但忽然看了一下本文已经超出

8500

字了,阅读到这里显然大家已经累了,那么请休息一会,然后请继续接着看笔者的下一篇文章《使用 FAT12 文件系统实现简单的 Boot 加载 Loader 到内存》

#参考链接

[1] FAT12/16/32 Media Boot Record: https://docs.microsoft.com/en-us/azure/rtos/filex/chapter3#fat121632-media-boot-record

[2] FAT Filesystem: http://elm-chan.org/docs/fat_e.html

[3] exFAT file system specification: https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification

觉得这篇文章对你有帮助的话,就留下一个赞吧^v^*

请尊重作者,转载还请注明出处!感谢配合~

[作者]: Imagine Miracle

[版权]: 本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

[本文链接]: https://blog.csdn.net/qq_36393978/article/details/126305288