KVM 虚拟化原理探究(4)— 内存虚拟化

标签(空格分隔): KVM


内存虚拟化简介

前一章介绍了CPU虚拟化的内容,这一章介绍一下KVM的内存虚拟化原理。可以说内存是除了CPU外最重要的组件,Guest最终使用的还是宿主机的内存,所以内存虚拟化其实就是关于如何做Guest到宿主机物理内存之间的各种地址转换,如何转换会让转换效率更高呢,KVM经历了三代的内存虚拟化技术,大大加快了内存的访问速率。

传统的地址转换

在保护模式下,普通的应用进程使用的都是自己的虚拟地址空间,一个64位的机器上的每一个进程都可以访问0到2^64的地址范围,实际上内存并没有这么多,也不会给你这么多。对于进程而言,他拥有所有的内存,对内核而言,只分配了一小段内存给进程,待进程需要更多的进程的时候再分配给进程。

通常应用进程所使用的内存叫做虚拟地址,而内核所使用的是物理内存。内核负责为每个进程维护虚拟地址到物理内存的转换关系映射。

首先,逻辑地址需要转换为线性地址,然后由线性地址转换为物理地址。

逻辑地址 ==> 线性地址 ==> 物理地址

逻辑地址和线性地址之间通过简单的偏移来完成。

一个完整的逻辑地址 = [段选择符:段内偏移地址],查找GDT或者LDT(通过寄存器gdtr,ldtr)找到描述符,通过段选择符(selector)前13位在段描述符做index,找到Base地址,Base+offset就是线性地址。

为什么要这么做?据说是Intel为了保证兼容性。

逻辑地址到线性地址的转换在虚拟化中没有太多的需要介绍的,这一层不存在实际的虚拟化操作,和传统方式一样,最重要的是线性地址到物理地址这一层的转换。

传统的线性地址到物理地址的转换由CPU的页式内存管理,页式内存管理。

页式内存管理负责将线性地址转换到物理地址,一个线性地址被分五段描述,第一段为基地址,通过与当前CR3寄存器(CR3寄存器每个进程有一个,线程共享,当发生进程切换的时候,CR3被载入到对应的寄存器中,这也是各个进程的内存隔离的基础)做运算,得到页表的地址index,通过四次运算,最终得到一个大小为4K的页(有可能更大,比如设置了hugepages以后)。整个过程都是CPU完成,进程不需要参与其中,如果在查询中发现页已经存在,直接返回物理地址,如果页不存在,那么将产生一个缺页中断,内核负责处理缺页中断,并把页加载到页表中,中断返回后,CPU获取到页地址后继续进行运算。

KVM中的内存结构

由于qemu-kvm进程在宿主机上作为一个普通进程,那对于Guest而言,需要的转换过程就是这样。

  Guest虚拟内存地址(GVA)
|
Guest线性地址
|
Guest物理地址(GPA)
| Guest
------------------
| HV
HV虚拟地址(HVA)
|
HV线性地址
|
HV物理地址(HPA)

What's the fu*k ?这么多...

别着急,Guest虚拟地址到HV线性地址之间的转换和HV虚拟地址到线性地址的转换过程可以省略,这样看起来就更清晰一点。

  Guest虚拟内存地址(GVA)
|
Guest物理地址(GPA)
| Guest
------------------
| HV
HV虚拟地址(HVA)
|
HV物理地址(HPA)

前面也说到KVM通过不断的改进转换过程,让KVM的内存虚拟化更加的高效,我们从最初的软件虚拟化的方式介绍。

软件虚拟化方式实现

第一层转换,由GVA->GPA的转换和传统的转换关系一样,通过查找CR3然后进行页表查询,找到对应的GPA,GPA到HVA的关系由qemu-kvm负责维护,我们在第二章KVM启动过程的demo里面就有介绍到怎样给KVM映射内存,通过mmap的方式把HV的内存映射给Guest。

struct kvm_userspace_memory_region region = {
.slot = 0,
.guest_phys_addr = 0x1000,
.memory_size = 0x1000,
.userspace_addr = (uint64_t)mem,
};

可以看到,qemu-kvm的kvm_userspace_memory_region结构体描述了guest的物理地址起始位置和内存大小,然后描述了Guest的物理内存在HV的映射userspace_addr,通过多个slot,可以把不连续的HV的虚拟地址空间映射给Guest的连续的物理地址空间。

