最近在看Xen在2003年发表在sosp上的论文《Xen and the Art of Virtualization》,中途遇到一些不理解的技术点,在网络上查找相关资料,发现大多数人都只是介绍了一些Xen的历史和相关概念,对底层实现方案几乎没有涉及,少有的几篇博客虽然介绍了一些内容,但是我个人觉得很多地方过于笼统,而且互相之间内容十分类似。因此决定在深入学习之后总结出一篇博客,既能理清自己在学习过程中的思路,也能帮助后来者快速入门。

这篇博客不太适合对于虚拟化和操作系统方向的初学者,有些概念我不会详细介绍,默认读者已经掌握,当然即使是初学者想要通过本篇博客学习Xen,我觉得也是可以的,遇到不懂的地方可以自行Google(像para-virtualization和full-virtualization这类概念,我在前面已经介绍过了,这里就不提了)。

由于我的理解是基于以往研究其他系统的经验,对于Xen的底层源码我没有深入研究过,只是根据上面我提到的论文以及网络上的资料分析整理而成,难免有疏漏或者错误,还请读者不吝批评指正。

总体介绍

先抛开Xen的虚拟化方案,不妨设想一下,一个什么样的虚拟化方案才是最好的呢?由于我本人就是研究虚拟化和操作系统的,如果让我来回答这个问题,那我希望有以下几点:

  1. 性能应该很好,最好能和裸机时相差无几。
  2. 兼容性应该足够好,最好能够研究出一种虚拟机,guest os可以不加修改直接运行。
  3. 安全性和隔离性应该得到提升,因为加了一个vmm(virtual machine monitor,也可称为hypervisor),相当于加了一个隔离层。

如果上述三点是我们设计一个虚拟机的目标,那么我们首先要问,这三点能不能同时满足呢?其实,这三个要求本身就是相互制约的。首先,1和3之间的矛盾关系很明显,隔离性越好安全性就会越高,可是由于模块之间的隔离,它们互相调用时的开销也就会越大,因此性能必然有所影响,看过之前我介绍微内核的虚拟化方案的读者应该对此深有体会。接下来我们看看1和2,从道理上分析,我们知道,在一般情况下越是通用的东西在具体的应用场景上是不如专用的东西的。所以兼容性的提升在针对某一个具体guest os时的性能就不会好过针对某一个具体guest os情况设计的方案,反映在实现原理上就是para-virtualization和full-virtualization之间的矛盾。para-virtualization需要修改guest os的源码,这在一定程度上是一种开销,但是它可以显著提升性能,尤其在没有硬件支持的条件下,这种性能提升是显著的。

接下来我们来看xen的虚拟化方案,它需要修改guest os的源码,但是这种修改量是可接受的,如下图所示,展示了guest os的修改量(数据来自于xen的论文)。

所以说xen的缺点就是需要修改guest os的源码,但是它并不需要修改guest application的源码,事实上它对于应用程序提供的ABI(application binary interface,应用二进制接口)是兼容的,其实修改os并不可怕,而且上图也展示了,修改量其实不大,但是如果修改guest application就变得不可接受了,因为应用程序的数量非常之大,而且开发应用程序的人可不愿意做额外的修改,那么这种解决方案很可能就被淘汰了。

综上,xen采用了para-virtualization的方案同时兼顾了1和3,对于2,xen需要修改guest os的源码,不太理想,但是由于修改量可以接受,因此也还可以,xen是剑桥大学研究的,可以说在虚拟化领域是十分有代表性的,值得深入学习。(个人感觉)

Xen的虚拟化方法

