计算机虚拟化原理
虚拟化是将底层的计算机资源抽象成为多组相互隔离的计算平台,每一个计算平台都具有计算机5大基本部件中的所有设备
直接上干货。。。。。。。。。。。。
虚拟化的两种实现方式:
1、硬件宿主机运行虚拟化软件、创建各种虚拟机(type-2型虚拟化---有虚拟机管理器)
可以借助宿主机的各种虚拟机管理工具来管理虚拟机---KVM、VMware、virtualbox
系统上电之后启动的是一个操作系统,操作系统上安装叫做虚拟机管理器的软件,VM Monitor
2、在硬件上安装Hypervisor、在其上运行各种虚拟机、没有宿主机(type-1型虚拟化---直接跑到硬件上的就叫做Hypervisor)
在实现虚拟化功能管理方面的更可靠、彻底---xen,VMware esx/ESXi
在系统上电之后直接运行虚拟机监控程序,而没有运行任何内核、Guest操作系统直接在其虚拟化环境中运行
虚拟化技术分类:
1、 模拟:硬件-host-模拟器(emulator-虚拟机监控器)---实现CPU、内存、IO的软件模拟
如,PearPC,Bochs,QEMU
2、完全虚拟化(full-virtulization也叫native-virtulization):CPU、内存不做模拟,IO要做模拟(BT技术、HVM硬件虚拟化) 如,VMware-Workstation,KVM,Xen(HVM)等,完全虚拟化跟模拟的硬件环境是一样的,只不过CPU平台需要保持一致(VMware Workstation打开的接口就是虚拟机管理器)
3、半虚拟化(para-virtualization):虚拟机的内核知道自己是运行在虚拟机上的,因此虚拟机的架构和底层架构必须完全一样,guest os必须修改其内核基于Hyper call完成底层调用。Windows的内核不允许修改,所以很难跑到半虚拟化环境中,著名的软件有xen,uml(user-mode linux)
4、OS级别虚拟化:基于提供虚拟机的目的是提供用户空间而非内核的,把虚拟化向下推了一层,底层上面跑一个内核,内核上面跑一个虚拟化管理器,虚拟化管理器管理跑在共享底层内核上的各个用户空间,某一个用户空间不小心把内核搞坏了,其他用户空间都玩完,很多IDC采用,著名的实现:OpenVZ,lxc,Solaris Containers,FreeBSD jails,docker等,稳定性、隔离性不好
5、库虚拟化:wine
6、应用程序虚拟化:JVM
基于上述概念,从CPU、Memory、IO针对虚拟化实现原理展开详述。。。。。。。。。。。。。
1.CPU虚拟化(时间片):
CPU在执行计算机指令的时候是按指令环来划分指令级别的(X86 CPU由环0,1,2,3四个环所组成)
对CPU的虚拟化基于分时实现,当一个操作系统操作系统启动以后,操作系统本身不执行任何具有生产能力的任务,真正生产功能是由执行具体任务的进程提供的,由此整个操作系统运行时,为了实现协调多任务,操作系统被分割成两段,接近硬件具有特权指令权限的叫做内核空间,执行具体任务的进程运行在用户空间,用户空间的进程需要使用特权权限和使用硬件时,向内核发起申请,这个过程就叫做系统调用
X86CPU具有保护环(protection ring~从内侧到外侧依次0/1/2/3),最外层环3执行普通指令,环1和环2暂时未使用,环0是特权环,执行包括操作硬件和访问CPU中敏感寄存器的特权指令------内核空间在环0执行,用户空间在环3执行
以VMWare为例,VM是一个运行在宿主机内核上的应用,虚拟机我们称作guest,宿主机我们成为host,guest有自己的用户空间和内核空间,guest用户空间的应用程序要使用硬件,首先要经过自己的内核,再经过host的内核,这就是虚拟机性能瓶颈的关键因素。
这时候就产生了一个问题?
host直接安装在硬件上,其内核运行在环0,guest运行在用户空间,其内核必然运行于环3,但是操作系统设计的时候,内核不运行在环0,就无法调用硬件资源,那么这个操作系统就无法运行起来
解决方案:
用软件给虚拟机模拟一个CPU,这个CPU有所有的环,guest运行在模拟CPU上(硬件资源提供给我们的接口都是微码编程的,用软件的方式可以模拟微码接口),模拟的CPU以一个进程的形式运行在host上,特权指令发给host的内核,通过这种方式我们可以自己定义CPU的核心数量,并且实现不同虚拟机之间的隔离。虚拟机操作特权指令需要模拟CPU转换成host的CPU执行特权指令,软件的执行、解码、封装、转换等一系列操作相当耗时,性能很低。
扩展思考:
1)在X86架构下软件模拟X86架构CPU,guest用户空间非特权指令怎么运行?
2)在X86架构下软件模拟ARM架构下的CPU,用户空间非特权指令怎么运行?
针对于问题1,guest本身是运行在host用户空间的进程,其运行非特权指令不涉及到自身和操作系统内核的系统调用,无论host还是guest的非特权指令都由host的CPU直接在用户空间执行。
针对于问题2,ARM和X86的指令集不同,guest用户空间的非特权指令需要转换成host物理CPU的指令集才能执行,中间涉及到指令集的转换会进一步降低虚拟机的性能。
针对于上述问题,引出如何提升虚拟机性能?
1)VMware引入了BT(binary transaction)技术,运行时翻译二进制转换,能让各guest执行特权指令时,按需要在调用的时候直接翻译成对host中特权指令的调用,不用在软件级别进行多次的解码,边运行边调用边转换,基于上述模式,guest用户空间运行在环3上,guest内核空间运行在环1,host内核运行在环0上,BT在环1上进行监控-----据上述,用BT的方式替代软件模拟CPU的方式就必须是host和guest底层物理架构保持一致(guest内核运行在环1的缘故)。
2)CPU硬件虚拟化:如Intel VTS等
在硬件虚拟化中,CPU将增加一个环-1,环-1叫做根环或者根区域,host内核运行在环-1上,-1就是特权指令环,把环0让出来给guest使用(依据上述知识,内核必须使用环0,无论软件虚拟一个CPU还是硬件加一个-1环),guest可以直接调用各种特权指令(至少是自己以为),host能捕获环0上guest的特权调用指令,调用特权指令时由CPU交给环-1,中间不需要软件指令转换。
据上述,引出及总结一下干货:
CPU虚拟化主要由以下形式:
1)模拟:emulation 纯软件,CPU模拟环0123
2)虚拟:virtulization,Guest和Host底层架构保持一致
3)完全虚拟化(full-virtulization):
BT:host完全虚拟出来一个虚拟平台,Guest不清楚自己运行在虚拟环境中,只需要给虚拟机模拟CPU环0,各Guset内核运行在环1
HVM:硬件虚拟化
4)半虚拟化(para-virtulization):为了提高模拟环0造成的性能损失而特别研发的
各Guset内核明确知道自己运行在虚拟化环境中,这时候需要运行特权指令时不是直接调用特权指令,而是向host内核发起请求,这时候Guest发起的指令调用就被简化为简单的对于host内核某些特权指令的操作请求,这时候host的名字就变成了VM Monitor(虚拟机监视器)== HyperVisor
直接跑在硬件平台的是Hypervisor直接管理硬件,相当于是一个内核,内核对底层硬件的操作,一般是CPU和内存,不包括IO,由Hypervisor来管理,hypervisor把CPU和内存的使用虚拟成hyper call,一般对于内核的调用叫做系统调用,对于Hypervisor的调用一般叫做Hyper调用,各guest在使用特权指令时,有些特权指令不影响到其他guest或Hypervisor的都由各guest内核自己直接调用,有影响的需要向Hypervisor发起请求,进行Hyper call调用,中间省去了翻译的过程---各guest的内核必须知道自己运行到虚拟机中,各内核必须能适应对hyper call的调用,所以开发内核时,一旦发生特权指令就不能以为自己调用CPU的指令集而是调用Hyper call,必须要修改能运行在各个操作系统中的内核,至少内核开发者开发时有这种功能------必须要修改操作系统内核来使其知道对Hyper Call的调用
guest的内核不认为自己能直接运行在环0上,明确知道自己不能再直接调用特权指令,一旦发起特权指令调用时,直接发起hyper call。
2.存储设备虚拟化(空间切割):
每一个进程看到的都是线性连续的地址空间,每一个guest默认自己是可以从最低地址空间到最高地址空间寻址内核看到的是整个物理内存空间(至少都是自己以为)
以半虚拟化为例,对内存管理的是hypervisor,hypervisor上运行多个guest,hypervisor实现对CPU和内存的管理,内存被分配成内存页,被分割以后划分给各个guest,每个guest拿到的是离散的非完整的内存空间,内核在研发的时候以为自己是运行在物理地址空间上的,所以以为自己使用的是连续的和完整的物理内存空间(堆中的内存申请是使用malloc进行系统调用从空闲的内存地址空间申请)
据以上描述引出一下,应用访问内存空间过程:guest上的应用进程访问某一个地址、把请求发给CPU,CPU发给MMU,MMU根据内核维护进程的内存映射表转换成guest内核管理的某一段内存地址,hypervisor把guest内核管理的内存地址空间再转换到真正的内存地址空间中,据以上进行了两级虚拟,这就意味着进程在CPU上的地址转换是无效的,只是guest自己内核管理的物理内存空间,第一次转换MMU,第二次转换是软件模拟(hypervisor使用shadow page table)实现,这个实现是一个非常复杂的软件实现,导致了快表TLB很难命中,尤其是多guest场景更是如此(MMU放的是page table,TLB放到是线性空间到物理地址的映射结果),每次guest切换就不得不清空TLB,快表命中率低导致了guest性能地下。
据以上问题引出,引出了虚拟MMU技术,如Intel EPT和AMD NPT,硬件级别提供的MMU虚拟化,实现了从guest应用程序线性地址直接到host物理地址的转换,省去了guest内核维护自己的地址空间的转换。
TLB虚拟化(tagged TLB,给不同guest的TLB打上标签)。
3.IO设备虚拟化:
外存:硬盘等
网络设备:网卡
显示设备:VGA等
键鼠:用焦点捕获的方式,给每一个guest模拟键鼠,建立临时关联关系
其他设备:串口设备等
IO设备虚拟化一般有三种形式:
1)模拟:完全使用软件模拟真实硬件,以网卡为例guest网络请求通过虚拟网卡驱动发送、请求转到host后,host再通过驱动和网卡发送。
2)半虚拟化:guest明确知道自己的硬件设备是虚拟出来的,guest会调用自己的虚拟网卡发送网络请求(用户看来是由网卡驱动的、叫做IO前端驱动,但内存的请求直接由内核发往后端驱动),设备的调用会转换成hypervisor虚拟网卡的调用,hypervisor使用IO stack来存储和调度不同guest的网卡请求,然后再调用后端驱动,利用真正的网卡发包(这种方式一般使用于网卡和硬盘)
3)透传(IO-through):让guest直接使用物理设备,依然需要hypervisor的Device Manager进行分配、管理、协调,硬件必须支持透传技术(Intel VT-d),传统的架构下,所有的IO设备都有一个共享的DMA或集中式管理的DMA(直接内存访问),就意味着hypervisor管理的IO设备都由hypervisor统一管理的DMA来管理,在DMA中使用IOMMU实现从IO地址总线到IO设备自动的转换设备,如果能将某一个IO设备直接分配给某一个guest使用,就意味着hypervisor级别的对某一个端口的调用就只能接受同一个guest进行,所以必须在IOMMU级别进行隔离,这就是Intel VT-d的方案。
中断隔离:
IO会产生大量的终端操作,如果IO端口分配给某一个guest使用,产生端口也必须由该guest处理,IO虚拟化必须正确的分离这些请求
传统设备的中断请求一般有两种方式:
1)可编程终端控制器
2)DMA:基于DMA来实现终端管理时,需要在DMA中嵌入目标内存地址,目标内存地址通常有hypervisor管理的内存地址,因此这个架构要完全访问到所有内存地址才能实现中断管理,并不能实现中断隔离,在Intel VT-d中完成完全的中断映射,从一个硬件设备到guest的内存地址空间的DMA映射,实现了中断隔离。
Intel VT-d:基于北桥的硬件辅助的虚拟化技术,提升IO的灵活性、可靠性
Intel硬件辅助虚拟化:
CPU:vt-x,EPT(MMU虚拟化---tagged-TLB)
I/O:vt-d,在vd-d基础上实现IOV,VMDq
第一类:跟处理器相关的:vt-x
第二类:跟芯片相关的:vt-d
第三类:跟IO相关的:VMDq和SR-IOV
4.网络虚拟化:
实现网络虚拟化常见的有如下方式:
1)桥接:
host网卡当做一个交换机来用,物理机上再虚拟出一个网卡(这个网卡设备就叫做桥接设备),无论是否发往本网卡的mac帧都由host网卡根据MAC地址转发(形象的比喻:桥接是物理网卡作为一个交换机,连接一个host和多个guest,host和guest是在同一个网络里面通过交换机更外界进行通信)。
使用桥接:第一步:建立网桥设备,把物理网卡让出来做交换机,在VMware workstation中网桥设备创建没有展示出来,在linux上网桥设备是由独立的接口,是可以展示出来的
2)仅主机模式:
目的是让各guest通信,让各guest跟host通信,跟外部是隔离的,guest跟host通信需要创建一个虚拟交换机(纯软件方式实现),各guest用虚拟网卡跟虚拟交换机通信,要想让host连接虚拟交换机,就给host虚拟一个网卡(vnet0),连接到虚拟交换机,如果host没有打开网卡间转发功能,就无法从host虚拟网卡转发到物理网卡,数据无法转发出去
使用VMware Workstation之后再Windows中网络适配器出现的vmnet1就是host用来跟guest通信的虚拟网卡
3)路由模式:
根据以上仅主机模式,如果打开了host网卡间转发功能,数据就可以从host虚拟网卡转发到物理网卡(pnet0),进而转发到外部网络中。该host上的Guest-1跟外部其他网络中的host-1通信,host-1收到请求报文的源地址是guest-1的地址,目的地址是host-1的地址,host-1回报文的目的地址是guest-1的地址,源地址是host-1的地址,host-1和guset-1很可能不在同一个网段中,意味着host-1极有可能需要通过另外一个网关才能转到guest-1,所以网关必须指向该host的物理接口地址(必须到达host网卡),但是如果host-1在互联网内,其必然接入了路由器,那么发出的地址都会往路由器发并被路由器转发,要想实现转发到该host中的guest-1就必须使用静态路由,配置host-1发送到该host的物理网卡的静态路由,在路由模式下外网的host-1可以看到该虚拟机网络中的guest-1。
4)NAT模式(在VMware Workstation中一般是vmnet8):
由host创建一个vnmet8虚拟网卡,并且创建一个NAT服务器,如果不想让外网的host-1看到虚拟网络中的guest-1,那么不打开转发功能,guest-1要跟host-1通信时,网关要指向vnet8,通过NAT服务器在数据包到达物理网卡pnet0时,把源地址改为物理网卡的地址,host-1回的时候再回给物理网卡pnet0,物理网卡再通过NAT转化成Guest-1的地址
5)隔离模式:
只有虚拟机之间可以通信,跟物理机不能通信,例如用vmnet2等,虚拟一个交换机,用来虚拟机间相互通信
以上,虚拟化原理基本讲完了。。。。。。。。。。。。。。。。。。
虚拟网络延展:
在Linux上每启动一个虚拟机,都会在我们物理设备上或hypervisor上显示一个虚拟网卡(虚拟网卡也会显示在host上,就相当于虚拟网卡分成了两半,一半在guest上,一半在host上,虚拟网络交换机在host上,就相当于把一半在host上的虚拟网卡连接到虚拟交换机上,两半虚拟网卡的地址都在guest上,在host上的虚拟网卡没有地址),基于上述模型在启动guest的时候必须相应在host上启动一个虚拟网卡,有如下两种:
TUN和TAP是(host)操作系统内核中的虚拟网络设备,不同于普通靠硬件网路板卡实现的设备,这些虚拟的网络设备全部由软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能
TAP:等同于一个以太网设备,它操作第二层数据包如以太网数据帧。TUN模拟了网络层的设备,操作第三层数据包比如IP数据封包。
操作系统通过TUN/TAP设备向绑定该设备的用户空间的程序发送数据,反之,用户空间的程序也可以向操作硬件网络设备那样,通过TUN/TAP设备发送数据。在后种情况下,TUN/TAP设备向操作系统的网络栈投递(或“注入”)数据包,从而模拟从外部接受数据的过程。TUN/TAP都是host上虚拟出来跟guest通信的另一半设备。
每个不同的VLAN中的guest要想使用动态地址获取,需要在每一个虚拟交换机上接一个DHCP服务器(软件方式提供的一个实例,仅服务于各个不同的虚拟网络)
基于脚本配置网桥:
linux上配置桥设备的步骤:
#brctl addrbr br0
#brctl stp br0 on
#ifconfig eth0 0 up
#brctl addif br0 eth0
#ifconfig br0 IP/NETMASK up
#route add default gw GW
以上配置可以基于配置文件
来源:oschina
链接:https://my.oschina.net/u/4348626/blog/4416017