您的位置:首页 > 其它

一个操作系统的实现(11)-让操作系统进入保护模式

2016-06-15 13:33 441 查看
这节首先介绍了突破引导扇区只有512字节的原理,然后介绍了FAT12文件系统,最后通过实验加载loader并将控制权交给loader来实现突破512字节的束缚。


突破512字节的限制

前面所用的引导扇区只有512字节。然而实际上操作系统在启动过程需要做的事情是很多的。所以需要通过某种方法突破512字节的限制。

那么如何突破512字节的限制呢?一种方法是再建立一个文件,通过引导扇区把它加载到内存,然后把控制权教给它。这样,512字节的束缚就没有了。

这里被引导扇区加载进内存的并不是操作系统的内核。因为从开机到开始运行,操作系统经历了“引导→加载内核入内存→跳入保护模式→开始执行内核”这样一个过程。也就是说,才内核开始执行之前不但要加载内核,而且还有准备保护模式等一系列工作,如果全都交给引导扇区来做,512字节很可能是不够用的。因此,这里加载进内存的并不是内核,而是另外一个模块叫Loader。引导扇区把Loader加载进内存并把控制权交给它。上面所说的其他工作都交给Loader来做。Loader没有512字节的限制。所以会灵活很多。

接下来最主要的是如何找到Loader文件并加载进入内存。首先介绍FAT12文件系统


FAT12

FAT的全称是File Allocation Table。它是DOS时代就开始使用的文件系统(File System),现在的软盘上面仍旧使用此文件系统。FAT把磁盘划分成若干层次以方便组织和管理,这些层次如下:
扇区(Sector):磁盘上的最小数据单元。
簇(Cluster):一个或多个扇区。
分区(Partition):通常指整个文件系统。

下面是FAT12格式的软盘的结构:





引导扇区

首先是引导扇区,它位于第0个扇区。它的结构如下图





引导扇区有一个很重要的数据结构叫做BPB(BIOS ParameterBlock),它以
BPB_
开头。以
BS_
开头的域不属于BPB,只是引导扇区(Boot
Sector)的一部分。


FAT

可以看到有两个FAT表,FAT2可看作是FAT1的备份,他们通常是一样的。FAT有点像是一个位图。每12位称为一个FAT项(FATEntry),代表一个簇。

通常FAT项的值代表的是文件下一个簇号。从这里可以计算出FAT12中数据区的最大簇号是
2^12=4K
,如果每簇512字节,那么最大数据量是
4K×512B=2MB


当FAT表项的值大于或等于
0xFF8
时,表示当前簇已经是文件的最后一个簇。如果值为
0xFF7
,表示它是一个坏簇。

其中第0个和第1个FAT项始终不使用,从第2个FAT项开始表示数据区的每一个簇。也就是说,第二个FAT项表示数据区的第一个簇,所以数据区的第一个簇号是2。


根目录区

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

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





根目录区主要定义了
名称
属性
时间
开始簇号
大小


数据区

数据区的簇号从2开始。这是因为上面所说的FAT表项从第二个开始。因为根目录区长度不是固定的。所以需要计算数据区的第一个簇号的位置。


如何读取某一文件

首先是进入根目录区根据文件名和属性来寻找文件。找到文件目录项后根据目录向中的开始簇号读取文件第一簇的信息,接下来查看FAT表项,找到文件的下一簇号是啥?如果小于0xFF7,则数据没读取完,如果大于或等于0xFF8则说明文件读取结束

接下来,实现一个最简单的loader并实现加载过程。主要有如下几步:


制作一个DOS可以识别的引导盘

引导扇区需要有BPB等头信息才能被微软识别,我们首先加上它,代码大致如下:


现在的软盘已经能够被DOS和Linux识别了,我们已经可以方便地往上添加或删除文件了。


编写一个简单的loader程序

要将
Loader
加载到内存中,首先需要有一个
Loader
。所以接下来就是写一个最简单的loader,代码如下:


将此代码大保存在
loader.asm
文件中。这段代码被编译成
.COM
文件直接在DOS下执行,效果是在屏幕中央输出字符
L
,然后进入死循环。在这里,我们用下面的命令行来编译:


这里面编译出的二进制代码加载到内存的任意位置都可以正确执行,但是我们要扩展它,为了将来的执行不会出现问题,要保证把它放入某个段内偏移0x100的位置。


加载loader进入内存


int 13h

加载软盘上的一个文件进入内存,使用的是BIOS中断
int 13h
。它的用法如下图:





从上图可以看出,中断需要的参数不是从第0扇区开始的扇区号,而是柱面号、磁头号以及在当前柱面上的扇区号三个分量。所以要通过下图方法来转换:


软盘相对扇区号的转换





转换的原理如下:

首先,1.44M的软盘结构:一个软盘包括2个盘面(0和1),每个盘面有80条磁道(磁柱),每个磁道有18个扇区,每个扇区大小位512Byte。所以总容量:
2×80×18×512Byte=1474569Byte=1.44MB


然后,从第0扇区开始一次编号叫做相对扇区,它与物理位置的关系如下:
0面,0道,1扇区             0
0面,0道,2扇区             1
0面,0道,3扇区             2
...
0面,0道,18扇区           17
1面,0道,1扇区            18
...
1面,0道,18扇区           35
0面,1道,1扇区            36
...
0面,1道,18扇区           53
1面,1道,1扇区            54


读软盘扇区

因为loader可能包含多个扇区,所以接下来写一个读软盘扇区的函数:


上面的代码用到了堆栈,所以程序开头要初始化
ss
esp



读扇区的函数写好了,接下来就开始在软盘中寻找
Loader.bin


寻找loader

主要包括两个寻找:
在根目录区寻找
Loader
的第一个扇区
在FAT表中寻找
Loader
的其余扇区


根目录区寻找loader.bin



上面的代码的逻辑过程是:遍历根目录区所有的扇区,将每一个扇区加载入内存,然后从中寻找文件名为
loader.bin
的条目,指导找到为止。找到的那一刻,
es:di
是指向条目中字母N后面的哪个字符。其中有一些宏定义如下:


还有一些变量和字符串的值定义如下:


读取过程中会打印一些字符,打印字符串的函数如下:


loader的第一个扇区找到了,接下来寻找loader的剩下扇区,在FAT表项中寻找下一个扇区号。


由扇区号寻找FAT项的值



上面寻找loader的工作已经做完了,接下来加载loader:


向loader交出控制权

万事具备,只差最后一步,向loader交出控制权,可以理解为直接跳转到loader所在的代码执行:


接下来看成果


bochs调试与运行



运行结果如下:





源代码


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息