第4课 - 主引导程序的扩展(上)
第4课 - 主引导程序的扩展(上)
1. 主引导程序的限制
在前面的课程中讲到,主引导程序的代码量不能超过 512 字节!!
那这 512 个字节存储的主引导程序可以完成操作系统的功能吗?答案显然是否定的。
因此主引导程序最大只能是 512 字节就成为了一种限制,那如何突破这种限制呢?
2. 突破限制的思路
主引导程序完成以下功能:
(1)主引导程序首先完成最基本的初始化工作
(2)然后从存储介质中加载程序到内存中,这一步是重中之重
(3)最后将控制权交由新加载的程序执行(跳转到新加载程序的第一句代码),上面这三步可以在 512 字节的代码量内完成
(4)......
用图表示,如下图所示:
第(1)个功能和第(3)个功能实现起来比较容易,重点是第(2)个功能的实现,即主引导程序如何加载存储介质中的其它程序?
3. 文件系统
文件系统就是指存储介质上组织文件数据的方法(数据组织的方式),下面是 FAT12 文件系统的格式:
3.1 文件系统的示例
(1)FAT12 是 DOS 时代的早期文件系统
(2)FAT12 结构非常简单,一直沿用于软盘
(3)FAT12 的基本组织单位
字节(Byte):基本数据单位
扇区(Sector):磁盘中的最小数据单元
簇(Cluster):一个或多个扇区
3.2 主引导程序大小受限的解决方案
(1)使用 FAT12 对软盘(data.img)进行格式化
(2)编写可执行程序(Loader),并将其拷贝到软盘中
(3)主引导程序(Boot)在文件系统中查找 Loader
(4)将 Loader 复制到内存中,并跳转到入口处执行
3.3 实验:往虚拟软盘中写入文件
— 原材料:FreeDos、Bochs、bximage
-
- Bochs 和 bximage 在前面的课程中已经介绍过了
- FreeDos 是一个古老的 dos 操作系统,在本节课中使用 FreeDos 将软盘 data.img 格式化为 FAT12 格式的文件系统
FreeDos 官网:http://www.freedos.org/
可以在官网上下载 FreeDos 的软盘镜像:(下载界面:http://www.freedos.org/download/)
— 步骤:
-
- 使用 bximage 工具创建虚拟软盘 data.img
-
- 在 freeDos 中进行格式化(FAT12)
更改上节课中的 bochsrc 配置文件,将 freedos.img 作为启动软盘,另外将 data.img 这个软盘插入到 freeDos 系统中。
1 ############################################################### 2 # Configuration file for Bochs 3 ############################################################### 4 5 # how much memory the emulated machine will have 6 megs: 32 7 8 # filename of ROM images 9 romimage: file=/usr/local/share/bochs/BIOS-bochs-latest 10 vgaromimage: file=/usr/share/vgabios/vgabios.bin 11 12 # what disk images will be used 13 floppya: 1_44=freedos.img, status=inserted 14 floppyb: 1_44=data.img, status=inserted 15 # choose the boot disk. 16 boot: a 17 18 # where do we send log messages? 19 # log: bochsout.txt 20 21 # disable the mouse 22 mouse: enabled=0 23 24 # enable key mapping, using US layout as default. 25 keyboard_mapping: enabled=1, map=/usr/local/share/bochs/keymaps/x11-pc-us.map
启动 Bochs ,进入到 freeDos 系统界面:
执行以下操作:
-
- 将 data.img 挂载到 Linux 中,并写入文件
将 data.img 挂载到 linux 中,然后创建一个 test.txt 文件并将该文件写入到挂载点,最后卸载 data.img。
在 Bochs 中查看 B 盘就看到了刚才写入的 test.txt 文件,表明文件写入成功。后面按照同样的方式将 Loader 写入到 data.img 中。
4. Boot 查找目标文件(Loader)
把 Loader 按照上面的方式写入到 FAT12 格式的虚拟软盘中,下一步的工作就是:Boot 查找目标文件(Loader),并读取文件的内容!
4.1 深入 FAT12 文件系统
FAT12 文件系统由引导区、FAT 表、根目录项表和文件数据区组成。
4.2 FAT12 的主引导区
主引导区存储的比较重要的信息是文件系统的类型、文件系统逻辑扇区总数、每簇包含的扇区数等。
主引导区最后以 0x55AA 两个字节作为结束,共占用一个扇区(第 0 扇区)。
第 0 扇区中的部分内容如下表所示:
4.3 实验:读取 data.img 中的文件系统信息
— 步骤:
-
- 创建 Fat12Header 结构体类型(根据上面表格的内容而来)
- 使用文件流读取前 512 字节的内容
- 解析并打印相关的信息
【编程实验:读取 FAT12 文件系统信息】
在 QT 中使用下面的代码查看 FAT12 文件系统第 0 扇区的内容,并对照上面的表格进行验证。
1 #include <QtCore/QCoreApplication> 2 #include <QFile> 3 #include <QDataStream> 4 #include <QDebug> 5 6 #pragma pack(push) 7 #pragma pack(1) 8 9 struct Fat12Header 10 { 11 char BS_OEMName[8]; 12 ushort BPB_BytsPerSec; 13 uchar BPB_SecPerClus; 14 ushort BPB_RsvdSecCnt; 15 uchar BPB_NumFATs; 16 ushort BPB_RootEntCnt; 17 ushort BPB_TotSec16; 18 uchar BPB_Media; 19 ushort BPB_FATSz16; 20 ushort BPB_SecPerTrk; 21 ushort BPB_NumHeads; 22 uint BPB_HiddSec; 23 uint BPB_TotSec32; 24 uchar BS_DrvNum; 25 uchar BS_Reserved1; 26 uchar BS_BootSig; 27 uint BS_VolID; 28 char BS_VolLab[11]; 29 char BS_FileSysType[8]; 30 }; 31 32 #pragma pack(pop) 33 34 void PrintHeader(Fat12Header& rf, QString p) 35 { 36 QFile file(p); 37 38 if( file.open(QIODevice::ReadOnly) ) 39 { 40 QDataStream in(&file); 41 42 file.seek(3); 43 44 in.readRawData(reinterpret_cast<char*>(&rf), sizeof(rf)); 45 46 rf.BS_OEMName[7] = 0; 47 rf.BS_VolLab[10] = 0; 48 rf.BS_FileSysType[7] = 0; 49 50 qDebug() << "BS_OEMName: " << rf.BS_OEMName; 51 qDebug() << "BPB_BytsPerSec: " << hex << rf.BPB_BytsPerSec; 52 qDebug() << "BPB_SecPerClus: " << hex << rf.BPB_SecPerClus; 53 qDebug() << "BPB_RsvdSecCnt: " << hex << rf.BPB_RsvdSecCnt; 54 qDebug() << "BPB_NumFATs: " << hex << rf.BPB_NumFATs; 55 qDebug() << "BPB_RootEntCnt: " << hex << rf.BPB_RootEntCnt; 56 qDebug() << "BPB_TotSec16: " << hex << rf.BPB_TotSec16; 57 qDebug() << "BPB_Media: " << hex << rf.BPB_Media; 58 qDebug() << "BPB_FATSz16: " << hex << rf.BPB_FATSz16; 59 qDebug() << "BPB_SecPerTrk: " << hex << rf.BPB_SecPerTrk; 60 qDebug() << "BPB_NumHeads: " << hex << rf.BPB_NumHeads; 61 qDebug() << "BPB_HiddSec: " << hex << rf.BPB_HiddSec; 62 qDebug() << "BPB_TotSec32: " << hex << rf.BPB_TotSec32; 63 qDebug() << "BS_DrvNum: " << hex << rf.BS_DrvNum; 64 qDebug() << "BS_Reserved1: " << hex << rf.BS_Reserved1; 65 qDebug() << "BS_BootSig: " << hex << rf.BS_BootSig; 66 qDebug() << "BS_VolID: " << hex << rf.BS_VolID; 67 qDebug() << "BS_VolLab: " << rf.BS_VolLab; 68 qDebug() << "BS_FileSysType: " << rf.BS_FileSysType; 69 70 file.seek(510); 71 72 uchar b510 = 0; 73 uchar b511 = 0; 74 75 in.readRawData(reinterpret_cast<char*>(&b510), sizeof(b510)); 76 in.readRawData(reinterpret_cast<char*>(&b511), sizeof(b511)); 77 78 qDebug() << "Byte 510: " << hex << b510; 79 qDebug() << "Byte 511: " << hex << b511; 80 } 81 82 file.close(); 83 } 84 85 int main(int argc, char *argv[]) 86 { 87 QCoreApplication a(argc, argv); 88 89 Fat12Header f12; 90 91 PrintHeader(f12, "E:\\data.img"); 92 93 return a.exec(); 94 }
扇区 0 中除了保存 FAT12 文件系统的信息外,还有一个主引导程序(FreeDOS 中的 format 程序格式化时生成),从上面的表格中也可以看到,扇区 0 的第一条指令就是跳转到 BOOT_CODE 上(表格左侧蓝色的箭头),有两个方法可以进行验证。
方法1:查看 data.img 的内容
方法2:在 Bochs 中将data.img 作为启动软盘,查看打印信息,红色部分就是 format 程序生成的主引导程序。
【实验结论】
(1)FreeDos 中的 format 程序在格式化软盘的时候自动在第 0扇区生成了一个主引导程序,这个主引导程序只打印一个字符串。
(2)文件格式和文件系统都是用于定义数据如何存放的规则,只要遵循这个规则就能够成功读写目标数据。
5. 小结
(1)主引导程序的代码量不能超过 512 字节
(2)可以通过主引导程序加载新程序的方式突破限制
(3)加载新程序需要依赖于文件系统
(4)FAT12 是一种早期用于软盘的简单文件系统
(5)FAT12 文件系统的重要信息存储于 0 扇区
注:本文整理于《狄泰12月提升计划》课程内容
狄泰QQ群:199546072
本人QQ号:502218614