虚拟机的原理其实非常简单,首先分配一块内存充当虚拟机的内存,然后再分配一块内存当作虚拟机的寄存器。
接下来,解析二进制文件,根据机器码的含义做对应的操作,比如机器码:
101110110000110101111100
查8086机器码手册,可知它的意思是将一个立即数放到寄存器bx中,对应的汇编为:
mov bx, imm
在我们的程序中可以直接将代表bx的内存设置为imm,这条指令就算虚拟执行完了。之后将指令指针寄存器eip的值加1,这个寄存器记录了下一条指令的位置,其实就是将代表eip的内存中的值加1。
下一条指令,先取出eip的值,然后根据eip去内存中取指令:
1111111111100011
通过查手册发现其对应的汇编为
jmp bx
这是一条跳转指令,跳到bx寄存器中的地址执行,我们只需要将eip寄存器设置为bx的值就行了。
通过上面简单的例子,相信你已经明白了,虚拟机就是将硬件的功能转换为简单的内存的读写。
前几天兴趣所致,还写了个简单的虚拟机demo,它能够执行下面的汇编:
mov bx,0x7c00+start
jmp bx
message:
db 'ABC abc '
start:
;设置附加段基址到显示缓冲区
mov ax,0x7c00
mov ds,ax
;设置附加段基址到显示缓冲区
mov ax,0xb800
mov es,ax
;显示字符串
mov si,message
mov di,0
mov cx,start-message
@g:
mov al,[si]
mov [es:di],al
inc di
mov byte [es:di],0x07
inc di
inc si
loop @g
;停机
hlt
times 510-($-$$) db 0
db 0x55,0xaa
这段汇编的作用是将字符串移动到显示缓冲区中。
如果你对我写的虚拟机demo感兴趣也可以下载我的代码,体验一下。它是用python写的,需要安装python3环境,还需要nasm编译汇编程序,然后到目录下执行make,如果python提示缺少包,用pip3 install安装对应的包即可。
程序成功执行之后可以用浏览器打开index.html,他会将显示缓冲区中的内容可视化显示出来,相当于虚拟屏幕。
用上面的方法是完全用软件模拟,执行一条指令必须用几条指令进行解析,效率比原来慢几倍。随着虚拟化越来越重要,CPU和内存开始支持硬件辅助虚拟化,甚至一些高频外设也支持硬件辅助虚拟化。
KVM是一种使用硬件辅助虚拟化技术的虚拟机,它相比软件虚拟化速度有明显提升。现在的linux与QEMU中都有KVM的代码。