Understanding QEMU devices



以下是一些注释,可以帮助新手了解QEMU设备的实际情况:

使用QEMU,要记住的一件事是,我们正在尝试模拟操作系统(OS)在裸机硬件上将看到的内容。大多数裸机基本上都是巨型内存映射,其中在特定地址处拨入的软件将具有特定的副作用(当然,最常见的副作用是访问内存;但是内存中的其他常见区域包括用于控制的寄存器组)特定的硬件,例如硬盘驱动器或网卡,甚至CPU本身)。仿真的最终目标是允许用户空间程序仅使用普通的内存访问来管理GuestOS期望的所有副作用。

作为实现细节,某些硬件(例如x86)实际上具有两个内存空间,其中I / O空间使用的汇编代码与正常情况不同。 QEMU必须模拟这些替代访问。同样,许多现代CPU在内存映射内为自己提供一组CPU本地寄存器,例如用于中断控制器。

对于某些硬件,我们拥有虚拟化挂钩,CPU本身可以轻松捕获有问题的汇编指令(那些访问I / O空间或CPU内部寄存器的指令,因此所带来的副作用与常规内存访问不同),因此Guest仅执行与在裸机上相同的汇编序列,但是该执行随后导致陷阱,让用户空间QEMU然后仅使用其正常的用户空间内存访问对指令做出反应,然后再将控制权返回给Guest。 QEMU通过“加速器”对此提供支持。

虚拟化加速器(例如KVM)可以使Guest运行的速度几乎与裸机一样快,在这种情况下,速度减慢是由从Guest返回QEMU(vmexit)以处理困难的汇编指令或内存地址的每个陷阱引起的。 QEMU还支持其他虚拟化加速器(例如HAXM或macOS的Hypervisor.framework)。

QEMU还具有TCG加速器,该加速器接收Guest汇编指令并将其即时编译为可比的主机指令或对主机帮助程序的调用。尽管不如硬件加速那么快,但它允许跨硬件仿真,例如在x86上运行ARM代码。

接下来要实现的是操作系统正在访问各种硬件资源时发生的情况。例如,大多数操作系统附带一个知道如何管理IDE磁盘的驱动程序-该驱动程序仅是经过编程以对内存映射的特定子集发出特定I / O请求的软件(无论IDE总线位于何处,特定于硬件板)。当IDE控制器硬件接收到这些I / O请求时,它会执行适当的操作(通过DMA传输或其他硬件操作),以将数据从内存复制到持久性存储(写入磁盘)或从持久性存储复制到内存(从磁盘读取) )。

首次购买裸机硬件时,磁盘是未初始化的。您将安装使用驱动程序的OS,以对内存映射的IDE硬件部分进行足够的裸机访问,然后将磁盘变成一组分区和这些分区之上的文件系统。

那么,QEMU如何模拟这一点?在提供给client的大内存映射中,它模拟了与裸机相同地址的IDE磁盘。当GuestOS驱动程序发出特定的内存写入IDE控制寄存器以将数据从内存复制到持久性存储时,QEMU加速器会捕获对该内存区域的访问,并将请求传递给QEMU IDE控制器设备模型。然后,设备模型将解析I / O请求,并通过发出主机系统调用来模拟它们。结果是client内存被复制到主机存储中。

在主机端,模拟持久性存储的最简单方法是将主机文件系统中的文件视为原始数据(主机文件中的偏移量与Guest驱动程序访问的磁盘偏移量的1:1映射),但是QEMU实际上具有将很多不同的主机格式(原始,qcow2,qed,vhdx等)和协议(文件系统,块设备,NBD,Ceph,gluster等)粘合在一起的能力,这些格式可以使用主机格式和协议的任意组合作为后端,然后绑定到提供访客设备的QEMU仿真。