在本文中,当我们提到xen的时候,其实是指vmm,也可以称之为hypervisor,因为xen的主要工作就是向guest os提供一个虚拟的运行环境。一般来讲,vmm的功能就是向guest os提供一个虚拟的硬件,具体来说,vmm一定要能够处理各种特权指令(supervisor instruction)并且能够虚拟化x86 MMU(其实就是能够处理页表的虚拟化)。这些问题当然可以解决,但是往往需要增加程序的复杂性和性能。这里我们举个例子(其实是xen的论文里举的例子):VMware的ESX Server,它通过重写hosted machine code,当与vmm发生交互时,插入trap指令。这就需要对整个guest os kernel作出修改,将所有的non-trap privileged都改成trap,这样这些指令就可以被捕获(caught)并且处理(handled)。ESX Server实现了影子结构(虚拟硬件的影子结构),每次对影子结构的更新操作都要执行trap,这会产生一个很大的开销。比方说,创建一个进程,需要执行fork()和exec()两个系统调用,这两个系统调用的实现还需要操作页表,这就需要很多额外的trap和与vmm的交互。

VMware还是基于full-virtualization的方法,xen让guest os可以同时访问real and virtual resource,比方说,guest os可以直接访问物理页表,这样可以提升性能。具体来说,xen通过vmm来管理底层硬件,向guest os提供一种硬件抽象,它类似但不等同于底层的物理硬件,这种方法就被称为para-virtualization。xen的设计原理主要为以下四条:

  1. 不需要修改ABI(application binary interface),否则应用程序开发者还要把应用程序移植到xen平台上。因此xen必须能够虚拟化现有的标准ABI所需的所有硬件feature。
  2. 需要能够支持多应用的操作系统,这需要允许guest os完成复杂的服务配置。
  3. 在硬件不支持虚拟化的情况下,采用para-virtualization获得高性能和隔离性。
  4. 即使在支持虚拟化的硬件架构上,对guest os完全隐藏资源虚拟化的影响也面临着正确性和性能的风险。

虚拟机接口(The Virtual Machine Interface)

接下来我们讨论xen向guest os提供的虚拟机抽象接口(virtual machine abstraction),以及guest os如何修改以便适应这些硬件接口。在此之前我们先区分几个概念,guest os、domain和hypervisor。guest os代表Xen需要虚拟化的操作系统,domain是guest os运行时所在的虚拟机,guest os和domain的区别类似于"程序"和"进程"之间的关系,两者一个是静态的,另一个是动态的。我们称Xen为hypervisor,因为Xen相比于guest os的supervisor code,运行在一个更高的特权级。

对于x86的硬件接口,我们将其分为三个主要部分:内存管理、CPU的运行和I/O外设。

如上图所示,接下来我们分别进行讨论

内存管理

对内存进行虚拟化是这三部分中最难的,无论是对于hypervisor的设计还是对于guest os的修改(在关于NOVA那篇博客中我已经提到,在没有硬件支持的条件下,虚拟化内存是十分困难的,因为硬件不感知多层次的页表,需要手工操作,典型的方法是影子页表)。x86硬件不支持由软件管理的TLB,每次TLB miss处理器都会自动通过硬件执行一次page-table walk,在这种情况下,想要实现最优的性能,最好的办法是设法让所有地址转换过程中需要访问到的页表都可以由硬件直接访问到。除此以外,由于TLB不是tagged的,不能标识对应的guest os,所以每次地址空间转换都需要执行TLB flush。由于这些限制,Xen在内存管理方面采取两点措施:

  1. 由guest os直接负责申请和管理硬件的页表,尽量绕过Xen来确保安全性和隔离性。
  2. Xen存在于每一个guest os的高64M空间(一个段的空间),这样确保进入和离开hypervisor的时候不需要额外的TLB flush操作。

当guest os想要申请一个页面的时候,比方说它创建一个进程时,它从自己管理的物理页表区域里申请并初始化一个页,并且向Xen进行注册。此时,guest os必须放弃对页表的直接

写权限,接下来更新页表的操作都需要经过Xen的验证。这些限制可以用多种方式实现,比如只允许guest os去map它的page table,但是不允许更改它的page table。guest os可以批量发起更新请求,减少进入和退出hypervisor所带来的开销。每一个地址空间的高64MB是不允许guest os访问或者重新映射的(remap)。这个地址区域x86的ABI是用不到的,所以它不会破坏应用程序的兼容性。

