【原创】自制操作系统知识储备(一)---流程篇

一、 最终目标
现在有一台裸机,如果要用你写的操作系统来让计算机为你工作,那么你应该像微软公司一样,提供一个安装操作系统的安装盘。这个安装盘可以是软盘或光盘,如果操作系统空间足够小,软盘就足以;如果操作系统空间较大,则需光盘。我们假定自己制作的操作系统足够下,一张软盘就能容纳,因此后面的说明全部都以软盘为例。那么你要做出操作系统的终极目标应是:写出的代码全部存储在软盘上,让用户使用这个软盘来安装到计算机,后面就借助你写出的操作系统来完全控制这台计算机。

二、 实现过程
为了实现这个终极目标,正确的流程和步骤应该是:用户使用你提供的软盘来引导计算机启动,并将软盘上的操作系统内容完全装载到用户的计算机硬盘上。这样,当用户使用你的软盘完成操作系统安装之后,下次打开计算机就只需要从硬盘上启动操作系统了,这个时候用户就已经完全脱离你的软盘了。

三、 所需工作
为了实现过程,自己写操作系统就需要以下任务:

  1. 编写操作系统程序。
  2. 把操作系统程序制作在启动软盘上。
  3. 启动软盘要把操作系统程序装载到硬盘。

四、 详细分解

  1. 编写操作系统程序
    操作系统可以简单,也可以复杂。这个取决于制作者的思路和制作操作系统的目的,但最基本的功能应该具备以下能力:设备管理、文件操作、办公、网络、娱乐、游戏等。

  2. 把操作系统程序制作在启动软盘上。 把操作系统程序经过汇编后以BIN二进制的格式存储到软盘上即可。但是在开发过程中,会引发出很多的问题:
    (1) 开发环境问题:虚拟机
    理论上讲,在开发操作系统过程中,每修改一次程序,就需要重新写到软盘上,然后用软盘引导计算机启动,来验证是否满足需要。如果不满足,就需要关机取出软盘来重新操作。但是这样的过程无疑不现实,首先现在的计算机都没有软盘,就算有软盘这样频繁操作也很困难。
    为了解决这个问题,我们需要用虚拟机。这样,每次关机、取出软盘、修改操作系统程序、重写软盘、插入软盘和启动机器的过程,就可以用虚拟机关机、修改软盘镜像和开启虚拟机的过程来代替。这样大大的提高了程序调试的便捷性。
    但是,就算这样的过程,也不简单。比如操作系统程序哪怕是只修改了一个字符,也需要重新启动虚拟机和加载软盘镜像也很繁琐。因此我们还需要进一步优化开发过程。
    (2) 开发效率问题:调试程序和引导启动程序为了解决以上开发效率严重低下的问题,我们就需要这样一种理想的调试环境:当修改操作系统程序中的任意地方时,能马上编译并运行出结果,而不需要将操作系统真正的装载到软盘镜像再到重启虚拟机等一系列繁琐操作。那么有没有这样的环境呢?答案是有的!但是需要借助于别的操作系统,因为你要需要编译并运行正在编写的操作系统代码。DOS操作系统下的.COM文件就是最好的调试环境,要成功运行.COM文件就需要遵循一些列的DOS系统规范,这里包括:
    (a) PSP 问题
    DOS系统中.COM程序要能正常运行,需要有1个256字节的程序前缀,所以必须要用ORG 100H来开头。
    (b) FAT 存储标准
    程序虽然可以在.COM下调试运行,但是不可能一直在这个DOS环境下调试,偶尔还是需要装载到引导软盘并启动虚拟机来验证开发结果,那么程序装载到引导软盘就涉及FAT存储标准的问题。装载到引导软盘的程序包括两部分:引导扇区程序和操作系统内核。引导扇区程序相对简单,只要把程序放在启动盘第1扇区带上启动标记“55AA”且不超过512B大小即可。操作系统内核的加载则相对复杂一些,开发者需要解决的问题是让引导盘在512B的有效空间内用汇编代码把操作系统内核完全加载到内存。无论何种方式,实际上最终的目的都是把操作系统内核从软盘上读出来并写到内存,所以以下的内容就是围绕这个目标来展开的。
    实际上,我们制作的引导软盘只要能成功引导进入新的操作系统就可以了,因此和DOS系统是没有任何关系的。那为什么我们还需要将引导软盘按DOS系统支持的FAT存储标准来组织程序内容呢?这是因为,我们在调试操作系统过程中,大部分的时间都在DOS系统下来调试,而不是频繁的重启虚拟机来进行调试。我们来详细比较一下不用DOS和用DOS两种生产方法的区别:

    <1> 普通软盘做引导盘(不用DOS)

    普通软盘中,我们把引导扇区程序BOOT.BIN写入第1个扇区,并以“55AA”做为启动盘标记,就能成功引导系统启动。但是BOOT.BIN的最大空间是一个扇区,也即512B,承载不了操作系统的内核。因此我们还需要往这个软盘后面的扇区添加操作系统内核内容,由于新的操作系统还没启动起来,我们还无法识别这个软盘,要往这个软盘里面写数据就只有一个唯一的办法:在别的操作系统中,提前将操作系统内核内容以BIN的方式写进软盘(如取名LOADER.BIN),这样系统软盘引导的时候,直接将操作系统内核的内容从软盘上读出来拷贝到计算机内存,然后通过一条跳转指定就可以执行操作系统内核了。但是如果没有特别的文件系统支持,要将数据便捷的写进软盘并不是一件容易的事情,因为涉及扇区、磁头和柱面等诸多参数控制。
    下面来详细讲解普通软盘做引导盘并装载内核和跳转到内核的操作过程:

