从零开始实现balloon操作系统(0x00)
在写过自然语言处理框架和nasal脚本语言解释器之后,觉得无事可做,于是开始捣鼓操作系统了。对计算机感兴趣的人大多都有想过自己去写一个玩具级别的操作系统玩儿一玩儿,不过写操作系统对个人实力真的很有考验啊……
我当时开始写操作系统的时候啥都不知道,计算机组成原理没学,汇编没学,只是整天写C++貌似写得很顺手,照着一哥们的****,把bootloader的代码抄了一遍。这个教程的bootloader是根据linux0.11的实现做出来的,说实话linus太顶了,相同的年纪他能写出这样的代码,而我抄完发现自己完全搞不懂这些代码是什么意思。
然后又间隔了一年,这一年里正好学完了数字电路和计算机组成原理,我的胆子就大起来了,铁着头要搞懂引导的代码。然后也终于是搞懂了,并且自己修改了一部分代码,所以balloon的bootloader有一大段的原创代码。
折腾了一段时间之后,我成功设置了vga显示模式并且进入了系统,当屏幕显示出自己想要的颜色之后(如下图),成就感满满。我给这个操作系统命名为balloon,之所以命名这个,是因为我大学英语教授很喜欢发气球emoji(无端联想)
但是对于初学者来说,尤其是对硬件掌握程度不高的人来说(其实我就是……),直接上来自己写bootsect.s,setup.s,head.s三段技术含量过于丰富的汇编代码,真的是一道大门槛,很多人到这里直接就望而却步了,一般学习操作系统的话,可以直接跳过boot部分去看后面的操作系统主体内容,这段内容会在很久以后(可能)更新。
直接跳过这个部分其实影响不大,因为目前主流操作系统逐渐开始采用一些成熟的第三方bootloader(比如grub)来加载自己,而不需要操作系统开发者再去自己手打。但是经历过这段艰难的阶段,你会对i386机器的运行过程有更深刻的理解(并且汇编能力会有提升,尤其是之前没写过汇编的)。
逼逼赖赖这么多,现在直接进入主题。计算机在启动时会先运行BIOS进行加电自检等启动前检查,此时如果遇到致命错误(比如内存缺失),那么主板上的扬声器会发出对应的长短和数量不等的警报声来传递错误信息。
在初始化硬件之后,BIOS会依次读取每个硬盘的第一个扇区,来查询引导扇区。而区分引导扇区的唯一方法就是主引导签名。
主引导签名是位于该扇区的最后两个字节。如果这两个字节为0x55 0xaa,则表明这是个可以用于引导的扇区,那么这个扇区存储的512个字节被称为主引导记录,下图中objdump给我们展示了bootsect.o的全貌,可以看到最后两个字节是0x55和0xaa。(使用了指令objdump -s)
查询到引导之后,BIOS会将这512字节加载到内存的0x7c00处,并且跳转到该地址,这时候bootloader正式开始工作,我们就要通过bootloader来一点点初始化操作系统需要的内容,并且一步步将操作系统载入到内存中来……
本次项目使用的开发环境为Ubuntu 16.04 LTS amd64,测试环境为qemu-system-i386,汇编语言风格采用AT&T风格,操作系统开发主要使用C语言。实现的操作系统为32位系统。在下一篇文章中,我们将讲述如何用汇编编写一个引导扇区,也可以说是最简单的操作系统(bootsect.s)。
在编写过程中,也会提及一部分linkerscript/Makefile的用法。如果在文章中发现有任何问题欢迎指出,因为本人也是一边在学一边在写……