软件模拟的虚拟化方式由qemu-kvm来负责维护GPA->HVA的转换,然后再经过一次HVA->HPA的方式,从过程上来看,这样的访问是很低效的,特别是在当GVA到GPA转换时候产生缺页中断,这时候产生一个异常Guest退出,HV捕获异常后计算出物理地址(分配新的内存给Guest),然后重新Entry。这个过程会可能导致频繁的Guest退出,且转换过程过长。于是KVM使用了一种叫做影子页表的技术。

影子页表的虚拟化方式

影子页表的出现,就是为了减少地址转换带来的开销,直接把GVA转换到HVP的技术。在软件虚拟化的内存转换中,GVA到GPA的转换通过查询CR3寄存器来完成,CR3保存了Guest中的页表基地址,然后载入MMU来做地址转换。

在加入了影子页表的技术后,当访问到CR3寄存器的时候(可能是由于Guest进程后导致的),KVM捕获到这个操作,CPU虚拟化章节 EXIT_REASON_CR_ACCESS,qemu-kvm通过载入特俗的CR3和影子页表来欺骗Guest这个就是真实的CR3,后面的操作就和传统的访问内存的方式一致,当需要访问物理内存的时候,只会经过一层的影子页表的转换。

影子页表由qemu-kvm进程维护,实际上就是一个Guest的页表到宿主机页表的映射,每一级的页表的hash值对应到qemu-kvm中影子页表的一个目录。在初次GVA->HPA的转换时候,影子页表没有建立,此时Guest产生缺页中断,和传统的转换过程一样,经过两次转换(VA->PA),然后影子页表记录GVA->GPA->HVA->HPA。这样产生GVA->GPA的直接关系,保存到影子页表中。

影子页表的引入,减少了GVA->HPA的转换过程,但是坏处在于qemu-kvm需要为Guest的每个进程维护一个影子页表,这将带来很大的内存开销,同时影子页表的建立是很耗时的,如果Guest进程过多,将导致频繁的影子页表的导入与导出,虽然用了cache技术,但是还是软件层面的,效率并不是最好,所以Intel和AMD在此基础上提供了硬件虚拟化技术。

EPT硬件加速的虚拟化方式



EPT(extended page table)可以看做一个硬件的影子页表,在Guest中通过增加EPT寄存器,当Guest产生了CR3和页表的访问的时候,由于对CR3中的页表地址的访问是GPA,当地址为空时候,也就是Page fault后,产生缺页异常,如果在软件模拟或者影子页表的虚拟化方式中,此时会有VM退出,qemu-kvm进程接管并获取到此异常。但是在EPT的虚拟化方式中,qemu-kvm忽略此异常,Guest并不退出,而是按照传统的缺页中断处理,在缺页中断处理的过程中会产生EXIT_REASON_EPT_VIOLATION,Guest退出,qemu-kvm捕获到异常后,分配物理地址并建立GVA->HPA的映射,并保存到EPT中,将EPT载入到MMU,下次转换时候直接查询根据CR3查询EPT表来完成GVA->HPA的转换。以后的转换都由硬件直接完成,大大提高了效率,且不需要为每个进程维护一套页表,减少了内存开销。

在笔者的测试中,Guest和HV的内存访问速率对比为3756MB/s对比4340MB/s。可以看到内存访问已经很接近宿主机的水平了。

总结

KVM内存的虚拟化就是一个将虚拟机的虚拟内存转换为宿主机物理内存的过程,Guest使用的依然是宿主机的物理内存,只是在这个过程中怎样减少转换带来的开销成为优化的主要点。

KVM经过软件模拟->影子页表->EPT的技术的进化,效率也越来越高。

