你知道当你按下开机按钮的时候,都发生了什么吗?
你知道当你看到显示器显示之前,都发生了什么吗?
你知道当你准备使用操作系统前,都发生了什么吗?
Boot,我们用这个单词来形容计算机启动的一系列操作,也就是Bootstrap的缩写。Bootstrap,意思是“通过拔自己的靴带来拔起自己”,出自谚语抑或是童话(无考究)。之所以这样称呼计算机启动,是因为Boot程序的作用是启动计算机程序,而其又是计算机程序的一部分,自己启动自己,也就是Bootstrap。
解释完Boot的含义,让我们来看看Boot是怎么进行的。
不像计算机科学的其他领域,你在操作系统的设计和制作中,经常打交道的是历史遗留的糟糕设计。正如我说过的:「向前兼容拖了整个计算机产业的后腿」,往往一些仅仅适用当时的设计通过向前兼容这一种设计原则而保留下来。比如说至今我们开机仍然需要进入16位实模式,再进入32位保护模式,才能进入64位长模式、计算机开机时候仍然要打开A20地址线,也就是通过IBM键盘控制的那条……当然,我们不能说向前兼容就是不好的设计原则,我们自然不能要求任何软件提供商都与时俱进更新他们的软件,也不能要求用户都无条件使用最新的软件。
废话不多说,对于这种魔数般的内容,我们仅需要知其然便可。
回归正题,来详细看看计算机是如何启动的:
- 计算机上电,控制器自动读取主板上的BIOS到内存里的0xFE000,也就是A20地址线不开启状态下的最靠后的区域。
- 读取完了BIOS之后跳转到0xFFFF0,距离0xFFFFF就只有一个jmp语句的距离。
- 执行这个jmp语句,会跳转到BIOS程序的入口
- BIOS处理计算机的硬件,并且写入中断供其他程序使用
- BIOS读取启动硬盘的第一个扇区,也就是512字节大小的数据,到0x7c00处
- BIOS跳转到0x7c00,执行我们的引导扇区
- 引导扇区加载Loader或者什么东西
- Loader或者什么东西正式开启保护模式并跳转入内核
- 计算机完成启动
上面通篇的东西基本上都是历史遗留设计,什么A20地址线,什么0x7c00,都是前人的设计,在这里我们不深究,基本上解释一下什么是A20地址线就行了:
在32位CPU发明之后,地址线也被拓宽到32位,可是之前16位的时候我们只有20位地址线(为什么16位有20位地址线参考王爽老师的《汇编语言》)。为了保证向前兼容,也就是不让老的程序溢出到20位地址线能访问到的最高内存地址,我们聪明的IBM同学想到了一个聪明的主意:把第20位地址线的开关设置在键盘控制器上,当要使用更多的内存的时候,打开这个开关就可以了。这样子我们就可以保证兼容。当开关没有打开的时候,所有进位到第20位地址线都会变成0,这也就保证了地址空间和16位时一样。
然后日新月异,我们现在有了三种方法开启A20地址线,当然我们以后会选用IBM键盘法,本质上都是一样的。
然后了解了计算机的启动过程,我们已经具备了书写操作系统的第一个条件:知道如何下手。
显然,BIOS程序是烧录在主板上的,我们无法更改;那我们只有一条路可以走了:写入我们的代码到启动硬盘的第一个扇区,512字节的空间。
我们使用nasm作为我们的汇编器,她是一个十分好用的汇编器(起码比GAS好用)
来看看我们的第一段代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20org 0x7c00
mov ax, cs ; 初始化各种段地址
mov es, ax
mov ds, ax
mov ax, msg ; 加载字符串地址
mov bp, ax
mov ax, 0x1300 ; 中断编号0x13, 显示页0
mov bx, 0x0007 ; 颜色参数
mov cx, len ; 长度
mov dx, 0 ; 位置参数
int 0x10 ; 调用中断
jmp $ ; 死循环
msg db "hello world"
len equ $ - msg
times 510 - ($ - $$) db 0
dw 0xAA55
利用nasm我们来编译这段代码(需要nasm工具,自己安装去吧~):nasm -f bin boot.asm -o boot.bin
然后再生成一个80MB大小的硬盘来供给我们的虚拟机(bximage命令来自下文介绍的bochs虚拟机):bximage -mode=create -hd=80M -q 80m.img
最后,把我们的引导扇区写入硬盘:dd if=boot.bin of=80m.img seek=0 conv=notrunc
完成!
我们现在有了一个可以启动的引导盘啦~
当然,我们需要选择一个合适的虚拟机,我们总不能来回重启我们自己的电脑来调试,这样她会哭的。选用什么虚拟机呢?VMWare还是VBox——肯定都不行,因为这些虚拟机为了更快的虚拟作业系统,通常会采用许多高端的措施来进行虚拟过程,比如说硬件虚拟化之类的技术。我们需要的是一个更加底层、更加生硬(直接)的虚拟机,也就是完完整整模拟一个机器的那种 —— Bochs!
Bochs提供了许多工具,也可以直接与gcc连接,相当多的调试指令,是操作系统开发者的不二选择(广告费请mail获得地址hh),上面创建硬盘的命令就是bochs提供的。
安装Bochs的过程和安装nasm一样,按过不表。
我们建立一个Bochs_img来作为我们的配置文件:1
2
3
4
5
6
7
8
9
10megs: 256
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
vga: extension=vbe, update_freq=15
clock: sync=slowdown, time0=local
mouse: enabled=0
cpu: count=1, ips=5000000
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="80m.img", mode=flat
boot: disk
然后启动:bochs -q -f bochsrc_img
Boom?嗯,你需要在终端里输入c,也就是continue的意思才能开始运行。
Boom!这就是一个最小的操作系统。
(看到左上角的hello world了没~)
这就是我们的最初的20行代码,也是一切的开端(中二口气)。
下一篇我们会来展示一下如何跳转入保护模式以及使用C语言。
P.S. 为啥我咕咕咕了这么久,是因为咱一直有其他事情啦>w<