段的虚拟化采用类似的方式,通过Xen去验证之后才去更新硬件的段描述符表(segment descriptor table),对于x86的段描述符,主要有两条限制:

  1. 它们必须比Xen的特权级低;
  2. 它们不能访问Xen保留的地址空间端口。

CPU

在native环境下,我们一般认为os应该运行在系统的最高特权级上,加入了hypervisor以后,为了保证guest os的错误不会影响hypervisor(进而影响其他的guest os),guest os必须被修改从而运行在低一级的特权级上。如果处理器只支持两个特权级,那么guest os就需要和guest application运行在相同特权级上,这时需要引入虚拟特权级来区分guest os和guest application,具体的做法其实类似于操作系统的常规做法,把os和应用程序分别放在不同的地址,通过hypervisor传递控制信息,这些控制信息包括修改当前的虚拟特权级和切换当前的地址空间。如果处理器支持地址空间的tags,还可以避免高开销的TLB flush操作。

由于x86架构支持4种特权级,ring0-ring3,因此比较容易做到虚拟化特权级。只有ring0可以执行特权指令,而ring1和ring2一般都是不使用的,我们可以修改guest os的源码,让它运行在ring1,这样guest os不能直接执行特权指令,同时可以区分guest os和guest application。guest os的特权指令的执行需要通过Xen验证并由其仿真执行,比如申请一个页表或者让出处理器当前执行权限。当guest os试图直接执行敏感指令,处理器可能什么也不做(silently)或者产生一个异常(fault),因为只有Xen运行在特权模式(ring0)。异常包括内存访问错误(page falut)和软件trap,在x86架构上都可以直接进行虚拟化。Xen中有一个表(类似于中断向量表),根据每种异常类型跳转到对应的handler去处理。这里面比较特殊的是page fault的处理方法,因为guest os不能访问CR2寄存器(保存faulting address),因此每次发生page falut都需要通过Xen把CR2寄存器的内容拷贝到guest os的地址空间(栈空间)。另一种影响性能的异常是system call,为了提高性能,我们允许每个guest os在Xen中注册一个fast exception handler,不需要通过ring0就可以被处理器直接访问,这个handler在被插入到Xen的中断向量表之前就需要被验证,Xen会检查这个handler的代码段是不是只能在ring0中执行。除此以外,其他问题都很好解决,比如handler对应的代码段不在内存中,那么当Xen执行iret指令想要跳转到handler时会产生额外的page fault,Xen会另行处理(按照正常的page fault进行处理)。

Device I/O

对于full-virtualization来说,需要仿真具体外设,而Xen采用para-virtualization,只需要完成一些外设抽象,我们因此只需设计接口能够满足高效性并且满足我们的安全性和隔离性要求,从这个角度来说,I/O数据通过Xen与每一个domain进行传输,这种数据传输通过共享内存和异步buffer来实现。这提供了系统间通信的一种高效的机制,同时允许Xen去有效的验证(比方说,检验一个buffer是否在一个domain的内存区域中)。

接下来一个重要的部分是外设中断,类似于真实的物理中断,Xen提供一种相对轻量级的时间传递机制,用于向domain传递异步notification(这个notification类似于interrupt)。这种notification的具体做法是:更新一个等待事件的bitmap区域进而调用一个guest os特定的事件处理程序。这种回调能够由guest os延迟,去避免notification造成额外的开销。

系统架构

首先看一张Xen虚拟机的总体架构图。

