实战 FAT12 文件系统

2020-07-01 18:09:23   最后更新: 2020-07-02 16:25:58   访问数量:90




经过一系列的文章,我们终于完成了从实地址模式跳转到保护模式,并且实现了分段、分页以及保护模式下的中断与异常机制

保护模式究竟“保护”了什么

 

可是我们除了最初的时候,在磁盘第一个扇区写入一段代码来实现最初“hello world”,此后,我们就一直用 DOS 来拉起我们的“操作系统”,那么,你是不是有种不够尽兴的感觉?明明说好要写自己的操作系统,却要用别人的操作系统来引导,我们能不能自己写一个引导区呢?

答案当然是可以的,编写一个引导区看起来是如此的容易,只需要创建一个新的文件,里面指定被载入内存的初始地址,然后在引导扇区的代码中先将这个新的文件内容载入内存,再用一条跳转指令,跳转过去即可

而这个“新的文件”就被称为操作系统的 loader,他负责在操作系统启动前进行一系列的准备工作,然后从实地址模式跳转到保护模式,再加载内核

 

既然要让引导扇区找到磁盘上的文件,那我们首先要来看看磁盘空间是如何构成的

通常,磁盘拥有远大于内存的容量,要想合理利用这些容量,就需要将他们进行划分,让数据能够在各自的区域内,从而方便迅速定位数据

 

 

磁道、柱面和磁头数

如图所示,我们可以将硬盘看做是多个光盘的组合,而每个“光盘”都被称为一个“盘片”,由于盘片位于硬盘盒内,不在暴露在空气中,因此,盘片得以更加高速的旋转,且数据密度也达到光盘所无法企及的程度,但其原理其实与光盘仍然非常类似

而正如上面的图上所示,硬盘中并不只有一个盘片,也不只有一个磁头,所有的同心旋转的盘片经由磁头扫过的磁道共同构成了一个圆柱面,这就是“柱面”

 

如上图所示,借由盘片的旋转,让磁头能够读取到的一圈圈轨道,就是“磁道”,数据就位于磁道上

 

扇区、簇与页

每连续的 512 字节数据组成了一个扇区,若干个扇区又组成了一个簇,簇是磁盘空间分配的最小单位,也就是说,你即使创建一个 1 字节的文件,实际上这个文件至少也要占用磁盘 1 簇的空间

而每次内存与磁盘的交互中,操作的最小单位就是一页,1 页由若干簇构成,通常是 4096B

 

磁盘容量的计算

根据上面的原理,我们可以知道一个磁盘的容量就等于:

磁盘容量 = 盘片数 * 每盘片磁道数 * 每磁道扇区数 * 512

 

FAT12 是一种“古老的”文件系统,到现在,软盘所使用的文件系统也通常是 FAT12,但其他地方已经基本上没有使用之处了

不过现在的 FAT32 与 FAT12 从结构上来说是非常类似的,所以我们本文以 FAT12 来做讲解,未来会有专门的文章来介绍各个文件系统之间的区别,敬请期待

本文,我们以一个 1.44M 的软盘为例进行介绍:

 

 

如上图所示,一个 1.44M 的软盘,可以划分为 2879 个扇区,共分为图上所示的五个区域

 

引导扇区

第一个扇区就是引导扇区,他的具体取值是固定的:

FAT12 引导扇区格式
名称偏移长度内容软盘参考值
BS_jmpBoot03jmp LABEL_STARTnop
BS_OEMName38厂商名'ForrestY'
BPB_BytsPerSec112每扇区字节数0x200(512)
BPB_SecPerClus131每簇扇区数0x01
BPB_RsvdSecCnt142Boot记录占用多少扇区0x01
BPB_NumFATs161共有多少FAT表0x02
BPB_RootEntCnt172根目录文件数最大值0xE0 (224)
BPB_TotSec16192扇区总数0xB40(2880)
BPB_Media211介质描述符0xF0
BPB_FATSz16222每FAT扇区数0x09
BPB_SecPerTrk242每磁道扇区数0x12
BPB_NumHeads262磁头数0x02
BPB_HiddSec284隐藏扇区数0
BPB_TotSec32324如果BPB_TotSec16是0,由这个值记录扇区数0xB40(2880)
BS_DrvNum361中断13的驱动器号0
BS_Reserved1371未使用0
BS_BootSig381扩展引导标记0x29
BS_VolD394卷序列号0
BS_VolLab4311卷标'OrangeS0.02'
BS_FileSysType548文件系统类型'FAT12'
引导代码62448引导代码、数据及其他填充字符等 
结束标志5102 0xAA55

 

文件分配表 -- FAT 表

FAT 表又叫“文件分配表”,从图上可以看到,FAT12 具有两个 9 扇区大小的 FAT 表

FAT2 通常是 FAT1 的备份,两者可以认为是一样的

在 FAT 表中,每 12 位被称为一个 FAT 项(FAT Entry),第 0 个和第 1 个 FAT 项始终不使用,从第 2 个 FAT 项开始,每个 FAT 项对应数据区的一个簇,数据区首个簇号为 2,FAT Entry N 正好对应数据区簇号为 N 的簇