因此,当您告诉QEMU使用主机qcow2文件时,Guest虚拟机不必知道qcow2,而只需让其普通驱动程序进行与在裸机上相同的寄存器读写操作,这将导致vmexits进入QEMU代码,然后QEMU将这些访问映射到qcow2文件的适当偏移中的读取和写入。首次安装Guest系统时,Guest系统会看到的是空白的未初始化线性磁盘(无论该磁盘在主机中是线性磁盘(如原始格式),还是针对随机访问进行了优化(如qcow2格式);由Guest操作系统决定如何对硬件视图进行分区并在其上安装文件系统,而QEMU不在乎Guest使用的是什么文件系统,只关心原始磁盘I / O寄存器控制序列的模式是什么。

接下来要意识到的是,仿真IDE并不总是最有效的。Guest每次写入控制寄存器时,都必须经过特殊处理,vmexits会减慢仿真速度。当然,虚拟化时,不同的硬件模型具有不同的性能特征。但是,总的来说,最适合实际硬件的软件不一定适合虚拟化的软件,直到最近,当设计为由QEMU之类的软件进行仿真时,硬件仍无法快速运行。因此,QEMU包括专门为此目的设计的半虚拟化设备。

这里的“半虚拟化”的含义与最初的“通过Guest和主机之间的合作进行虚拟化”的含义略有不同。 QEMU开发人员已经制定了一组硬件寄存器的规范以及这些寄存器的行为,旨在使可能的vmexits数量最少,同时仍能完成硬盘必须完成的工作,即在普通Guest内存和硬盘之间传输数据。持久存储。此规范称为virtio;使用它需要在客户机中安装virtio驱动程序。虽然不存在遵循与virtio相同的寄存器布局的物理设备,但概念是相同的:virtio磁盘的行为就像一个内存映射的寄存器组,GuestOS驱动程序随后知道将什么顺序的寄存器命令写入该寄存器组,以导致将数据复制到其他Guest存储器中以及从其他Guest存储器中复制数据。 virtio中的大部分加速来自其设计-Guest将大部分常规内存留给其大部分命令队列使用,并且只需要踢一个寄存器即可告诉QEMU读取命令队列(映射寄存器访问较少)意味着更少的vmexits),再加上握手保证了客户机驱动程序在QEMU对其进行操作时不会更改普通内存。

顺便说一句,就像最近的硬件可以相当高效地进行仿真一样,virtio也在不断发展以更加高效地在硬件中实现,当然,同时又不牺牲仿真或虚拟化的性能。因此,将来,您也可能会偶然发现物理虚拟设备。

同样,许多操作系统都支持许多网卡,常见的示例是PCI总线上的e1000卡。在裸机上,操作系统将探测PCI空间,查看是否填充了带有e1000签名的寄存器组,然后加载驱动程序,该驱动程序随后知道要写入的寄存器序列,以使硬件卡将网络流量传入和传出。的客人。因此,作为许多网卡仿真之一,QEMU具有一个e1000设备,该设备被映射到与真实的裸机存储区相同的Guest存储区。

同样,e1000的寄存器布局往往需要大量的寄存器写操作(因此也需要vmexits)来完成硬件的工作量,因此QEMU开发人员添加了virtio-net卡(PCI硬件规范,尽管没有公开)。 -metal硬件,但实际上已经实现了),因此在GuestOS中安装virtio-net驱动程序可以最大程度地减少vmexits的数量,同时仍具有发送网络流量的相同副作用。如果您告诉QEMU使用virtio-net卡启动Guest虚拟机,则Guest虚拟机OS将探测PCI空间并看到带有virtio-net签名的寄存器组,并像加载其他任何PCI硬件一样加载适当的驱动程序。

总而言之,即使QEMU最初是作为模拟硬件内存映射以虚拟化GuestOS的一种方式而编写的,但事实证明,最快的虚拟化还取决于虚拟硬件:具有特定记录副作用的寄存器的内存映射具有 没有裸机。 归根结底,所有虚拟化实际上意味着运行一组特定的汇编指令(GuestOS)来操纵巨型内存映射中的位置,从而引起一组特定的副作用,其中QEMU只是一个用户空间。 该应用程序提供了一个内存映射,并模仿了您在适当的裸机硬件上执行这些Guest指令时将获得的相同副作用。

Tuesday, February 4, 2020 by blast