Xen 虚拟机简介
本文主要面向 Xen 新手以及想要了解更多关于 Xen 的开发者。然而,前两节是通用的,不涉及代码。
Xen VMM(虚拟机监视器)是一个开源项目,正在英国剑桥大学的计算机实验室中开发。它使我们能够创建许多虚拟机,每个虚拟机都运行一个操作系统的实例。
这些客户操作系统可以是打过补丁的 Linux 内核(2.4 或 2.6 版本)或打过补丁的 NetBSD/FreeBSD 内核。用户应用程序可以在客户操作系统上按原样运行,无需更改代码。Sun 也在致力于 Solaris-on-Xen 移植。
一年多以来,我一直在密切关注 Xen 项目。在阅读了 OLS(渥太华 Linux 研讨会)2004 年会议论文集中的相关内容后,我对 Xen 产生了兴趣。在当地 UNIX 小组会议上听取了关于该主题的 有趣的讲座 后,我的兴趣更浓厚了。
完全虚拟化已经通过一些硬件模拟器完成;其中一个流行的开源项目是 Bochs IA-32 模拟器。另一个著名的项目是 qemu。硬件模拟器的缺点是性能。
Xen 项目(准虚拟化)背后的想法并不新鲜。性能指标及其实现的高效率可以被视为一项突破。运行 Xen 的开销确实非常小,约为 3%。
正如开头所说,目前 Xen 会修补内核。但是,未来的处理器将支持虚拟化,以便内核可以在不打补丁的情况下运行。例如,英特尔 VT 和 AMD Pacifica 处理器都将包含此类支持。
2005 年 8 月,XenSource 是一家基于 Xen 开发虚拟化解决方案的商业公司,在英特尔开发者论坛 (IDF) 上宣布,它已将支持英特尔 VT 的平台与 Xen 一起使用,以虚拟化 Linux 和 Microsoft Windows XP SP2。
使用英特尔 VT 的 Xen 或使用 AMD Pacifica 的 Xen 将与其他虚拟化方法以及本机操作相比具有竞争力,甚至更胜一筹。
在同一领域,VMware 是一家商业公司,开发了 ESX 服务器,这是一种不基于 Xen 的虚拟化解决方案。VMware 在 2005 年 8 月初宣布,它将根据一项名为 VMware Community Source 的新计划,向其合作伙伴提供对 VMware ESX Server 源代码和接口的访问权限。
VMware 的一个明显优势是它不需要在客户操作系统上打补丁。VMware 解决方案还使客户操作系统能够成为 Windows。然而,VMware 解决方案可能比 Xen 慢,因为它使用影子页表,而 Xen 同时使用直接页表和影子页表。
Xen 已经捆绑在一些发行版中,包括 Fedora Core 4、Debian 和 SuSE Professional 9.3,并且将包含在 RHEL5 中。Fedora 项目有 用于安装 Xen 的 RPM,其他 Linux 发行版也准备了 Xen 的安装包。
此外,还有一个 Xen 到 IA-64 的移植版本。另外,已经有一篇关于该主题的有趣的硕士论文,"在 Itanium 上使用 Xen 进行 HPC 虚拟化"。
对其他处理器的支持正在进行中。Xen 团队正在开发 x86_64 端口,而 IBM 正在开发 Power5 支持。
Xen 网站上提供了一些版本供下载,包括 2.0.* 版本和 xen-unstable 版本,也称为 xen-3.0-devel。您还可以使用 Mercurial 源代码管理系统下载最新版本。
我安装了 xen-3.0-devel,因为当时 2.0.* 版本没有我需要的 AGP 支持。自从我安装以来,情况可能已经发生了变化。我发现安装过程非常简单。您应该运行make world和make install,更新引导加载程序配置文件,就可以了——您已准备好启动进入 Xen。为了获得最佳效果,您应该遵循 用户手册 中的说明。
英特尔 x386 CPU 的保护模型由四个环构建而成:环 0 用于操作系统,环 3 用于用户应用程序。环 1 和环 2 在大多数情况下不使用,除非在 OS/2 等少数情况下使用;请参阅 IA-32 英特尔架构软件开发人员手册,第 1 卷:基本架构,第 4.5 节(特权级别)。
在 Xen 中,“hypervisor”在环 0 中运行,而客户操作系统在环 1 中运行,应用程序在环 3 中运行。x64/64 在这方面略有不同:客户内核和应用程序都在环 3 中运行(请参阅 OLS 2005 会议论文集中的 Xen 3.0 与虚拟化艺术,第 4.1 节)。
Xen 本身被称为 hypervisor,因为它在比其托管的客户操作系统的管理程序代码更高的特权级别上运行。
在启动时,Xen 被加载到环 0 的内存中。它在环 1 中启动一个打过补丁的内核;这称为域 0。从这个域中,您可以创建其他域、销毁它们、执行域的迁移、设置域的参数等等。您创建的域也在环 1 中运行其内核。用户应用程序在环 3 中运行。请参见图 1,其中说明了 Xen 中的 x86 保护环。