[原] KVM 虚拟化原理探究(4)— 内存虚拟化的更多相关文章

  1. [原] KVM 虚拟化原理探究(5)— 网络IO虚拟化

    KVM 虚拟化原理探究(5)- 网络IO虚拟化 标签(空格分隔): KVM IO 虚拟化简介 前面的文章介绍了KVM的启动过程,CPU虚拟化,内存虚拟化原理.作为一个完整的风诺依曼计算机系统,必然有输 ...

  2. [原] KVM 虚拟化原理探究(1)— overview

    KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...

  3. [原] KVM 虚拟化原理探究 —— 目录

    KVM 虚拟化原理探究 -- 目录 标签(空格分隔): KVM KVM 虚拟化原理探究(1)- overview KVM 虚拟化原理探究(2)- QEMU启动过程 KVM 虚拟化原理探究(3)- CP ...

  4. [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化

    KVM 虚拟化原理探究(6)- 块设备IO虚拟化 标签(空格分隔): KVM [toc] 块设备IO虚拟化简介 上一篇文章讲到了网络IO虚拟化,作为另外一个重要的虚拟化资源,块设备IO的虚拟化也是同样 ...

  5. [原] KVM 虚拟化原理探究(3)— CPU 虚拟化

    KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...

  6. [原] KVM 虚拟化原理探究(2)— QEMU启动过程

    KVM 虚拟化原理探究- QEMU启动过程 标签(空格分隔): KVM [TOC] 虚拟机启动过程 第一步,获取到kvm句柄 kvmfd = open("/dev/kvm", O_ ...

  7. KVM 介绍(2):CPU 和内存虚拟化

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  8. KVM 内存虚拟化

    内存虚拟化的概念     除了 CPU 虚拟化,另一个关键是内存虚拟化,通过内存虚拟化共享物理系统内存,动态分配给虚拟机.虚拟机的内存虚拟化很象现在的操作系统支持的虚拟内存方式,应用程序看到邻近的内存 ...

  9. 2017.4.28 KVM 内存虚拟化及其实现

    概述 KVM(Kernel Virtual Machine) , 作为开源的内核虚拟机,越来越受到 IBM,Redhat,HP,Intel 等各大公司的大力支持,基于 KVM 的开源虚拟化生态系统也日 ...

随机推荐

  1. 记一个mvn奇怪错误: Archive for required library: 'D:/mvn/repos/junit/junit/3.8.1/junit-3.8.1.jar' in project 'xxx' cannot be read or is not a valid ZIP file

    我的maven 项目有一个红色感叹号, 而且Problems 存在 errors : Description Resource Path Location Type Archive for requi ...

  2. 在ASP.NET Core应用中如何设置和获取与执行环境相关的信息?

    HostingEnvironment是承载应用当前执行环境的描述,它是对所有实现了IHostingEnvironment接口的所有类型以及对应对象的统称.如下面的代码片段所示,一个HostingEnv ...

  3. obj.style.z-index的正确写法

    obj.style.z-index的正确写法 今天发现obj.style.z-index在js里面报错,后来才知道在js里应该把含"-"的字符写成驼峰式,例如obj.style.z ...

  4. 通过AngularJS实现前端与后台的数据对接(一)——预备工作篇

    最近,笔者在做一个项目:使用AngularJS,从而实现前端与后台的数据对接.笔者这是第一次做前端与后台的数据对接的工作,因此遇到了许多问题.笔者在这些问题中,总结了一些如何实现前端与后台的数据对接的 ...

  5. C#使用Aspose.Cells导出Excel简单实现

    首先,需要添加引用Aspose.Cells.dll,官网下载地址:http://downloads.aspose.com/cells/net 将DataTable导出Xlsx格式的文件下载(网页输出) ...

  6. C++整数转字符串的一种方法

    #include <sstream> //ostringstream, ostringstream::str() ostringstream stream; stream << ...

  7. 谁偷了我的热更新?Mono,JIT,iOS

    前言 由于匹夫本人是做游戏开发工作的,所以平时也会加一些玩家的群.而一些困扰玩家的问题,同样也困扰着我们这些手机游戏开发者.这不最近匹夫看自己加的一些群,常常会有人问为啥这个游戏一更新就要重新下载,而 ...

  8. .NET Web的身份认证

    百度一下”asp.net身份认证“,你会得到很多相关的资料,这些资料通常上来就会介绍诸如”Form认证“”Windows认证“等内容,而没有给出一个完整的流程.初学者对此往往一头雾水,我也曾经被坑过很 ...

  9. 布里斯班Twilight Bay Run半程马拉松

    自从8月3日跑了半马以后,又一鼓作气报了11月份的西昌马拉松.与第一次马拉松的只求完赛目标不同,第二次当然想取得一个更好的成绩.所以8月份练的比较猛,基本上是练2.3天休息一天,周么还要拉个长于21公 ...

  10. LINQ Group By操作

    在上篇文章 .NET应用程序与数据库交互的若干问题 这篇文章中,讨论了一个计算热门商圈的问题,现在在这里扩展一下,假设我们需要从两张表中统计出热门商圈,这两张表内容如下: 上表是所有政区,商圈中的餐饮 ...