引言
最近在学操作系统,所以就想着自己实现一个操作系统顺便写一系列教程。而这篇博客就是第一篇。虽然说是一系列教程,然而未必就能够坚持到底,所以,这篇也许是最后一篇。
一个最简单的操作系统
操作系统是怎样运行起来的
要想写一个操作系统,首先得明白操作系统都干了些什么。但这里既然是讲一个最简单的操作系统,当然是什么都不干的操作系统最简单了。可是如果真的什么都不干,那就等于不存在了。所以还是得干一点最简单的事,比如输出个”Hello, memi OS!”。
我们的操作系统要想输出个”Hello, memi OS!”,首先得运行起来。那么操作系统是怎么运行起来的呢?
当我们按下计算机的电源键后,计算机就会自动开始执行被固化到计算机ROM芯片上的BIOS程序,这个程序在完成自检等操作后,就会去读取磁盘物理起始位置的扇区(512Byte)加载到内存中(当然,前提是这个扇区已被申明为引导扇区),然后自动跳转到这个块数据在内存中的位置并开始执行这段指令。如果你自己安装过操作系统或者接触过BIOS,你就会知道计算机启动时是可以在BIOS里选择启动磁盘的,其实这里选择启动盘,就是在选择BIOS加载哪一个磁盘的起始扇区。所以这里我们就利用这个块来载入我们的操作系统。
编写最简单的操作系统
申明引导扇区
为了载入我们的最简单的操作系统,我们就要把磁盘(或者软盘)中的第一个扇区申明为一个引导扇区(主引导记录MBR),申明引导扇区的方法很简单,就是在起始扇区末尾两个字节写入0xAA55,所以,我们的引导程序可能是这样的:
有了这行汇编代码,当我们的程序写入磁盘第一个扇区后,这个扇区就会被识别为引导扇区,BIOS就会加载这个扇区到内存特定位置中并执行它。
“Hello, memi OS!”
为了输出”Hello, memi OS!”字符串,我们需要在程序中定义一个字符串:
有了字符串以后,我们需要调用BIOS中断函数将这个字符串显示出来。要知道,我们是在开发一个系统底层最开始的程序,这个时候是没有如同我们在编写普通C程序时的“printf()”这样的系统调用的,那怎样才能显示我们的字符串呢?好在硬件开发商已经在BIOS中为我们初始化了一些最基本的和硬件打交道的接口,这些接口的调用地址被初始化在BIOS的中断向量表(interrupt vector table,IVT)中(如果你对中断还不了解的话,有必要先去了解一下有关中断的知识。)查询x86架构BIOS的中断向量表,可以看到,有一个用于视频服务的中断向量10h,其详细调用方法如下:调用这个中断,并传入合适的参数,即可显示字符串:
载入引导扇区并显示字符串
在x86架构中,BIOS会将引导扇区加载到内存中0x7c00h处,并跳转到这里开始执行。为了我们的程序能正确寻址,程序开头必须加上这样一句伪指令:
接下来,就是调用显示函数然后进入死循环以便我们可以看见显示的内容了:
完整程序
前面我们说过,一个扇区有512Byte,但如果我们把上面这些程序编译到一起,显然不足512Byte,为了解决这一问题,我们需要在程序后面的空间中都补上0来占位:
所以,我们的整个程序可以是这样的:
编译运行
在Linux下,安装nasm编译器、bximage磁盘工具、qemu模拟器:
然后执行命令:
就可以看到屏幕上输出的红色字符串”Hello, memi OS!”:
后话
至此,我们的最简单的操作系统就完成了。当然,这个操作系统几乎什么都没干,所以严格来说它并不算一个操作系统。但当我们了解了操作系统的启动方法之后,其他功能的实现,就与普通的编程并没有太大区别了,无非就是把这里的显示字符串替换为其他功能而已。
但是,其他功能的实现也依然是一个挑战。