前面铺垫了这么久,今天终于开始写程序了。本讲将介绍3个逐步深入但非常简单的程序,一方面是让大家熟悉开发流程,另一方面是顺便解决前面遇到的CPU占用率高的问题。

一、mbr1.asm回顾

mbr1.asm的代码之前我们介绍过,这里我们回顾一下代码和演示步骤。
mbr1.asm代码如下:

;生成一个空的MBR
times 510 db 0 ;前510个字节全为0
db 0x55,0xaa   ;最后两个字节是0x55和0xaa。

下面我们来演示:

1.启动并登录CentOS

在VirtualBox中启动CentOS虚拟机,并用PowerShell登录到CentOS虚拟机。

2.创建空虚拟硬盘

如果没有虚拟硬盘或想重新创建一个空的虚拟硬盘,执行下面这条语句:

dd if=/dev/zero of=/media/VMShare/GrapeOS.img bs=1M count=4

截图如下:
自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

在上面截图中,用hexdump命令查看生成的虚拟硬盘文件GrapeOS.img,可以看到每个字节都是0,符合预期。

3.汇编mbr1.asm

nasm mbr1.asm -o mbr1.bin

截图如下:
自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

在上面截图中,用hexdump命令查看生成的mbr1.bin,共512个字节,前510个字节都是0,最后两个字节是0x55和0xaa,符合预期。

4.将mbr1.bin写入到虚拟硬盘中

dd conv=notrunc if=mbr1.bin of=/media/VMShare/GrapeOS.img

截图如下:
自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

在上面截图中,我们同样用hexdump命令验证,看到的确是将mbr.bin写入到虚拟硬盘的第一个扇区中了。

5.启动QEMU

在Windows的cmd命令行中运行如下命令:

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

从截图上可以看到,运行结果和之前的一样。回顾到此为止,下面来解决CPU占用率高的问题。

二、CPU占用率高的原因

前面我们介绍过,在QEMU+GDB调试中,反编译16位代码是有问题的,下面来介绍另一种反编译方法。nasm汇编器自带了一个反汇编工具叫ndisasm,下面我们来反汇编mbr1.bin。

ndisasm mbr1.bin

截图如下:
自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

从截图中可以看到两个0字节正好是一条汇编指令“add [bx+si],al”,最后的2个字节0x55和0xaa也正好是指令“push bp”和“stosb”。但这些都不是我们要写的程序,只是这些二进制数正好是某条机器指令。从BIOS跳转到0x7c00地址后,无论此处是什么样的二进制数,CPU都会把它当作指令一条一条的执行。当执行完这512字节,会继续执行后面内存中的数据。而后面内存中的数据是不确定的,CPU就会乱执行半天,而且也没有意义,这就是程序跑飞了。下面我们先来解决程序跑飞的问题。

三、mbr2.asm阻止程序跑飞

1.程序讲解

mbr2.asm代码如下:

jmp $ ;$表示当前行的地址
times 510-($-$$) db 0 ;$$表示段开始的地址,$-$$表示当前行前面的代码占用的字节数。
db 0x55,0xaa

在Linux命令行中执行如下命令:

nasm mbr2.asm -o mbr2.bin
hexdump mbr2.bin -C
ndisasm mbr2.bin

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

从上面截图中可以看到“jmp $”生成的机器码是“0xeb,0xfe”,其中0xeb是操作码,0xfe是操作数。这条指令中的操作数是当作有符号数处理的,0xfe换算成十进制数是“-2”(负2)。而这条指令共2个字节,当CPU读取完这条指令后寄存器ip的值会加2,执行完这条指令后,寄存器ip的值会加负2。这样的话CPU就会不断的重复执行这条指令,程序就不会跑飞了。

2.程序演示

下面我们将mbr2.bin写入到虚拟硬盘的第一个扇区。

dd conv=notrunc if=mbr2.bin of=/media/VMShare/GrapeOS.img
hexdump /media/VMShare/GrapeOS.img -C

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

然后用调试模式观察一下。

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img -S -s

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

前面我们介绍过,这里GDB是按32位反汇编的,16位反汇编是有问题的。但有些汇编代码在16位和32位下生成的机器码是一样的,比如这里的jmp $。所以这里的反汇编也可以适当参考一下。从上面截图上可以看到,每次单步运行后,程序地址仍然停留在0x7c00。这就是通过死循环来防止程序跑飞的办法。
下面我们来删除断点,让程序正常运行。
首先来查看断点:

(gdb) i b

删除断点:

(gdb) d 断点编号

然后让程序继续运行:

(gdb) c

截图如下:
自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

mbr2.asm虽然解决了程序跑飞的问题,但CPU占用率仍然高,笔记本风扇还是呼呼的转。下面我们来彻底解决这个问题。

四、mbr3.asm彻底解决CPU占用率高的问题

mbr3.asm的代码如下:

stop:
hlt ;使CPU暂停运行,直到有中断发生。(降低CPU使用率)
jmp stop 

times 510-($-$$) db 0
db 0x55,0xaa

上述代码主要用了一个hlt指令,让CPU暂停,如果有中断发生,会执行下一行jmp stop,然后又执行hlt。这样不仅防止了程序跑飞,而且降低了CPU使用率。
有了前面的基础,我们这次编译运行一气呵成。
在Linux命令行中执行:

nasm mbr3.asm -o mbr3.bin
dd conv=notrunc if=mbr3.bin of=/media/VMShare/GrapeOS.img

在Windows命令行中执行:

qemu-system-i386 d:\GrapeOS\VMShare\GrapeOS.img

截图如下:
自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

自己动手从零写桌面操作系统GrapeOS系列教程——13.向MBR中写入程序-LMLPHP

从上图任务管理器中可以看到,QEMU的CPU占用率已经降下来了。


本讲视频版地址:https://www.bilibili.com/video/BV1io4y1i7GP/
本教程代码和资料:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS操作系统QQ群:643474045

03-14 15:35