虚拟机相关信息收集



2006年之后的确变简单了。1998年之前的x86是号称无法完全虚拟化的,然后“某几个个人”搞了出来,于是就有了vmware。不过就算今天虚拟化再怎么简单,我也不相信能问出这种问题的题主能写一个vmm出来。

================
趁着编译,补充点干货:

早期vmm难以实现的原因:
- 最大难题:x86的某些指令,在非ring 0的情况下执行,行为和在ring 0下不同,并且不会产生fault,这导致了trap-n-emulate形式的virtualization没办法直接实现,这也是早期被认为是无法虚拟化的主要原因。解决方法是binary translation,代码极其复杂,需要深厚内力才能读懂,更别说写出来。compiler、OS、architecture的知识一样不能少。
- MMU的虚拟化。虚拟内存提供了virtual address -> physical address的mapping,但是要想virtualize一个guest OS,那么就要做到virtual address -> guest virtual physical address -> machine address的mapping。于是就要在MMU里面动手脚,创建一个shadow page table,完成virtual address -> machine address的直接翻译,为了保持正确性,必须对OS透明地维护好TLB,并监视guest OS对自己认为的真正的page table的一切修改。这个过程可以简单粗暴,只要guest一load CR3就清空shadow page table,flush TLB。也可是十分细腻,对shadow page table做更加细致的更新。前者实现复杂度略低,性能自然很差,因为TLB miss rate奇高,并且由于要经常清空shadow page table,page fault也更多。后者性能好,但是实现异常复杂,cache consistency难以维护。
- IO。如果不计性能,这块其实比较容易解决,但是也需要对PCI device的工作机制有着深厚的理解。早期的vmm对IO几乎就是直接emulate。
- CPU的虚拟化,CPU的型号一大堆,每个型号之间的指令集和指令行为都有不同。还有很多CPU有bug,于是为了over come这些,monitor里面要做很多ad hoc的修改。
- 其他:BIOS,perforamce monitoring,hardware counters,graphics,guest crash handling,interrupt delivery等

今天vmm难以实现:
- CPU种类繁多
- PCI pass through的维护
- 达到近乎native running的速度
- 高并发I/O
- concurrent bugs和lock contention
- numa
- security
- cache consistency
...

最后,题主可能是把virtualization当成是emulation了。即使后者本身也很复杂,参考qemu。




linux 内核开机后是不是得一直运行着,不然怎么调度别人?假设只有一个cpu核的话,内核一直在运行,用户进程怎么运行?
2 条评论
当年上我老板的OS课,老板上来就告诉我们,OS不是运行着的代码,而是一堆躺在内存里等着被调用的代码。

内核就是一个由interrupt驱动的程序。这个interrupt可以是一个系统调用(x86下,很多OS的系统调用是靠software interrupt实现的),可以是一个用户程序产生的异常,也可以是一个硬件产生的事件中断。

于是你的问题解决了:一个用户程序运行的时候,Linux就在内存里呆着,等着一个中断的到来。在这个中断的处理过程中,来做“调度”。而一般的时分系统里,都会有个timer interrupt每隔一段时间到来,也就是楼上说的“时间片”。

PS:很多人在认识OS的过程中被大量的artificial concept给困惑了。比如神马进程,线程,调度,micro kernel,monolithic kernel的。从x86架构上来理解OS才是王道。

=================================================================
PS of PS: 把评论粘上来,补充上面的PS,谢 @许越提醒:
因为很多教材在讲os的时候,更喜欢从app的角度来看待os,于是很多时候被各种概念绑架。因为很多例如进城,线程,系统调用这样的东西都是由os在硬件上抽象出来的。站在这些概念上看os,就有点“不识庐山真面目”的感觉。所以我的意思是从硬件的角度看os,反过来理解为何os要抽象出这些概念。站在cpu的角度,理解指令是怎么在cpu上一条一条的运行的。

因为很多教材在讲os的时候,更喜欢从app的角度来看待os,于是很多时候被各种概念绑架。因为很多例如进城,线程,系统调用这样的东西都是由os在硬件上抽象出来的。站在这些概念上看os,就有点“不识庐山真面目”的感觉。所以我的意思是从硬件的角度看os,反过来理解为何os要抽象出这些概念。站在cpu的角度,理解指令是怎么在cpu上一条一条的运行的

虚拟机是怎么实现的?修改

请解释下原理 修改
2 条评论
按投票排序 按时间排序

6 个回答

问题有点大,笼统的试答一下。坑比较深,最好有基本的OS基础才好理解。