(i) 把LOADER和内核写在一个程序中

总体思路:程序命名为BOOT.ASM文件,前512B程序放LOADER,后面放内核。LOADER的任务是从软盘的第2个扇区开始读数据,把所有的内核程序读到内存的物理地址8000h开始的地方。然后在LOADER的最后设置一条跳转命令,直接跳转到8000h实现到内核启动。这样程序编译之后形成BOOT.BIN文件,就可以实现一个文件引导进入操作系统了。
(a) LOADER部分
在这里插入图片描述
(b) 跳转
在这里插入图片描述
(c) 程序分界线
在这里插入图片描述
(d) 内核部分

在这里插入图片描述
在这里插入图片描述
特点:这种方法虽然只有1个程序进行编程,但是逻辑相对复杂一点:需要在内核程序中重新设置各寄存器的段值,对于内存中数据变量的访问,还需要重新计算偏移地址,以及重新写调用子程序。

(ii) 把LOADER和内核写在分开的独立程序中

总体思路:LOADER程序命名为BOOT.ASM文件,只占512B,负责启动。KERNAL程序命名为KERNAL.ASM文件放内核。分别编译后取名为:BOOT.BIN和KERNAL.BIN。在DOS下,用命令:copy /b boot.bin+kernel .bin bootkernal.bin 实现将KERNAL.BIN文件挂接在BOOT.BIN的后面,形成一个文件bootkernal.bin,最终用这个文件来引导进入操作系统内核。
LOADER的任务同样是从软盘的第2个扇区开始读数据,把所有的内核程序读到内存的物理地址8000h开始的地方。然后在LOADER的最后设置一条跳转命令,直接跳转到8000h实现到内核启动。
(a) LOADER部分
同上面一种方法,只不过需要把上面一种方法中超过512B的部分删除掉,只保留512B大小的启动程序。
在这里插入图片描述
(b) 跳转:也同上。
(c) 内核程序
由于内核程序,是一个单独的文件,因此需要单独设置以8000h为基础的各个段寄存器值。
在这里插入图片描述
它最大的特点是:可以独立编程,无需和引导程序BOOT.ASM发生任何逻辑关系,子函数和偏移地址等都和引导程序无关。

 用以上方法能成功进入操作系统内核,实验结果如下:

在这里插入图片描述
结论:第2种方法逻辑清晰,编程不需要过分关注段寄存器的设置。但是调试很不方便,每次都需要把两个程序编译之后用dos连接起来。第1种方法逻辑更复杂,要需注意切换段寄存器的值,对于不熟悉的人来说难度增加。但是调试非常方便,每次编译之后直接用虚拟机验证即可。我在开发操作系统过程中就一直用的第1种方法。

<2> 能被DOS系统识别的FAT软盘做引导盘

在一张已经能被DOS系统识别的FAT软盘中(可通过虚拟软盘导入自己手工制作的BPB),我们同样把带有“55AA”作为启动盘标记的引导扇区程序BOOT.BIN写入第1个扇区。那么这张软盘就具备神奇的双重功能:第1,它能引导你进入新的操作系统;第2,它还能被当前的DOS系统随时进行读写。所以,你可以直接在这个软盘的剩下扇区进行操作系统内核的补充和写入操作,如果要反复调试的话,会相当的方便,因为FAT是比较成熟的文件系统,可以在DOS系统下方便的对软盘进行读写。典型的应用是通过再写一个叫做LOADER.BIN的二进制程序到这张软盘,可以将引导扇区代码设置成在FAT软盘的根目录数据区寻找文件名:LOADER.BIN,一旦找到就将LOADER.BIN的内容从软盘上读出来写到内存中。那么启动操作系统的执行顺序就变成了: BOOT.BIN—>LOADER.BIN。这个时候计算机就已经完全摆脱了启动扇区,也就是突破了512B的程序空间了。后面可以通过进一步的引导就完操作系统内核的加载。
下面来详细讲解能被DOS系统识别的FAT软盘引导操作系统,并装载内核和跳转到内核的操作过程:
(a) 首先要写一个能被DOS系统识别的FAT软盘镜像,该镜像的基础二进制文件用程序BOOT.ASM生成并编译,要求BOOT.ASM的头格式必须是FAT软盘标准。

