《30天自制操作系统》是一本学习操作系统的好教材,它教我们怎么从建立引导区开始,从零实现一个操作系统。但是,实现书中例子的时候,我们需要不断将我们写好的操作系统代码写入软盘并且还要不断重启电脑来试验我们的代码,大家一定感到很头疼吧。


与其不停的重启,不如使用模拟器,向大家推荐一款模拟器qemu,它是由Fabrice Bellard编写,功能非常强大。那么下面我就来教大家使用qemu运行书中自制操作系统的方法吧。



一、安装qemu


我使用的是苹果笔记本,苹果有一个非常好用的程序管理工具叫brew,安装brew需要在终端中输入


sudo ruby -e "$(curl -fsSLhttps://raw.github.com/mxcl/homebrew/go)"


或者


curl -LsSf http://github.com/mxcl/homebrew/tarball/master | sudo tar xvz -C/usr/local --strip 1


然后使用brew安装qemu


sudo brew install qemu --env=std --use-gcc


如果成功,就会在/usr/local/bin/中创建出许多以qemu开头的文件,其中qemu-img和qemu-system-i386是对我们来讲最重要的两个文件。




qemu创建的虚拟机如何挂载u盘 qemu虚拟机安装mac_u盘


二、自制操作系统


为了方便读者,我把书中前三天内容的精髓汇总在一个程序中,其中包含FAT软盘格式、从软盘中读扇区和在屏幕中输出文字。程序如下:


; 程序名称ipl.nas


; hello-os


; TAB=8




    ORG    0x7C00     ; 程序加载到内存地址0x7C00后




; 以下的记述用于标准FAT12格式的软盘


start:


    JMP    entry


    DB    "HELLOIPL"    ; 启动区名称(8字节)


    DW    512        ; 扇区大小(512字节)


    DB    1        ; 簇大小(1扇区)


    DW    1        ; FAT起始位置


    DB    2        ; FAT个数


    DW    224        ; 根目录大小(224项)


    DW    2880        ; 磁盘大小(2880扇区)


    DB    0xf0        ; 磁盘种类


    DW    9        ; FAT长度


    DW    18        ; 每个磁道扇区数


    DW    2        ; 磁头数


    DD    0        ; 不使用分区


    DD    2880        ; 重写一次磁盘大小


    DB    0,0,0x29    ; 意义不明


    DD    0xffffffff    ; 可能是卷标号码


    DB    "HELLO-OS   "    ; 磁盘名称(11字节)


    DB    "FAT12   "    ; 格式名称(8字节)


    RESB    18        ; 空出18字节




entry:


    MOV    AX,0        ; 初始化寄存器


    MOV    SS,AX


    MOV    SP,0x7c00


    MOV    DS,AX


    MOV    ES,AX


; 读磁盘


    CYLS    EQU    10


    MOV    AX,0x0820


    MOV    ES,AX


    MOV    CH,0        ; 柱面0


    MOV    DH,0        ; 磁头0


    MOV    CL,2        ; 扇区2


readloop:


    MOV    SI,0        ; 记录失败次数




retry:


    MOV    AH,0x02        ; 读盘


    MOV    AL,1        ; 1个扇区


    MOV    BX,0


    MOV    DL,0x00        ; A驱动器


    INT    0x13        ; 调用磁盘BIOS


    JNC    next        ; 没出错跳转fin


    ADD    SI,1        


    CMP    SI,5        ; 比较SI与5


    JAE    error        ; SI >= 5时,跳转到error


    MOV    AH,0x00


    MOV    DL,0x00


    INT    0x13        ; 重置驱动器


    JMP    retry


next:


    MOV    AX,ES


    ADD    AX,0x0020    ; 把内存地址后移0x200


    MOV    ES,AX        ; 因为没有ADD ES,0x20


    ADD    CL,1


    CMP    CL,18


    JBE    readloop    ; 如果CL <= 18,跳转至readloop


    MOV    CL,1


    ADD    DH,1        ; 读磁盘另一面


    CMP    DH,2


    JB    readloop


    MOV    DH,0


    ADD    CH,1


    CMP    CH,CYLS        ; 读CYLS个柱面


    JB    readloop