假设这里的虚拟化是指在一个OS下虚拟化另外一个OS(ESX那种hypervisor直接跑在硬件上的做法,其实大同小异),另外假设这里的虚拟化是指full virtualization而不是Xen那种para-virtualization。最后假设虚拟化的是一个早期的x86机子(没有hardware supported virtualization的存在)。

要回答这个问题,首先看为什么一个OS无法直接与另外的OS共存。答案很简单,OS作为硬件上第一层软件,认为自己拥有全部的硬件的访问和控制权,且自己是唯一的控制者。在这种情况下,如果两个OS共存,必然产生问题。

OS主要负责管理的是CPU和内存,以及众多的IO设备。于是我们可以分别讨论。hypervisor是实现虚拟化的关键,它会以一个内核态的驱动存在。


CPU的虚拟化:
背景知识:x86 CPU有一项权限机制,把CPU的状态置于RING 0到RING 3分别使CPU具有最高的权限到最低的权限。以Linux为例,内核运行于RING 0上,而其余全部用户进程运行于RING 3上(Xen比较奇葩,Linux在Xen下面会运行于RING 1)。在用户权限下,所有的IO设备是不可操作的,另外,有些控制寄存寄是无法访问的,一些privilege的指令是不能运行的。因此一个用户进程要想读写文件,进行一些操作,就要依赖于内核。系统调用能够使CPU运行于RING 0,并执行内核代码(具体方法见一些操作系统教程)。

背景说完。一个CPU的全部状态其实就是所有寄存器的值,只要保证任何操作之后寄存寄的值在OS看来是正确的,guest OS就可以正常执行。hypervisor会为每个虚拟的CPU创建一个数据结构,模拟CPU的全部寄存器的值,在适当的时候跟踪并修改这些值。

那么考虑虚拟化一个CPU,在虚拟化的guest OS里面,CPU无论如何也不可能运行于RING 0,因为这样的话,host OS必然会crash掉。因此,当一个guest OS想要进入到RING 0执行内核代码时,hypervisor会向guest OS说谎,并告诉它,你已经在RING 0上了,而实际上,所有的指令还是在RING 3上。当guest OS访问到任何privilege的东西时,hypervisor会接到fault,此时hypervisor会判断这个指令是什么,并修改相应的虚拟寄存器的状态,然后返回。这样guest OS就可以正常的运行。需要指出的是,在大多数的指令下代码是直接跑在硬件上的,而不需要软件介入。只有在一些权限高的请求下,软件会介入,并维护虚拟的CPU状态。

内存的虚拟化:
背景知识:虚拟内存,页表结构等。OS的基础内容,不表。

hypervisor虚拟化内存的方法是创建一个shadow page table。正常的情况下,一个page table可以用来实现从虚拟内存到物理内存的翻译。在虚拟化的情况下,由于所谓的物理内存仍然是虚拟的,因此shadow page table就要做到:虚拟内存->虚拟的物理内存->真正的物理内存。

以下是细节,如果看着闹心,请忽略。hypervisor会维护一个从虚拟内存到物理内存的映射,当guest OS更换自己的page table,也就是改变CR3寄存器的值,hypervisor会因为用户态的权限不足而接到一个general exception,hypervisor会记录用户想要更换的新的页表,并放上一个空的shadow page table,然后返回。这个空的shadow page table会在接下来的执行中造成CPU无法进行地址翻译,而产生page fault。在fault发生后,hypervisor会得到一个虚拟地址,然后根据之前记录的用户的页表结构,翻译出一个虚拟机器地址,然后再把这个虚拟的机器地址,由hypervisor维护的映射翻译为实际的机器地址,然后装入shadow page table,并返回执行。如此,就实现了:虚拟内存->虚拟的物理内存->真正的物理内存。

I/O虚拟化:
背景知识:memory mapped I/O device。大多数的PCI设备都是直接将自己的某些控制寄存器映射到物理内存空间上,CPU访问这些控制寄存器的方法和访问内存相同。CPU通过修改和读取这些寄存器来操作I/O设备。

虚拟化的方法很简单,没当hypervisor接到page fault,并发现实际上虚拟的物理内存地址对应的是一个I/O设备,hypervisor就用软件模拟这个设备的工作情况,并返回。比如当CPU想要写磁盘时,hypervisor就把相应的东西写到一个host OS的文件上,这个文件实际上就模拟了虚拟的磁盘。


这里忽略了很多异常处理等等细节,但求简化,可是貌似还是写多了。以上。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值