Xen的虚拟化详解的更多相关文章

  1. [转帖]PC虚拟化主流:KVM、XEN、OpenVZ详解

    PC虚拟化主流:KVM.XEN.OpenVZ详解 https://zhuanlan.zhihu.com/p/90920566 1.pc虚拟化——KVM KVM是完整的硬件虚拟化,可以在Windows ...

  2. Xen虚拟化基本原理详解

    标签:虚拟化 xen 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://wangzan18.blog.51cto.com/80210 ...

  3. GPU虚拟化技术详解

    GPU虚拟化技术详解 GPU英文名称为Graphic Processing Unit,GPU中文全称为计算机图形处理器,1999年由NVIDIA公司提出. 一.GPU概述 GPU这一概念也是相对于计算 ...

  4. centos6.7环境之kvm虚拟化quem工具配置及使用详解

    环境准备 需要勾选CPU的虚拟化支持,支持cpu虚拟化的CPU列表: intel支持虚拟化技术CPU列表: Intel 6 Cores / 12 Threads CPU Number: Code Na ...

  5. wpf 客户端【JDAgent桌面助手】开发详解(三) 瀑布流效果实现与UI虚拟化优化大数据显示

    目录区域: 业余开发的wpf 客户端终于完工了..晒晒截图 wpf 客户端[JDAgent桌面助手]开发详解-开篇 wpf 客户端[JDAgent桌面助手]详解(一)主窗口 圆形菜单... wpf 客 ...

  6. VMware 虚拟化编程(14) — VDDK 的高级传输模式详解

    目录 目录 前文列表 虚拟磁盘数据的传输方式 Transport Methods Local File Access NBD and NBDSSL Transport SAN Transport Ho ...

  7. VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 VixDiskLib_GetMetadataKeys VixDiskLib_ReadMetadata 获取虚拟磁盘元数据 VixDiskLib_ ...

  8. VMware 虚拟化编程(6) — VixDiskLib 虚拟磁盘库详解之二

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 VixDiskLib_Open 打开 VMDK File VixDiskLib_Read 读取 VMDK File 数据 VixDiskLib_ ...

  9. VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 虚拟磁盘数据的传输方式 Transport Methods VixDiskLib_ListTransportModes 枚举支持的传输模式 Vi ...

随机推荐

  1. Js DOM 详解

    DOM事件类 基本概念 DOM事件的级别 1.DOM0 element.onclick = function(){} 2.DOM2 element.addEventListener("cli ...

  2. mybatis实战教程三:多对多关联

    MyBatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作 一.创建student.te ...

  3. JSTL遇到的问题

    1.jstl 中不可以用关键字命名 例如class new. 2.jstl取值的问题 如果jstl通过对象.属性取值 属性值中包括特殊字符(例如:31/20180131195356867.txt&qu ...

  4. linux_运维职责

    运维准则: 不要删文件,移动文件,可以复原,一个月后什么事没有,删除 运维人员主要关注哪些方面? CPU:对计算机工作速度和效率起决定性作用(intel amd) 内存: 临时存放数据:容量和处理速度 ...

  5. 错误: 非法字符: '\ufeff'

    单独设置出错的类的编码方式   改成普通的utf-8格式即可

  6. Linux Shell 文件描述符 及 stdin stdout stderr 重定向

    Abstract: 1) Linux Shell 命令的标准输入.标准输出.标准错误,及其重定位: 2)Linux Shell 操作自定义文件描述符: 文件描述符是与文件相关联的一些整数,他们保持与已 ...

  7. 【转】vim取消高亮显示

    vim是vi的加强版. 进入vim或编辑完毕按esc后,输入/可帮助查找字符串,例如/main帮助查找main函数,找到的main高亮显示. 取消高量显示的两个办法: 1)按esc键,输入:nohl ...

  8. HH的项链

    传送门 题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链变得越 ...

  9. Lambda表达式详解 (转)

    前言 1.天真热,程序员活着不易,星期天,也要顶着火辣辣的太阳,总结这些东西. 2.夸夸lambda吧:简化了匿名委托的使用,让你让代码更加简洁,优雅.据说它是微软自C#1.0后新增的最重要的功能之一 ...

  10. window.print打印指定html元素中的内容

    通常有些时候我们项目过程中使用到打印功能,而wndow.print便是系统里提供的一个函数. 但是直接使用的话,它打印的将是整个页面的所有元素,而有些时候我们又只需要打印部分内容. <body& ...