; 输出helloworld




    MOV    SI,msg


putloop:


    MOV    AL,[SI]


    ADD    SI,1        ; 给SI加1


    CMP    AL,0




    JE    fin


    MOV    AH,0x0e        ; 显示一个文字


    MOV    BX,15        ; 指定字符颜色


    INT    0x10        ; 调用显卡BIOS


    JMP    putloop


fin:


    HLT


    JMP    fin


error:


    MOV    SI,errmsg


errloop:


    MOV    AL,[SI]


    ADD    SI,1        ; 给SI加1


    CMP    AL,0




    JE    fin


    MOV    AH,0x0e        ; 显示一个文字


    MOV    BX,15        ; 指定字符颜色


    INT    0x10        ; 调用显卡BIOS


    JMP    errloop




msg:


    DB    0x0a, 0x0a    ; 换行2次


    DB    "hello, world"


    DB    0x0a        ; 换行


    DB    0


errmsg:


    DB    0x0a, 0x0a    ; 换行2次


    DB    "disk error"


    DB    0x0a        ; 换行


    DB    0


marker:


    RESB    0x1fe-(marker-start)


    DB    0x55, 0xaa


; 以下是磁盘其他内容


DB    0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
 
    RESB    4600
 
    DB    0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
 
    RESB    1469432



看过前三章的读者理解上面的程序应该不难。


另外fat启动扇区格式要安照 http://www.ntfs.com/fat-partition-sector.htm,所以假如你发现在汇编后的机器码中由于JMP entry的机器代码长度(需占前三个字节)有变导致与fat格式不符合(需从第四字节即地址为0x03开始),那么你要回到程序中在JMP entry后使用DB手工添加占位字节。



三、汇编


我使用mac自带的nasm汇编器。查看能过汇编成哪些类型的机器代码可以输入


nasm -hf


我们需要把代码汇编成bin格式,这也是nasm的默认格式。因为在我们的代码中全部都是intel的16位汇编指令,而mac的cpu使用的也是intel芯片,所以汇编后的代码x86和x64都可以执行。


nasm -f bin ipl.nas -o ipl.bin -l ipl.lst


-f后面跟输出格式,-o后面是输出文件,-l后面是list文件,其内容是汇编语言和机器语言的对照表。


你可以用xxd查看二进制文件的内容:


xxd ipl.bin | less


以及:


file ipl.bin # 显示为:DOS floppy 1440k, x86 hard disk boot sector


qemu-img info ipl.bin # 其对应的qemu镜像类型为raw




四、虚拟机


在终端中输入:


qemu-system-i386 -fda ipl.bin -boot a


其中


-fda/-fdb 指定软盘


-hda/-hdb/-hdc/-hdd 指定硬盘


-cdrom 指定光盘


-boot 指定从哪个设备启动


a(软盘),c(硬盘),d(光盘),n(网络)


因为ipl.bin是软盘格式且为启动盘,我们使用-fda ipl.bin -boot a


运行结果如下:



qemu创建的虚拟机如何挂载u盘 qemu虚拟机安装mac_qemu创建的虚拟机如何挂载u盘_02



五、制作u盘磁盘镜像


我们当然也能把ipl.bin写入u盘然后从u盘启动,此时u盘其实就相当于一个软盘。


当插入u盘后,我的/dev中多了一个disk2。


sudo diskutil unmountDisk /dev/disk2


sudo dd if=ipl.bin of=/dev/disk2 # 将软盘镜像写入u盘中


写好后重新插拔一次u盘

sudo diskutil unmountDisk /dev/disk2
 
sudo qemu-system-i386 -fda /dev/disk2 -boot a

运行结果同上。