本帖最后由 august 于 2016-8-2 10:35 编辑
[size=1.6]目录
1 硬件虚拟化技术背景 2 KVM的内部实现概述
2.1 KVM的抽象对象
2.2 KVM的vcpu
2.3 KVM的IO虚拟化
2.3.1 IO的虚拟化
2.3.2 VirtIO 3 KVM-IO可能优化地方
3.1 Virt-IO的硬盘优化
3.2 普通设备的直接分配(Direct Assign)
3.3 普通设备的复用 ===================================
1 硬件虚拟化技术背景 硬件虚拟化技术通过虚拟化指令集、MMU(Memory Map Unit)以及IO来运行不加修改的操作系统。 传统的处理器通过选择不同的运行(Ring 特权)模式,来选择指令集的范围,内存的寻址方式,中断发生方式等操作。在原有的Ring特权等级的基础上,处理器的硬件虚拟化技术带来了一个新的运行模式:Guest模式[1],来实现指令集的虚拟化。当切换到Guest模式时,处理器提供了先前完整的特权等级,让Guest操作系统可以不加修改的运行在物理的处理器上。Guest与Host模式的处理器上下文完全由硬件进行保存与切换。此时,虚拟机监视器(Virtual Machine Monitor)通过一个位于内存的数据结构(Intel称为VMCS, AMD称为VMCB)来控制Guest系统同Host系统的交互,以完成整个平台的虚拟化。 传统的操作系统通过硬件MMU完成虚拟地址到物理地址的映射。在虚拟化环境中,Guest的虚拟地址需要更多一层的转换,才能放到地址总线上: 其中MMU1可以由软件模拟(Shadow paging中的vTLB)或者硬件实现(Intel EPT、AMD NPT)。MMU2由硬件提供。 系统的IO虚拟化技术,通常是VMM捕捉Guest的IO请求,通过软件模拟的传统设备将其请求传递给物理设备。一些新的支持虚拟化技术的设备,通过硬件技术(如Intel VT-d),可以将其直接分配给Guest操作系统,避免软件开销。 [1]X86处理器的生产厂商有自己的称谓,比如英特尔将Guest模式称为non-root operation,与之相对的是root operation,本文称为host模式。 2 KVM的内部实现概述 KVM是Linux内核的一个模块,基于硬件虚拟化技术实现VMM的功能。该模块的工作主要是通过操作与处理器共享的数据结构来实现指令集以及MMU的虚拟化,捕捉Guest的IO指令(包括Port IO和mmap IO)以及实现中断虚拟化。至于IO设备的软件模拟,是通过用户程序QEMU来实现的。QEMU负责解释IO指令流,并将其请求换成系统调用或者库函数传给Host操作系统,让Host上的驱动去完成真正的IO操作。她们之间的关系如下图所示: 从Host操作系统的角度来看,KVM Guest操作系统相当于一个进程运行在系统上,普通的命令如kill、top、taskset等可以作用于该Guest。该进程的用户虚拟空间就是Guest的物理空间,该进程的线程对应着Guest的处理器。 从Qemu的角度来看,KVM模块抽象出了三个对象,她们分别代表KVM自己,Guest的虚拟空间以(VM)及运行虚拟处理器(VCPU)。这三个对象分别对应着三个文件描述符,Qemu通过文件描述符用系统调用IOCTL来操作这三个对象,同KVM交互。此时,Qemu主要只模拟设备,她以前的CPU和MMU的模拟逻辑都被kvm.ko取代了。 2.1 KVM的抽象对象 KVM同应用程序(Qemu)的交互接口为/dev/kvm,通过open以及ioctl系统调用可以获取并操作KVM抽象出来的三个对象,Guest的虚拟处理器(fd_vcpu[N]), Guest的地址空间(fd_vm), KVM本身(fd_kvm)。其中每一个Guest可以含有多个vcpu,每一个vcpu对应着Host系统上的一个线程。 Qemu启动Guest系统时,通过/dev/kvm获取fd_kvm和fd_vm,然后通过fd_vm将Guest的“物理空间”mmap到Qemu进程的虚拟空间,并根据配置信息创建vcpu[N]线程,返回fd_vcpu[N]。然后Qemu将操作fd_vcpu在其自己的进程空间mmap一块KVM的数据结构区域。该数据结构(下图中的shared)用于同kvm.ko交互,包含Guest的IO信息,如端口号,读写方向,内存地址等。Qemu通过这些信息,调用虚拟设备注册的回调函数来模拟设备的行为,并将Guest IO请求换成系统请求发送给Host系统。由于Guest的地址空间已经映射到Qemu的进程空间里面,Qemu的虚拟设备逻辑可以很方便的存取Guest地址空间里面的数据。三个对象之间的关系如下图所示: 图中vm-exit代表处理器进入host模式,执行kvm和Qemu的逻辑。vm-entry代表处理器进入Guest模式,执行整个Guest系统的逻辑。如图所示,Qemu通过三个文件描述符同kvm.ko交互,然后kvm.ko通过vmcs这个数据结构同处理器交互,最终达到控制Guest系统的效果。其中fd_kvm主要用于Qemu同KVM本身的交互,比如获取KVM的版本号,创建地址空间、vcpu等。fd_vcpu主要用于控制处理器的模式切换,设置进入Guest mode前的处理器状态等等(内存寻址模式,段寄存器、控制寄存器、指令指针等),同时Qemu需要通过fd_vcpu来mmap一块KVM的数据结构区域。fd_vm主要用于Qemu控制Guest的地址空间,向Guest注入虚拟中断等。 2.2 KVM的vcpu 如前文所述,KVM的vcpu对应着host系统上的一个线程。从Qemu的角度来看,她运行在一个loop中: - for (;;) {
- kvm_run(vcpu);
- switch (shared_data->exit_reason) {
- ...
- case KVM_IO:
- handle_io(vcpu);
- break;
- case KVM_MMIO:
- handle_mmio(vcpu);
- break;
- ...
- }
- }
复制代码 该线程同Guest的vcpu紧密相连。如果我们把线程的执行看作Guest vcpu的一部分,那么从Host的角度来看,该vcpu在三种不同的上下文中运行:Host user/Host kernel/Guest,将运行于一个更大的循环当中。该vcpu的运行逻辑如下图:
实际上,在host上通过ps命令看到的关于vcpu这个线程的运行时间正是这三种上下文的总和。
|