图 1
目前,域 0 可以是打过补丁的 2.4 或 2.6 Linux 内核。然而,根据 Xen 开发人员邮件列表,似乎在未来,域 0 将仅支持 2.6 内核补丁。构建 domain0 的大部分工作是在 xen/arch/x86/domain_build.c 中的 construct_dom0() 方法中完成的。
物理设备驱动程序仅在特权域(域 0)中运行。Xen 依靠 Linux 或另一个打过补丁的操作系统内核来提供几乎所有的设备支持。这样做的好处是使 Xen 开发团队无需编写自己的设备驱动程序。
在具有标记 TLB 的处理器上使用 Xen 可以提高性能。标记 TLB 能够将地址空间标识符 (ASID) 附加到 TLB 条目。使用此功能,当处理器在 hypervisor 和客户操作系统之间切换时,无需刷新 TLB,这降低了内存操作的成本。
一些制造商提供此标记 TLB 功能。例如,一份题为 "AMD64 虚拟化代号为 'Pacifica' 技术安全虚拟机架构参考手册" 的文档于 2005 年 5 月发布。根据该文档,此架构使用标记 TLB。
接下来是 Xend 和 XCS 层的概述。这些层是管理层,使用户能够管理和控制域和 Xen。接下来是关于域之间以及虚拟设备之间通信机制的讨论。Xen 项目源代码非常复杂,我希望这可以成为深入研究它的起点。
首先,什么是 Xend 守护程序?它是 Xen 控制器守护程序,意味着它处理创建新域、销毁现有域、迁移和许多其他域管理任务。其大部分活动都基于运行 HTTP 服务器。HTTP 套接字的默认端口是 8000,可以配置。控制域的各种请求通过发送 HTTP 请求来处理,用于域创建、域关闭、域保存和恢复、实时迁移等等。Xend 代码的大部分是用 Python 编写的,并且它还使用从 Python 脚本内部调用 C 方法。
我们在启动进入 Xen 后,通过从命令行运行来启动 Xend 守护程序,xend start。此命令究竟涉及什么?首先,Xend 需要 Python 2.3 来支持其日志记录功能。
Xend 守护程序的工作基于与 XCS 服务器(控制交换机)的交互。因此,当我们启动 Xend 守护程序时,我们会检查 XCS 是否已启动并正在运行。如果不是,我们会尝试启动 XCS。本文稍后将更全面地讨论此步骤。
实际上,SrvDaemon 是 Xend 主程序;启动 Xend 守护程序会创建 SrvDaemon 类的一个实例 (tools/python/xen/xend/server/SrvDaemon.py.)。此处创建了两个日志文件,/var/log/xend.log 和 /var/log/xend-debug.log。
接下来,我们在 createFactories() 方法中创建一个 Channel Factory。Channel Factory 内部嵌入了一个 notifier 对象。Xend 守护程序的大部分工作都基于此 notifier 接收到的消息。此工厂创建一个线程,该线程在一个无限循环中读取 notifier。notifier 将读取请求委托给 XCS 服务器;请参阅 xen/lowlevel/xu.c 中的 xu_notifier_read()。此方法通过调用 xcs_data_read() 将读取请求发送到 XCS 服务器。
域的创建是通过使用 hypercall (DOM0_CREATEDOMAIN) 完成的。什么是 hypercall?在 Linux 内核中,有一个系统调用,用户空间可以使用该调用来调用内核中的方法;这是通过中断 (Int 0x80) 完成的。在 Xen 中,类似的调用是 hypervisor 调用,域 0 通过该调用来调用 hypervisor 中的方法。这也是通过中断 (Int 0x82) 完成的。hypervisor 通过其虚拟 CPU(include/xen/sched.h 中的 struct vcpu)访问每个域。
XendDomain 类和 XendDomainInfo 类在创建和销毁域中起着重要作用。当我们创建一个新域时,会调用 XendDomain 类中的 domain_create() 方法;它启动域的创建过程。
XendDomainInfo 类及其方法负责域的实际构建。构建过程包括在新域中设置设备。这涉及域中的前端设备驱动程序和后端域中的后端设备驱动程序之间的大量消息传递。我们稍后会讨论后端和前端设备驱动程序。
XCS 服务器打开两个 TCP 套接字:控制连接和数据连接。控制连接和数据连接之间的区别在于,控制连接是同步的,而数据连接是异步的。例如,前面提到的 notifier 对象是 XCS 服务器的客户端。
与 XCS 服务器的连接由 connection_t 类型的对象表示。绑定连接后,它会被添加到连接列表 connection_list 中,该列表每五秒钟迭代一次,以查看是否有新的控制消息或数据消息到达。控制消息(可以是控制消息或数据消息)分别由 handle_control_message() 或 handle_data_message() 处理。
XendDomainInfo 中的 create() 方法启动一系列操作以创建域。首先创建域的虚拟设备。create() 方法调用 create_blkif() 来创建块设备接口 (blkif);即使 VM 不使用磁盘,这也是必须的。其他虚拟设备由 create_configured_devices() 创建,create_configured_devices() 最终调用 DevController 类(请参阅 controller.py)的 createDevice() 方法。此方法调用相应类的 newDevice() 方法。所有设备类都继承自 Dev,Dev 是表示附加到设备控制器的设备的抽象类。它的 attach() 抽象(空)方法在 Dev 类的每个子类中实现;此方法将设备附加到其前端和后端。图 2 显示了设备层次结构,图 3 显示了设备控制器层次结构。
域 0 运行后端驱动程序,新创建的域运行前端驱动程序。后端和前端驱动程序之间会传递大量消息。前端驱动程序是一种虚拟驱动程序,因为它不使用特定的硬件细节;代码位于稀疏树中的 drivers/xen 中。
事件通道和共享内存环是域之间通信的方式。例如,在网络前端设备 (netfront.c)(即网卡前端接口)的情况下,np->tx 和 np->rx 是共享内存页,一个用于接收器缓冲区,另一个用于发送缓冲区。在 send_interface_connect() 中,我们告诉网络后端启动接口。连接消息通过事件通道传递到后端 interface.c 的 netif_connect() 方法。netif_connect() 方法调用 get_vm_area(2*PAGE_SIZE, VM_IOREMAP))。get_vm_area() 方法在内核虚拟映射区域中搜索大小等于两页的区域。
在块设备前端接口 blkif 的情况下,blkif_connect() 也调用 get_vm_area()。然而,在这种情况下,它仅使用一页内存。
与虚拟设备关联的中断是虚拟中断。当您从 domainU 运行cat /proc/interrupts时,请查看编号高于 256 的中断;它们被标记为“Dynamic-irq”。
IRQ 如何重定向到客户操作系统?do_IRQ() 方法已更改为支持客户操作系统的 IRQ。如果 IRQ 用于客户操作系统,则此方法调用 __do_IRQ_guest(),xen/arch/x86/irq.c。__do_IRQ_guest() 使用事件通道机制将中断发送到客户操作系统,即 event_channel.c 中的 send_guest_pirq() 方法。
Rami Rosen 是以色列理工学院 Technion 的计算机科学毕业生,该学院位于海法。他为一家网络初创公司担任 Linux 内核程序员,可以通过 ramirose@gmail.com 与他联系。在他的业余时间,他喜欢跑步、解决隐秘难题,并说服和帮助他认识的每个人转移到这个出色的操作系统 Linux。