在上节博客中,我们介绍了一些操作系统的基本概念。本节我们将会继续来学习操作系统,创建一个在屏幕上有输出主引导程序。我们上节讲到主引导程序,那么它究竟是软件还是固件呢?如果是软件,那么是由谁来开发的?如何开发呢?
下来我们来介绍下 主引导程序 的相关知识:1、它是一段存储在主引导区(MBR)中的有效代码;2、它并不固化于硬件,属于操作系统代码的一部分;3、它是启动操作系统内核的桥梁,由汇编程序编写而成;4、代码总量是不能超过 512 个字节(包含 0x55aa 在内)。下来我们来看看主引导程序的开发,首先来看看它和一般的应用程序的对比,如下图所示
我们看到主引导程序的的入口地址是 0x7c00,而一般的应用程序的入口地址为 main 函数;主引导程序的语言是汇编,而一般的应用程序的语言则是常见的 C/C++;主引导程序因为没有操作系统的概念,因此它最后调用的是 BIOS 中断,一般的应用程序最后是 OS 系统程序的调用。
我们本节课程的是实验目标是:a> 编写一个主引导程序(基于汇编语言);b> 可独立运行于 x86 架构的主机(无操作系统);c> 运行后在屏幕上打印“Hello,YHOS!”。我们实现这个目标的思路是:1、先将关键寄存器的值设置为 0(mov ax, 0);2、定义需要打印的数据(db "Hello, YHOS!");3、打印预定义好的字符数据(int 0x10)。
首先我们先来讲解一些关于汇编的知识点:
a> mov:赋值操作,它是将右操作数赋值给左操作数。如 mov ax, 0 代表的意思是将 0 赋值给 ax 寄存器
b> int:触发中断。如 int 0x10 代表的是触发 0x10 中断,对屏幕来进行操作
c> hlt:停止运行,CPU进入暂停状态,不再执行任何操作。如 hlt 代表的是使程序进入睡眠状态
d> 汇编中的地址访问方式是:段地址:段内偏移地址。如 mov byte [0xb800:0x01], 0x07;那么此处的 0xb800:0x01 可等价于 0xb800 + 0x01,byte 代表的是后面的数据占用一个字节
e> 标签。用于表示后续指令的地址(可等同于 C 语言中的标签,作用类似于 goto 跳转语句一样)
f> $ VS $$。$ 表示当前指令行地址,$$ 表示当前汇编段起始地址
下来我们来看看中断调用和函数调用的对比,如下图所示
我们看到在一般的应用程序中 printf 的 %c 对应于在主引导程序中是用两条 mov 指令来完成的,参数 'c' 对应于主引导程序中的 mov al, 'c'。那么在一般的应用程序到此就编写完成了,而主引导程序还加了一句 int 0x10。因为一般的应用程序是基于操作系统来编写的,后续的处理由操作系统来完成;而主引导程序则是没有操作系统的,后面的 CPU 挂起工作就交由编写者自己来完成了。下来我们来编写一个用于引导加载的程序,如下
boot.asm 源码
org 0x7c00 // 定义起始地址start: mov ax, cs mov ss, ax mov ds, ax mov es, ax mov si, msg // 将 msg 所代表的标签地址放到 si 寄存器中 print: mov al, [si] // 将 si 中所代表的内容取出来,类似于 C 中的*(头地址) add si, 1 // 将地址 + 1 cmp al, 0x00 je last // 如果上面定义的 al 和 0x00 相等,则跳转到 last mov ah, 0x0e mov bx, 0x0f int 0x10 // 触发中断 jmp print // 类似于 C 中的 while 循环 last: hlt // 跳转完成,CPU 挂起 jmp last // 循环跳转 msg: db 0x0a, 0x0a // 定义换行符 db "Hello, YHOS!" // 定义打印数据 db 0x0a, 0x0a times 510-($-$$) db 0x00 // 将 510 - 上面代码所占用的字节,因为 msg 也占用字节,所以是 510 // $ 代表本行的地址数,$$ 代表上面全部代码的地址个数 db 0x55, 0xaa
我们在完成引导程序的编写之后,应如何来验证所编写的程序呢?这时我们便可以利用 VMWare 来创建一个空的虚拟机,基于它来验证我们所写的汇编程序。具体思路是:首先将汇编代码编译为二进制机器码(nasm指令),紧接着创建虚拟盘(bximage);然后将二进制diamante写入虚拟盘起始位置(dd),最后在虚拟机中将虚拟盘作为启动盘执行(VMware)。
那么接下来我们就要准备实验的原材料了:a> nasm 指令:nasm boot.asm -o boot.bin,这是将我们编写的 boot.asm 编译为二进制 bin 文件;b> bximage 指令:bximage a.img -q -fd -size=1.44,其中的 -q 选项是指不用进行交互界面,直接一次运行完成;c> dd 指令:dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc,这是将 bin 文件写入到 a.img 文件中。下来我们来进行实验,在 VMware 中创建一个新的并且为空的虚拟机。先来编译得到目标文件 a.img,步骤如下
1、利用 nasm 指令来完成 bin 文件的编译,如下
2、利用 bximage 指令来完成 a.img 的创建,如下
我们看到 a.img 已经创建出来。
3、利用 dd 指令来完成 boot.bin 在 a.img 的写入,如下
下来我们先来启动创建的空的虚拟机,开机应该是没有操作系统的,我们来看看屏幕上会输出什么
我们看到输出的是操作系统没发现,接下来我们就要将我们自己编写的主引导程序 a.img 放入它里面,让它打印出我们自己定义的 “Hello,YHOS!”。先将 a.img 装入到软盘中,然后再来启动,看看屏幕上输出什么
我们看到在屏幕上已经打印出了我们所想要的字符串了。至此,本节的目标已经实现。通过今天对主引导程序的学习,总结如下:1、主引导程序的代码量不能超过 512 字节;2、主引导程序需要使用汇编语言开发;3、主引导程序中可以通过 BIOS 中断使用硬件功能;4、主引导程序运行于实模式(地址都是实际的物理地址)。