在这里插入图片描述

(b) BOOT.ASM里面还必须要有LOAER,因为我们是用它来将操作系统内核独到内存里面,并且我们约定LOADER将软盘的内核数据读进到内存从8000H开始的地方。在BOOT.ASM的最后用一条跳转命令,跳转到内核开始执行的第一条指令处。当然BOOT.ASM还必须具备引导能力,因此程序结尾要按要求设置。

在这里插入图片描述

在这里插入图片描述

(c) 内核程序
内核程序是单独用文件编写的,取名kernal.asm。经过编译之后为kernal.bin,它会被LOADER读进内存。内核程序独立编程,需要注意的时候,跳转到内核之后要重新设置寄存器:

在这里插入图片描述
(d) 制作软盘镜像
但是读进之前的前提是它必须要放进软盘中,也即软盘镜像中。要将上面的boot.bin和kernal.bin写进软盘中,我们需要用软盘镜像生成工具winimage。首先新建一个引导镜像文件BOOT.IMG,然后打开BOOT.IMG并务必将引导扇区属性装载进去,装载引导扇区属性的时候就需要我们之前的boot.bin文件。

在这里插入图片描述
只要之前,我们的BOOT.BIN是按FAT标准格式组织的数据,它在被装载进去的时候就能被系统识别:

在这里插入图片描述

这样我们就生成了具备引导系统能力的FAT格式软盘,这个时候我们最后一步,往里面加内核程序文件:kernal.bin。由于kernal.bin是FAT软盘加进去的第1个文件,根据FAT标准,它在软盘中的偏移地址是4200h,这个很重要。

在这里插入图片描述
(e) 系统启动
制作好软盘镜像之后,放入虚拟机启动。可以看到顺利执行。这里的关键点是从LOADER跳转到内核的地址是C200H,因为:8000H+4200H=C200。8000H为软盘被读进内存中的起始地址,而4200H又为内核程序在软盘中的偏移地址。我们之所以用FAT格式来制作软盘,其实就是想获得这个4200H!最终的操作系统运行如下(其中的界面效果就是内核程序的运行结果):
在这里插入图片描述
在这里插入图片描述

<3> 以上两种开发方式的优缺点比较

第1种开发方式的优点是,运行过程中不需要依赖于任何操作系统,是真正实现了自己完全的“纯”操作系统。大名鼎鼎的LINUX就是用的这种方式实现的操作系统引导和内核加载。但缺点是调试过程不方便,每次修改内核之后,都需要重新写入引导盘进行验证,而且向引导盘写入操作系统内核需要用扇区、磁头和柱面等最原始的磁盘参数进行控制,逻辑更加复杂。
第2种开发方式的优点是借助于DOS系统的FAT标准,调试过程相当方便,因为理论上讲只要任何一个.COM文件中不含DOS系统调用,我们都可以把它当做LOADER来使用,这个特点就非常强大。因为修改和调试.COM文件都非常的方便,所以这个是这种方式最大的优势。完全汇编语言实现的开源操作系统MenuetOS就是用的这种方式实现的操作系统引导和内核加载。但缺点是需要额外掌握FAT标准知识,要懂得怎样读取目录、文件名以及根据文件名获取物理扇区的位置等,也就增加了操作系统内核加载过程中的逻辑代码。

<4> 调试工具

开发操作系统调试工作量巨大,碰巧我选择的是用汇编语言来进行开发,调试难度就更大。开发调试的工具不多,说明如下:
(a) 调试.COM程序
由于.COM或.EXE程序已经有操作系统(MS-DOS)做支撑,因此Turbo Debugger (简称TD)无疑是很好的汇编调试工具,也是非常好的汇编语言学习调试工具。
(b) 调试操作系统程序
自己开发操作系统过程中,汇编语言难度极大,不可避免的要进行高频调试。由于程序没有操作系统支撑,调试工具有且只有唯一的选择:Bochs。这是一个伟大的工具,它可以跟踪从BOIS启动、7C00h引导直到程序结束!

猜你喜欢

转载自blog.csdn.net/csdn1340802/article/details/86648671