• FAT12 文件系统(摆脱术语用实际例子介绍)


    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 个部分,如下:

    在这里插入图片描述

    2. MBR (Main Boot Record)

    MBR (Main Boot Record) 主引导记录占用大小为 1 个扇区,即 512 B。在这个扇区里记录了整个文件系统的组织结构信息和引导程序两部分内容。

    标识偏移量类型大小默认值描述
    BS_JmpBoot0db3-跳转指令
    BS_OEMName3db8MSWIN4.1OEM字符串,必须为 8 个字符,不足会以空格填充
    BPB_BytePerSec11dw20x200每个扇区字节数
    BPB_SecPerClus12db11每簇占用的扇区数
    BPB_RsvdSecCnt14dw21Boot占用的扇区数
    BPB_NumFATs16db12FAT表的数量
    BPB_RootEntCnt17dw20xE0根目录可容纳的目录项数
    BPB_TotSec1619dw20xB40逻辑扇区总数
    BPB_Media21db10xF0媒体描述符
    BPB_FATSz1622dw29每个FAT占用扇区数
    BPB_SecPerTrk24dw20x12每个磁道扇区数
    BPB_NumHeads26dw22磁头数
    BPB_HiddSec28dd40隐藏扇区数
    BPB_TotSec3232dd40若BPB_TotSec16是0,则在这里记录扇区总数
    BS_DrvNum36db10中断 13(int 13h)的驱动器号
    BS_Reserved137db10未使用
    BS_Bootsig38db10x29扩展引导标志
    BS_VolID39dd40卷序列号
    BS_VolLab43db11-卷标,必须为11个字符,不足会以空格填充
    BS_FileSysType54db8FAT12文件系统类型,必须是8个字符,不足以空格填充
    BOOT_Code62db4480x00引导代码,由偏移0字节(BS_JmpBoot)跳转过来
    END510db20x55, 0xAA系统引导标识,引导扇区结束标识

    BPB_NumFATs: 描述在存储介质中 FAT 表的数量。此处虽然规定最小设置的值为 1,但是为了能够起到恢复文件的作用,一般建议设置为 2,即表示拥有两份 FAT 表。

    BPB_Media: 描述存储介质类型,对于不可移动的存储介质,标准值为 0xF8,对于可移动的存储介质,常用值为 0xF0
    。该字段的合法值有 0xF00xF80xF90xFA0xFB0xFC0xFD0xFE0xFF。此处写入的值也必须向 FAT 的第 0 项的最后一个字节写入同样的值。

    3. FAT 表

    本文采用两个 FAT 表格式的 FAT12 文件系统,由于 FAT2 是用于数据恢复作用,因此 FAT2 的内容与 FAT1 表的内容完全相同,即是拷贝了 FAT1 一份。

    FAT12 文件系统以簇 (Cluster) 为单位分配数据区(管理扇区),每个簇大小为 BPB_NumFATs * BPB_RootEntCnt 个字节。

    FAT 表中的表项位宽与 FAT 类型有关,FAT12 文件系统的表项位宽为 12bitFAT16 的表项位宽为 16bit,而 FAT32 的表项位宽为 32bitFAT 表中的表项与数据区的簇是一一对应的关系,即一个表项对应数据区的一个簇大小的内存单元。

    FAT 表项的取值如下:

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

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

    4. 根目录和数据区

    根目录区只保存目录项 (BootEntry) 信息,数据区的不仅可以保存目录项信息,也可以保存文件数据。目录项是由一个 32B 组成的结构体,目录项本身可以表示一个目录,也可以表示一个文件,其中记录着名字、长度 以及数据起始簇号等信息。其完整结构如下:

    名称偏移长度描述
    DIR_Name0x0011文件名 8B,扩展名 3B
    DIR_Attr0x0B1文件属性
    保留0x0C10保留位
    DIR_WrtTime0x162最后一次写入时间
    DIR_WrtDate0x182最后一次写入日期
    DIR_FstCtus0x1A2起始簇号
    DIR_FileSize0x1C4文件大小

    其中 DIR_FstClus 字段描述的是文件在磁盘中存放的具体位置,由于 FAT[0]FAT[1] 已明确其作用不能用于数据区的簇索引,因此这里的值不能取 01,有效值将从 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 大小。

    在这里插入图片描述
    在这里插入图片描述

    5.2. 准备好要放置的文件

    需要事先准备好需要放置进去的文件,这里笔者准备了两个。一个名为 imboot.txt,其中可以看到只有简单的一句话,这段内容长度只有 32 KB,在文件系统中会占据一个簇的大小。

    另一个名为 BPB.txt 的文件,这里的内容是摘抄一篇有关 FAT 文件系统中 BPB 介绍的内容,该文件总长度为 2488 KB,按照 FAT12 中规定的每簇中仅包含 1 个扇区,即每个簇为 512 KB,因此按照道理该文件将会在文件系统中为其分配 5 个簇来存储,即 5 * 512 = 2560KB。这里读者需要自己做实验的话,随便填充文件内容,只要超过 1024KB 即可,目的是为了查看 FAT 表项的索引簇号的原理,因此需要必须超过一个簇大小的文件。

    在这里插入图片描述

    5.3. 放置文件进根目录

    依次选择 镜像 > 加入,找到提前准备好的文件,选择即可。
    在这里插入图片描述

    5.4. 导出为软盘格式

    可以看到我们准备好的文件已经放置进来。
    在这里插入图片描述
    依次选择 文件 > 另存为,这里的格式选择 vfd 或者 ima 格式均可。(其它格式笔者并未尝试,可以自行选择尝试,记得留言告诉笔者哦❤️)

    在这里插入图片描述

    5.5. 打开输出的镜像文件

    这里笔者选择的使用 VSCode 软件,当然还有很多其它文本查看工具可以以 Hex 格式阅读文本,根据个人习惯选择即可。

    首先简单看一下这里的内容,第一个扇区就不用看了,第一个扇区主要记录文件系统的结构信息以及引导程序,这里直接定位到了第二个扇区的起始位置 200h,虽然暂时还不明白这串内容是什么,但至少我们已经发现了,第一个扇区的最后两个字节为 0x550xAA,这起码说明我们的文件格式是没问题的。
    在这里插入图片描述

    5.6. 查看根目录区

    根据 FAT12 文件系统的结构,MBRFAT[1]FAT[2] 分别占用 1 个扇区、9 个扇区、9 个扇区,即根目录的起始扇区号应该为 1 + 9 + 9 = 19。十六进制的地址为 19 * 512 = 2600h。OK,我们直接定位到文件的 2600h 的位置。

    在这里插入图片描述

    其实你已经从文件右侧的 ASCII 码解码器显示的文本中看到了刚才我们放入的两个文件名。由于 Inter 采用的大端存储,即低字节存储在地位,高位字节存储在高位,因此这里看到的字母是顺序的。

    我们已经知道了根目录是由一个个目录项组成的,而在 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];		// 文件大小
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    先来看第一个目录项:

    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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从这里我们可以看出这个目录项是对 imboot.txt 文件的描述,文件属性为 0x00 表示普通文件,可任意读写。DIR_FstClus 的值为 0x0007,表示该文件在数据区的起始簇号为 7DIR_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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从文件名可以看出,该目录项是对 BPB.txt 文件,同样属性是普通文件。DIR_FstClus 的值为 0x0002,表示该文件在数据区的起始簇号为 2DIR_FileSize 的值为 0x09B8,表示该文件大小为 2488 B,可以对比 上图 中文件大小,是一样的。

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

    当我们找到一个目录项后,最重要关心的是该文件保存在数据区的起始簇号,根据这里的簇索引便可以在数据区找到该文件对应的第一个簇,紧接着再去查 FAT[1] 表中的对应表项(例,文件起始簇号为 20,需要查看 FAT 表中的第 20 项),根据表项内容知道需要继续找下一个簇还是已经找完所有簇到了文件结束位置。具体流程大概如下图这样:

    在这里插入图片描述

    5.8. FAT 表的查看方式

    FAT12FAT 表的每个表项长度为 12 bit,加上大端存储的缘故,这里阅读起来并不会那么直观。笔者来解读一下这里应该如何阅读。

    在这里插入图片描述
    上图正是以文中的 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 号簇对应的即为数据区的起始簇,这个内容在上文也提到过,为了避免翻来翻去,笔者将上文截图贴在这里。

    在这里插入图片描述

    那么这样计算的话,第 7 簇就应该在第 2 簇的基础上再加上 5 个簇的大小,即得到 imboot.txt 在数据区存储位置,(33 + 5) * 512 = 4C00h,让我们直接定位到文件的 4C00h 的位置。

    在这里插入图片描述

    虽然这里我们很直观的看到,该文件的所有内容都被保存在这里,但对于整套检索流程并没有结束,在查阅完第 7 号簇后,应立马去 FAT 表查看第 7 个表项,这里我们只用看 FAT1 表即可,定位到 FAT1 表的位置 200h

    在这里插入图片描述

    通过上文我们已经会查看 FAT 表项了,这里的 FAT[7] 的值为 FFFh,表示该簇为最后一个簇,到这里将不会继续索引下去,文件内容结束。

    5.10. 查看 BPB.TXT 文件

    已经分析过 imboot.txt 文件,再来看 BPB.txt 将会非常的快了。根据上文分析,BPB.txt 文件在数据区的起始簇号为 2,也就是数据区的第一个簇单元,直接定位到数据区的第一个簇位置 4200h

    在这里插入图片描述

    每个簇大小为 512 B,当读完 2 号簇后,将会去 FAT 表中查询第 2 号表项。

    在这里插入图片描述
    FAT[2] 的值为 003h,那么这时候表示下一个簇号为 3 号簇,再读完数区的第 3 簇后又会回来查询 FAT[3] 表项,接下来的 45 簇以及 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

  • 相关阅读:
    湖仓一体电商项目(八):业务实现之编写写入ODS层业务代码
    最新暴力破解漏洞技术详解
    Intel汇编-内联汇编使用浮点数
    Qt编写ERP库存库房发货电子看板
    贪心算法------单源最短路径问题(Dijkstra)
    架构——方法多态(重载)
    企业计算机服务器中了faust勒索病毒怎么办,faust勒索病毒解密文件恢复
    区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测
    Python变强有这70个项目就够了,一定要收藏
    【iOS】—— 通知、单例、代理以及设计模式总结
  • 原文地址:https://blog.csdn.net/qq_36393978/article/details/126305288