每个 FAT 项中存储的是当前文件的当前簇的下一个簇的簇号,如果值大于等于 0xFF8,那么就表示这已经是文件的最后一个簇,0xFF7 则表示这对应了一个坏簇

 

根目录区

根目录区存储了若干条目录条目,每个目录条目长 32 字节,最多存储 BPB_RootEntCnt 个条目

因此可以得到公式:

根目录区扇区数 = (BPB_RootEntCnt * 32)/BPB_BytsPerSec

BPB_RootEntCnt 和 BPB_BytsPerSec 就是上文中起始扇区中定义的相应字段

目录条目的存储内容为:

FAT12 根目录区目录条目内容
名称偏移长度描述
DIR_Name00xB文件名8字节,扩展名3字节
DIR_Attr0xB1文件属性
保留0xC10 
DIR_WrtTime0x162最后修改时间
DIR_WrtDate0x182最后修改日期
DIR_FstClus0x1A2此条目对应的开始簇号
DIR_FileSize0x1C4文件大小

 

数据区

毋庸多言,数据区存储的就是文件的实际内容

如果这个文件实际是一个目录,那么这个簇实际存储的就是这个目录下文件构成的条目列表,具体信息与根目录区中的条目格式相同

 

经过上述 FAT12 分区的介绍,我们就已经可以清楚的知道如何在一个 FAT12 类型的磁盘上寻找一个文件了:

  1. 获取文件系统基本信息 -- 读取位于第 0 个扇区的起始扇区
  2. 计算数据区首个扇区 -- 根据起始扇区中的 BPB_RootEntCnt 字段和 BPB_BytsPerSec 字段计算根目录区大小,从而计算出数据区对应的扇区号
  3. 获取根目录中的文件 -- 从19号扇区开始读取根目录区条目,找到 DIR_NAME 保存的相同文件名的文件或目录,读取对应的簇号 DIR_FstClus
  4. 获取文件内容 -- 通过 DIR_FstClus 存储的簇号找到对应的 FAT 项,同时读取数据区中对应的簇号的文件内容,并根据 FAT 项获取下一簇号递归进行读取,直到 FAT 项标识文件内容损坏或文件读取完成

 

既然我们已经非常清楚了软盘的文件系统结构,你是否想要实践一下看看呢?是否一个软盘的文件系统真的如我们上面所描述的就是这样存储的呢?

 

创建软盘镜像

通过下面的命令可以实现一个虚拟软盘镜像文件的创建:

dd if=boot.bin of=boot.img bs=512 count=1 conv=notrunc

 

建立文件系统

通过下面的命令,可以将刚刚创建的软盘格式化为对应的文件系统格式:

mkfs.vfat floppy.img /*建格式化为vfat文件系统*/

 

向软盘镜像中添加文件

创建挂载目标目录

mkdir /mnt/floppy

 

挂载软盘镜像

mount -o loop boot.img /mnt/floppy

 

你也可以增加 -t vfat 参数指定文件系统格式

 

添加文件

对于已经挂载好的 loop 设备,你可以随意去操作:

cp hello.txt /mnt/floppy

cp world.txt /mnt/floppy

rm /mnt/floppy/hello.txt

 

解除挂载

umount /mnt/floppy

 

查看虚拟软盘镜像文件内容

虚拟软盘镜像内容是一个二进制文件,我们可以通过 vim 来读取:

vim -b boot.img

 

可是这样打开后,你仍然会发现看到了一团乱码,别急,通过下面的 vim 命令就可以切换到十六进制模式展示了:

:%!xxd -g 1

 

 

 

既然引导扇区中要有上述的 BPB 等头信息才能被识别为一个 FAT12 文件系统,那么我们就在我们的引导扇区中加入这些信息:

org 07c00h jmp short LABEL_START ; Start to boot,占用 3 字节 nop ; 空余 1 字节占位 ; FAT12 磁盘头 BS_OEMName DB 'ForrestY' ; OEM String, 8 字节 BPB_BytsPerSec DW 512 ; 每扇区字节数 BPB_SecPerClus DB 1 ; 每簇扇区数 BPB_RsvdSecCnt DW 1 ; Boot 记录占用扇区数 BPB_NumFATs DB 2 ; FAT 表数量 BPB_RootEntCnt DW 224 ; 根目录文件数最大值 BPB_TotSec16 DW 2880 ; 逻辑扇区总数 BPB_Media DB 0xF0 ; 媒体描述符 BPB_FATSz16 DW 9 ; 每FAT扇区数 BPB_SecPerTrk DW 18 ; 每磁道扇区数 BPB_NumHeads DW 2 ; 磁头数(面数) BPB_HiddSec DD 0 ; 隐藏扇区数 BPB_TotSec32 DD 0 ; wTotalSectorCount为0时这个值记录扇区数 BS_DrvNum DB 0 ; 中断 13 的驱动器号 BS_Reserved1 DB 0 ; 未使用 BS_BootSig DB 29h ; 扩展引导标记 (29h) BS_VolID DD 0 ; 卷序列号 BS_VolLab DB 'OrangeS0.02' ; 卷标, 必须 11 个字节 BS_FileSysType DB 'FAT12 ' ; 文件系统类型, 8 字节 LABEL_START:

 

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

 

实现一个操作系统

 






文件系统      文件      磁盘      fat      loader     


京ICP备15018585号