QEMU

来自 Arch Linux 中文维基

来自 QEMU 关于页面:“QEMU”是一个广泛使用的开源计算机模拟器和虚拟机。"

当作为架构模拟器时,可以在一种架构(如x86 PC)下运行另一种架构(如ARM)下的操作系统和程序。通过使用动态转换,它可以获得非常好的性能。

作为虚拟机时,QEMU可以使用其他虚拟机管理程序(如 XenKVM)来使用 CPU 扩展(HVM)进行虚拟化,通过在主机 CPU 上直接执行客户机代码来获得接近于宿主机的性能。

安装[编辑 | 编辑源代码]

安装 qemu-full,(或 qemu-base,一个无GUI支持的基本版本;qemu-desktop,默认仅支持 x86_64 模拟)

并根据需要安装下面的可选软件包:

除了这些之外, qemu-user-static 提供了 user-mode 下运行静态编译程序的支持。

QEMU 变种[编辑 | 编辑源代码]

QEMU提供了多个变种以供用户在不同场景中使用。

粗略地说,QEMU 有两种运行模式:

全系统模拟模式(full-system emulation)
在该模式下, QEMU 将会模拟一个完整的系统,包含一个或多个处理器以及各种外围设备。这种模式更加贴近真实的系统,且这种模式不要求被模拟的客户机系统是 Linux,但它的速度较慢。
QEMU 中启用 full-system 模式的命令依照如下规则进行命名 qemu-system-目标机器架构,例如 qemu-system-x86_64 用于模拟 x86_64 架构 CPU, qemu-system-i386 模拟 Intel 32位 x86 架构 CPU, qemu-system-arm 模拟 32 位 ARM 架构qemu-system-aarch64 模拟 64 位 ARM 架构,等等。
如果模拟的 CPU 架构与宿主机的 CPU 架构相同, 那么即使在此模式下,QEMU 仍有可能使用 hypervisor(例如KVM 或者 Xen)的技术对模拟机进行加速。
用户模式(Usermode emulation)
在此模式下, QEMU 能够利用宿主机的系统资源来调用为其他架构编译的 Linux 可执行文件。当然,里面依旧存在一些小问题, 比如说一些功能特性没有被实现, 采用动态链接的可执行文件无法直接在上面使用(参阅#从x86_64环境中 Chroot 至 arm/arm64 环境解决该问题)并且只支持 Linux 程序(尽管我们可以使用wine在 Linux 上运行 Windows 程序)。
QEMU 中启用用户模式的命令依照如下规则进行命名 qemu-目标机器架构, 例如 qemu-x86_64 用于模拟 64 位的 CPU。

QEMU 拥有动态链接和静态链接两个变种:

动态链接 (默认)
qemu-* 命令依赖于宿主机上的库文件, 因此整个程序体积较小。
静态链接
qemu-* 命令则可以在任何架构相同的 Linux 系统上使用。

在 Arch Linux 中,全系统模拟有两个变种:

有界面(Non-Headless)(默认):这个变种启用了一些 GUI 相关的特性,需要额外的依赖(例如 SDL 或 GTK)。
无界面(Headless):这个变种不需要 GUI 相关的依赖(适用于服务器场景)。

需要注意的是,这两个版本安装的执行文件名称是一样的(例如 qemu-system-x86_64),因此系统上不能同时安装这两个版本。

Arch Linux中相关安装包的详细信息[编辑 | 编辑源代码]

  • qemu-desktop 包提供了 x86_64 架构的模拟器, 可以进行全系统模拟 (qemu-system-x86_64)。 qemu-emulators-full包提供了 x86_64 用户模式的模拟 (qemu-x86_64)。 对于其他支持的架构,这个包都提供了全系统模拟和用户模拟两个变种 (比如说 qemu-system-armqemu-arm)。
  • 对应于这些包的无界面版本 (仅适用于全系统模拟模式) 分别是 qemu-base(仅x86_64) 和 qemu-emulators-full (其余架构)。
  • 可以用如下独立的安装包中的 QEMU 模块扩展全系统模拟的功能: qemu-block-gluster, qemu-block-iscsi, 和 qemu-guest-agent
  • qemu-user-static 为所有 QEMU 支持的架构提供了一个带用户模式和静态链接模式的变种。 它的QEMU命令依照 qemu-target_architecture-static的规则命名, 例如, qemu-x86_64-static 代表目标架构为intel 64位CPU。
注意: 目前为止,Arch Linux 无论是在官方仓库还是 AUR 仓库中都不提供全系统模式且使用静态链接的 QEMU 变种,原因是很少有人需要它们。

QEMU 的图形前端[编辑 | 编辑源代码]

与其他的虚拟化程序如 VirtualBoxVMware 不同, QEMU不提供管理虚拟机的GUI(运行虚拟机时出现的窗口除外),也不提供创建具有已保存设置的持久虚拟机的方法。除非您已创建自定义脚本以启动虚拟机,否则必须在每次启动时在命令行上指定运行虚拟机的所有参数。

Libvirt提供了一种管理 QEMU 虚拟机的便捷方式。有关可用的前端,请参阅 libvirt 客户端列表

创建新虚拟系统[编辑 | 编辑源代码]

创建硬盘镜像[编辑 | 编辑源代码]

本文或本章节的事实准确性存在争议。

原因: 如果我没有理解错手册页的话,原始格式只有在文件系统不支持“空洞”或者明确指定了预分配的情况下才会分配全部大小。参见 qemu-img(1) § NOTES。(在 [主站 Talk:QEMU] 中讨论)
提示:有关QEMU镜像的更多信息,请参阅 QEMU Wikibook

除非直接从 CD-ROM 或网络引导(并且不安装系统到本地),运行 QEMU 时都需要硬盘镜像。硬盘镜像是一个文件,存储虚拟机硬盘上的内容。

一个硬盘镜像可能是 raw镜像, 和客户机器上看到的内容一模一样,并且将始终使用主机上的来宾硬盘驱动器的全部容量。此方法提供的I / O开销最小,但可能会浪费大量空间,因为guest虚拟机上未使用的空间无法在主机上使用。

另外一种方式是qcow2 格式,仅当客户系统实际写入内容的时候,才会分配镜像空间。对客户机器来说,硬盘大小表现为完整大小,即使它可能仅占用主机系统上的非常小的空间。此映像格式还支持QEMU快照功能(有关详细信息,请参阅 #通过 monitor console 创建快照和管理快照)。但是,使用此格式而不是 raw 可能会影响性能。

QEMU 提供 qemu-img命令创建硬盘镜像.例如创建一个 4 GiB raw 格式的镜像:

$ qemu-img create -f raw image_file 4G

您也可以用 -f qcow2 创建一个 qcow2 镜像。

ddfallocate 也可以创建一个 raw 镜像。

注意: 您也可以通过 ddfallocate 创建一个所需大小的 raw 镜像。
警告: 如果硬盘镜像存储在 Btrfs 系统上,则应在创建任何映像之前考虑禁用该目录的 写时复制 功能。可以在创建镜像时为qcow2格式指定选项nocow:
$ qemu-img create -f qcow2 image_file -o nocow=on 4G

覆写(Overlay)镜像[编辑 | 编辑源代码]

你只需创建一个存储镜像(基础(backing)镜像),并让 QEMU 在这个基础镜像的覆写镜像中做出改变。这允许你恢复到这个存储镜像的先前状态。你可以通过在你想恢复的时候创建一个新的覆写镜像来进行恢复,这一切都是在基于原来的备份镜像中进行。

要创建一个覆写镜像,执行下面的命令。

$ qemu-img create -o backing_file=img1.raw,backing_fmt=raw -f qcow2 img1.cow

之后,你可以正常运行 QEMU 虚拟机(参阅 #运行虚拟化的系统):

$ qemu-system-i386 img1.cow

然后,基础镜像将被保留,对该存储的改变将被记录在覆写镜像中。

当基础镜像的路径改变时,需要进行修复。

警告: 基础镜像的绝对文件系统路径存储在(二进制的)覆写镜像文件中。若改变基础镜像的路径,可能需要做一些调整。

确保原来的基础镜像的路径仍然指向该镜像。如果有必要,在原路径上做一个符号链接到新路径上。然后执行如下命令:

$ qemu-img rebase -b /new/img1.raw /new/img1.cow

你也可以选择执行一个 “不安全 ”的重定向,即不检查基础镜像的原路径。

$ qemu-img rebase -u -b /new/img1.raw /new/img1.cow

调整镜像大小[编辑 | 编辑源代码]

警告: 调整包含NTFS引导文件系统的镜像将无法启动已安装的操作系统,推荐在操作之前进行备份

执行 qemu-imgresize 选项调整硬盘驱动镜像的大小.它适用于 rawqcow2. 例如, 增加镜像 10 GiB 大小, 运行:

$ qemu-img resize disk_image +10G

在磁盘映像扩容后,必须使用虚拟机内部系统的分区工具对该镜像进行分区并格式化后才能真正开始使用新空间。

Shrinking an image[编辑 | 编辑源代码]

When shrinking a disk image, you must first reduce the allocated file systems and partition sizes using the file system and partitioning tools inside the virtual machine and then shrink the disk image accordingly. For a Windows guest, this can be performed from the "create and format hard disk partitions" control panel.

警告: Proceeding to shrink the disk image without reducing the guest partition sizes will result in data loss.

Then, to decrease image space by 10 GiB, run:

$ qemu-img resize --shrink disk_image -10G

Converting an image[编辑 | 编辑源代码]

You can convert an image to other formats using qemu-img convert. This example shows how to convert a raw image to qcow2:

$ qemu-img convert -f raw -O qcow2 input.img output.qcow2

This will not remove the original input file.

准备安装介质[编辑 | 编辑源代码]

要将操作系统安装到您的磁盘镜像, 你需要操作系统的安装介质 (例如 光盘, USB设备, 或 ISO 镜像). 不要挂载安装介质,因为 QEMU 要直接访问媒体。

提示:如果使用光盘,最好先将媒体转储到文件中,因为这既可以提高性能,又不需要您直接访问设备(也就是说,您可以将QEMU作为普通用户,而无需更改对媒体设备文件的访问权限)。例如,如果CD-ROM设备节点名为/dev/cdrom,则可以使用以下命令将其转储到文件中:
$ dd if=/dev/cdrom of=cd_image.iso

安装操作系统[编辑 | 编辑源代码]

这是你第一次需要去启动模拟器的步骤,为了在磁盘镜像上安装操作系统,你必须同时将磁盘镜像与安装介质装载到虚拟机上,从安装介质中启动操作系统。

以i386的客户机为例,为了从CD-ROM内的把可用于启动的ISO文件安装到磁盘镜像上,你需要:

$ qemu-system-x86_64 -cdrom iso_image -boot order=d -drive file=disk_image,format=raw

参阅 qemu(1) 获得更多关于不同类型安装介质的信息 (例如floppy,磁盘镜像和物理驱动盘),参阅 #运行虚拟化的系统了解更多有用的选项。

在安装完操作系统后,就可以直接从QEMU镜像内启动了。(参阅 #运行虚拟化的系统

注意: 默认情况下仅分配给虚拟机128MiB的内存, 分配的内存大小可以通过 -m 调整, 比如 -m 512M-m 2G
提示:
  • 相较于指定 -boot order=x ,一部分用户感觉使用 -boot menu=on 启用boot菜单的体验更舒服些,至少在配置和实验时是这样的。
  • 当使用无界面(headless)模式时, 将会默认在本地5900端口启动一个VNC服务器, 可以用 TigerVNC 连接到客户机的系统上: vncviewer :5900
  • 若你在安装过程中需要替换软盘或CD,可以使用QEMU机器监视器(在虚拟机窗口中按Ctrl + Alt + 2)来删除存储设备并将其连接到虚拟机。使用 info block查看块设备,然后使用change命令换出设备。按下 Ctrl + Alt + 1返回虚拟机。

运行虚拟化的系统[编辑 | 编辑源代码]

qemu-system-* 程序 (例如 qemu-system-i386qemu-system-x86_64, 取决于客户机架构)用来运行虚拟化的客户机。用法是:

$ qemu-system-i386 options disk_image

所有 qemu-system-*的选项是相同的,参见 qemu(1) 查看文档和所有选项

一般来说,如果一个选项有多个可能的值,你可以使用

$ qemu-system-x86_64 option help

来列出所有可能的值。如果它支持属性,你可以使用

$ qemu-system-x86_64 option value,help

来列出所有可能的属性。

例如:

$ qemu-system-x86_64 -machine help
$ qemu-system-x86_64 -machine q35,help
$ qemu-system-x86_64 -device help
$ qemu-system-x86_64 -device qxl,help

你可以使用这些方法和 qemu(1) 的文档来理解接下来几个小节中的相关选项。

默认 QEMU 会在窗口中显示虚拟机的视频输出.有一点要记住:当您单击QEMU窗口,鼠标指针被捕获。要放开,按 Ctrl+Alt+g.

警告: QEMU 不应以 root 身份运行. 如果必须以 root 身份在某个脚本中运行 QEMU,那么你需要使用 -runas 选项让 QEMU 放弃root 权限

启用 KVM[编辑 | 编辑源代码]

KVM(基于内核的虚拟机)完全虚拟化必须要您的 Linux 内核和硬件支持,并且必须加载必要的内核模块。更多信息参见 KVM

要在 KVM 模式中启动 QEMU, 追加 -accel kvm 到启动选项。检查运行中的虚拟机是否启用了 KVM,进入 QEMU 监视器,输入 info kvm

注意:
  • -machine 选项中的 accel=kvm 参数与-enable-kvm-accel kvm 选项是等价的。
  • CPU模型 host 需要 KVM。
  • 如果你使用 GUI 工具去启动 QEMU,但是性能体验极差,那么最好检查一下是否真的开启了 KVM 支持,因为 QEMU 可能选择了备用的软件级模拟。
  • 需要启用 KVM 才能正常启动 Windows 7 和 Windows8,否则会出现“蓝屏”.

启用 IOMMU (Intel VT-d/AMD-Vi)的支持[编辑 | 编辑源代码]

首先,启用 IOMMU。参阅 PCI passthrough via OVMF#Setting up IOMMU.

添加 -device intel-iommu 选项创建 IOMMU 设备:

$ qemu-system-x86_64 -enable-kvm -machine q35 -device intel-iommu -cpu host ..
注意: 在基于 Intel CPU 的系统上,用 -device intel-iommu 创建 QEMU 内的 IOMMU 设备将会禁用 PCI 直通, 并返回一个像这样的错误报告:
Device at bus pcie.0 addr 09.0 requires iommu notifier which is currently not supported by intel-iommu emulation
虽然仍然需要添加内核参数 intel_iommu=on 来重新映射 IO(例如,通过 vfio-pci 的 PCI 直通),但如果需要 PCI 直通,则不应设置 -device intel-iommu

以 UEFI 模式启动[编辑 | 编辑源代码]

QEMU 使用的默认固件是 SeaBIOS,一个传统 BIOS 的实现。QEMU 使用 /usr/share/qemu/bios-256k.bin (由 seabios 提供) 作为默认的只读 (ROM) 镜像。你可以使用参数 -bios 来选择另一个固件文件。然而, UEFI 需要可写的内存来正常工作,所以你需要模拟 PC System Flash

OVMF 是一个为虚拟机提供 UEFI 支持的 TianoCore 项目。它可以通过 edk2-ovmf安装

使用 OVMF 作为固件有两种方法。第一种是复制 /usr/share/edk2/x64/OVMF.4m.fd 文件,使其可写并用作 pflash 驱动器:

-drive if=pflash,format=raw,file=/copy/of/OVMF.4m.fd

所有对 UEFI 设置的更改将直接保存到此文件中。

另一种更优选的方法是将 OVMF 分成两个文件。第一个文件将是只读的,并存储固件可执行文件,而第二个文件用作可写的变量存储区。这样做的好处是您可以直接使用固件文件,而无需复制,因此它会由 pacman 自动更新。

使用 /usr/share/edk2/x64/OVMF_CODE.4m.fd 作为第一个只读 pflash 驱动器。复制 /usr/share/edk2/x64/OVMF_VARS.4m.fd ,使其可写,并用作第二个可写 pflash 驱动器:

-drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
-drive if=pflash,format=raw,file=/copy/of/OVMF_VARS.4m.fd

如果需要启用安全启动,请使用q35机器类型,并将 /usr/share/edk2/x64/OVMF_CODE.4m.fd 替换为 /usr/share/edk2/x64/OVMF_CODE.secboot.fd

可信平台模块模拟[编辑 | 编辑源代码]

QEMU 可以模拟可信平台模块 (Trusted Platform Module) ,这是某些系统(例如需要 TPM 2.0 的 Windows 11)所必需的。

安装 swtpm ,来提供一个软件 TPM 实现。创建一个目录来存储 TPM 数据 (这里以 /path/to/mytpm 为例) 。 运行以下命令启动模拟器r:

$ swtpm socket --tpm2 --tpmstate dir=/path/to/mytpm --ctrl type=unixio,path=/path/to/mytpm/swtpm-sock

/path/to/mytpm/swtpm-sock 将由 swtpm 创建: 这是一个 UNIX 套接字,QEMU 将连接到此套接字。您可以将其放在任何目录中。

默认情况下, swtpm 启动一个 TPM 1.2 模拟器。 --tpm2 选项启用 TPM 2.0 模拟。

最后,将以下选项添加到 QEMU:

-chardev socket,id=chrtpm,path=/path/to/mytpm/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0

然后 TPM 将在虚拟机内可用。关闭虚拟机后, swtpm 将自动终止。

有关更多信息,请参见 the QEMU documentation

如果虚拟机操作系统仍然无法识别 TPM 设备,请尝试调整可能会导致该问题的 CPU 模型和拓扑选项。

宿主机和虚拟机数据交互[编辑 | 编辑源代码]

网络[编辑 | 编辑源代码]

我们可以利用任何支持文件传输的网络协议实现客户机和宿主机之间的数据交互, 例如 NFS, SMB, NBD, HTTP, FTP, 或 SSH, 当然这么做的前提是你已经配置好二者之间的网络,且在系统上启动了相应的服务程序。

在默认情况下,用户模式的客户机能够通过 10.0.2.2 这个IP访问到宿主机。任何运行于宿主机上的服务端程序都可以通过这个地址被访问到,比如说我们可以通过这个IP访问到宿主机上的 SSH 服务器或 SMB 服务器。因此在这种情况下,客户机能够挂载宿主机通过 SMBNFS 暴露出来的目录,也可以访问宿主机上的 HTTP 服务器等。通常情况下宿主机无法访问客户机上的服务,不过你也可以通过一些特殊的网络配置达到这个目的 (参阅#Tap 网络)

QEMU 端口转发[编辑 | 编辑源代码]

注意: QEMU's port forwarding is IPv4-only. IPv6 port forwarding is not implemented and the last patches were proposed in 2018.[1]

QEMU 能够将宿主机的端口转发到客户机上以实现一些功能,例如从宿主机上访问客户机的 SSH 端口。

举个例子,将宿主机上的 60022 端口与客户机上的22 (SSH) 端口进行绑定,需通过如下命令启动 QEMU:

$ qemu-system-x86_64 disk_image -nic user,hostfwd=tcp::60022-:22

确认你客户机上的 sshd 程序正在运行,然后可以通过如下命令连接到客户机的 SSH 端口:

$ ssh guest-user@127.0.0.1 -p 60022

你可以用 SSHFS 把客户机的整个文件系统都挂到宿主机上,这样就可以在宿主机上对客户机的文件系统进行读写了。

想进行多端口转发的话, 只需要在 -nic 参数中指定多个 hostfwd, 以 VNC 端口为例:

$ qemu-system-x86_64 disk_image -nic user,hostfwd=tcp::60022-:22,hostfwd=tcp::5900-:5900

QEMU 的内置SMB服务器[编辑 | 编辑源代码]

QEMU的文档中指出它有一个内置的 SMB 服务器,但实际上,它只是在宿主机上加载一个自动生成的 smb.conf 配置文件 (位于/tmp/qemu-smb.random_string),然后启动宿主机上的 Samba,使得客户机能够通过一个IP地址进行访问 (默认的IP地址是10.0.2.4)。这个方法只适用于用户网络,在你不想在宿主机开启通常的 Samba 服务 (客户机同样能访问这类Samba服务) 时这个方法还挺好用的。

选项 smb= 仅可以设置共享一个目录,如果 QEMU 的 SMB 配置允许用户使用符号链接,那么即使在虚拟机运行时也很容易新加入更多的目录,只需要通过在共享目录里创建相应的软链接就行。然而就算并没有这么配置,我们可以依照如下进行配置 SMB 服务器

宿主机上必须安装 Samba。通过如下QEMU命令启用这项特性:

$ qemu-system-x86_64 -nic user,id=nic0,smb=shared_dir_path disk_image

shared_dir_path 就是你想要在宿主机和客户机之间共享的目录。

接着,在客户机内,你应该能够通过 10.0.2.4 访问到名为 qemu 的共享文件夹,例如可以在 Windows 文件浏览器中前往 \\10.0.2.4\qemu 这个地址。

注意:
  • 如果你像这样多次指定共享选项:-net user,smb=shared_dir_path1 -net user,smb=shared_dir_path2 或是 -net user,smb=shared_dir_path1,smb=shared_dir_path2,qemu 只会共享参数中最后的一个目录。
  • 如果你不能访问共享文件夹且客户机系统为 Windows, 请检查 NetBIOS 协议是否被启用[失效链接 2023-05-06 ⓘ] 并确认防火墙没有屏蔽 NetBIOS 协议的 端口
  • 如果你无法访问共享文件夹且客户机系统为 Windows 10 企业版/教育版或 Windows Server 2016, 请启用游客访问.

共享多个文件夹并在运行时增删文件夹的一个方法是:共享一个空目录,然后在其中创建指向其余共享目录的符号链接。可以用下面的脚本修改SMB服务器的配置,这个脚本还能使宿主机上不允许执行的文件在客户机内拥有执行权限。

#!/bin/sh
eval $(ps h -C smbd -o pid,args | grep /tmp/qemu-smb | gawk '{print "pid="$1";conf="$6}')
echo "[global]
allow insecure wide links = yes
[qemu]
follow symlinks = yes
wide links = yes
acl allow execute always = yes" >> "$conf"
# in case the change is not detected automatically:
smbcontrol --configfile="$conf" "$pid" reload-config

仅当 qemu 启动的客户机第一次访问到网络磁盘后,才能应用该脚本。共享多文件夹的另一个方法是在配置文件里加入额外的共享路径,就像下面这样

echo "[myshare]
path=another_path
read only=no
guest ok=yes
force user=username" >> $conf

这个共享文件夹可以在客户机内通过 \\10.0.2.4\myshare 访问。

使用文件系统直通和 VirtFS[编辑 | 编辑源代码]

参阅 QEMU 文档.

Host file sharing with virtiofsd[编辑 | 编辑源代码]

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

virtiofsd 提供了 virtiofsd 工具。有关可用选项的完整列表,请参阅在线文档qemu-docs 内置的 /usr/share/doc/virtiofsd/README.md 文件。

由于 qemu 程序需要访问 virtiofsd 套接字,因此请将运行 qemu 的用户添加到 'kvm' 用户组。你可能需要重新登录才能使更改生效。

本文或本章节的事实准确性存在争议。

原因: Running services as root is not secure. Also the process should be wrapped in a systemd service.(在 Talk:QEMU 中讨论)


Start as virtiofsd as root:

# /usr/lib/virtiofsd --socket-path=/var/run/qemu-vm-001.sock --shared-dir /tmp/vm-001 --cache always

where

  • /var/run/qemu-vm-001.sock is a socket file,
  • /tmp/vm-001 is a shared directory between the host and the guest virtual machine.

The created socket file has root only access permission. Give group kvm access to it with:

# chgrp kvm qemu-vm-001.sock; chmod g+rxw qemu-vm-001.sock

Add the following configuration options when starting the virtual machine:

-object memory-backend-memfd,id=mem,size=4G,share=on \
-numa node,memdev=mem \
-chardev socket,id=char0,path=/var/run/qemu-vm-001.sock \
-device vhost-user-fs-pci,chardev=char0,tag=myfs

where

这篇文章的某些内容需要扩充。

原因: Explain the remaining options (or remove them if they are not necessary). (在 Talk:QEMU 中讨论)
  • size=4G shall match size specified with -m 4G option,
  • /var/run/qemu-vm-001.sock points to socket file started earlier,

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:The section should not be specific to Windows.(在Talk:QEMU讨论)

Remember, that guest must be configured to enable sharing. For Windows there are instructions. Once configured, Windows will have the Z: drive mapped automatically with shared directory content.

Your Windows 10 guest system is properly configured if it has:

  • VirtioFSSService windows service,
  • WinFsp.Launcher windows service,
  • VirtIO FS Device driver under "System devices" in Windows "Device Manager".

If the above installed and Z: drive is still not listed, try repairing "Virtio-win-guest-tools" in Windows Add/Remove programs.

在宿主机上挂载客户机的分区[编辑 | 编辑源代码]

在主机系统上挂载驱动器镜像可能很有用,它可以作为在虚拟机和主机之间传输文件的一种方式。这应在虚拟机未运行时进行。

挂载驱动器的过程取决于 qemu 镜像的类型 (rawqcow2) 。我们将在 #挂载 raw 镜像内的分区#挂载 qcow2 镜像内的分区中详细介绍两种格式的挂载步骤。完整文档请参见Wikibooks:QEMU/Images#Mounting an image on the host

警告: 请确保重新运行虚拟机之前,所有挂载的分区都被卸载,否则很可能造成磁盘数据的损坏。

挂载 raw 镜像内的分区[编辑 | 编辑源代码]

可以通过将 raw 磁盘镜像文件内的分区设置为环回设备来挂载这些分区。

手动指出偏移量[编辑 | 编辑源代码]

挂载磁盘镜像中分区的一个方法是手动指定挂载的偏移量,你可以使用类似下面的命令完成这个操作:

# mount -o loop,offset=32256 disk_image mountpoint

offset=32256 选项实际上会被传递给 losetup 程序,用于设置一个起始地址为 32256 字节处的 loop 设备。接着这个 loop 设备将会被挂载。 你也可以使用 sizelimit 选项指定这个分区的具体大小,通常不需要指定该选项。

具体的偏移量取决于你的磁盘镜像,你所需要的分区可能并不以 32256 字节处作为起始地址。运行 fdisk -l disk_image 查看磁盘镜像中的分区,fdisk 会显示分区的起始地址和结束地址,地址以 512 字节的扇区为单位,因此需要将该地址乘以 512 获得可用于 mount 的字节偏移量。

loop模块自动检测分区[编辑 | 编辑源代码]

Linux 的 loop 驱动支持 loopback 设备的分区,不过默认情况下它是关闭的,可以通过下面的方法启用:

  • 卸载所有 loopback 设备 (比如说卸载所有挂载的镜像)。
  • 卸下 loop 内核模块, 接着以 max_part=15 参数重新加载该模块。 此外,你可以用 max_loop 参数设置 loop 设备的最大数量。
提示:你可以在 /etc/modprobe.d 增加一个条目使得每次加载 loop 模块时带上 max_part=15 参数, 或者把 loop.max_part=15 加入内核命令行, 这得看你的内核是否带有 loop.ko 模块。

将镜像文件设置为 loopback 设备:

# losetup -f -P disk_image

接着,假设创建的loop设备名称为 /dev/loop0 ,相应的 /dev/loop0pX 也会被自动创建, X 代表分区的编号,这些分区可以被直接挂载,例如:

# mount /dev/loop0p1 mountpoint

如要使用 udisksctl 挂载磁盘镜像, 参阅 Udisks#Mount loop devices.

使用 kpartx[编辑 | 编辑源代码]

multipath-tools 包内的 kpartx 可以读取设备的分区表,然后为每个分区创建一个新设备,举个例子:

# kpartx -a disk_image

这条命令将会为你设置 loopback 设备,并在 /dev/mapper/ 下创建必要的分区设备。

挂载 qcow2 镜像内的分区[编辑 | 编辑源代码]

我们将使用 qemu-nbd 完成这一功能, 同时它也能让我们使用 NBD (network block device) 协议共享该磁盘镜像。

首先,我们需要加载 nbd 模块:

# modprobe nbd max_part=16

接着,共享该磁盘并创建设备条目:

# qemu-nbd -c /dev/nbd0 /path/to/image.qcow2

进行分区发现检测:

# partprobe /dev/nbd0

fdisk 可以获取 nbd0 内各分区的相关信息:

# fdisk -l /dev/nbd0
Disk /dev/nbd0: 25.2 GiB, 27074281472 bytes, 52879456 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xa6a4d542

Device      Boot   Start      End  Sectors  Size Id Type
/dev/nbd0p1 *       2048  1026047  1024000  500M  7 HPFS/NTFS/exFAT
/dev/nbd0p2      1026048 52877311 51851264 24.7G  7 HPFS/NTFS/exFAT

接下来可以挂载镜像的任意分区了,比如说我们要挂载分区2:

# mount /dev/nbd0p2 mountpoint

完成任务后,切记卸载镜像文件,然后根据之前的操作一步步还原,即分区并断开与 nbd 设备的连接:

# umount mountpoint
# qemu-nbd -d /dev/nbd0

将任意分区作为磁盘镜像唯一主分区[编辑 | 编辑源代码]

有时,你可能想在 QEMU 中使用物理机的一个系统分区。在虚拟机里使用原始分区可以改善读写性能,因为此时 QEMU 不需要经过宿主机的文件系统层完成读写操作,该分区同样可以用于在宿主机和客户机之间进行数据共享。

在 Arch Linux 的默认设置中,代表原始分区的设备文件的所有者为 root , 隶属于 disk 组。如果你希望使用一个非 root 用户能够对原始分区进行读写,那么请更改对应设备文件的所有者为该用户,将这个用户加入 disk 组中,或者使用 ACL 完成更精细的权限管理。

警告:
  • 不推荐让虚拟机修改宿主机系统上重要数据,比如说对 root 分区进行修改,尽管你拥有这么做的能力。
  • 不要同时挂载一块在宿主机和客户机内都可读写的分区,否则会造成数据的损坏。

在完成上述设置后,你可以将该分区作为一块虚拟磁盘添加到 QEMU 虚拟机内了。

然而,如果你想使得整个虚拟机都包含在单个分区中,事情就开始变得有些复杂了。在这个场景下,由于系统分区本身是作为文件系统完成格式化的,而非一个带有 MBR 的分区设备,我们便无法在这个分区上安装引导加载程序,进而也就没有办法启动虚拟机了。要启动这类虚拟机,你有如下几种选择:#手动指定 kernel 和 initrd#模拟一块带 MBR 的磁盘#使用设备映射器#使用线性 RAID或是#使用网络块设备

手动指定 kernel 和 initrd[编辑 | 编辑源代码]

QEMU 支持直接加载 Linux 内核init ramdisks,从而绕过了类似 GRUB 这类的引导加载程序。它会把包含根文件系统的物理分区作为虚拟机的虚拟磁盘,然后启动虚拟机。通过类似下面的命令可以完成这些操作:

注意: 在示例中, 我们使用的是 宿主机的 镜像,而非客户机的。如果你希望使用客户机的镜像,请将 /dev/sda3 设置为只读(为了保护宿主机上的文件系统),并且指定 /full/path/to/images ,或者在客户机内使用一些 kexec 的技巧,重新加载客户机的内核(会延长启动时间)
$ qemu-system-x86_64 -kernel /boot/vmlinuz-linux -initrd /boot/initramfs-linux.img -append root=/dev/sda /dev/sda3

在上面这个例子中,宿主机上的 /dev/sda3 物理分区被用于客户机的根文件系统,但是在虚拟机内部它的名字则是 /dev/sda

当然你可以指定任意一个 kernel 和 initrd,而不局限于Arch Linux提供的。

-append 中可以传递许多内核参数, 需要用单引号或双引号将他们包起来,比如:

... -append 'root=/dev/sda1 console=ttyS0'

模拟一块带 MBR 的磁盘[编辑 | 编辑源代码]

当我们想要既保持物理分区遵循文件系统的格式,又作为客户机的虚拟磁盘包含客户机内的分区时,我们有一个比较复杂的方法,那就是模拟一块带 MBR 的磁盘,这样就使得它可以被类似 GRUB 的引导程序正常启动。

假设你有一块普通的、未挂载的硬盘分区 /dev/hdaN ,分区内有些文件系统,若想将其制作为QEMU硬盘镜像,需要在该物理分区内动态地预置一个主引导记录(MBR)。更宽泛地说,该分区可以是更大的模拟磁盘中的一部分,尤其是那种模拟了物理磁盘,但仅向虚拟机暴露 /dev/hdaN 名称的块设备。

这类虚拟磁盘可以用带引用的 VMDK 文件形容,VMDK内的引用指向 MBR 的副本以及实际的分区。 但 QEMU 并不支持这类VMDK格式,例如通过如下命令创建的虚拟机:

$ VBoxManage internalcommands createrawvmdk -filename /path/to/file.vmdk -rawdisk /dev/hda

将会被QEMU拒绝,并返回一个错误信息

Unsupported image type 'partitionedDevice'

注意 VBoxManage 会创建两个文件:file.vmdkfile-pt.vmdk, 后一个是 file.vmdk MBR 的副本。对目标分区外的数据或MBR本身的读操作将会返回0, 而写操作则会被丢弃。

使用设备映射器[编辑 | 编辑源代码]

与使用 VMDK 描述文件类似的方法就是使用设备映射器,设备映射器会在目标分区内添加一份带有 loop 设备的 MBR 文件。在此种情况下,不要求虚拟磁盘与原始分区拥有同样的大小。首先创建一个包含 MBR 的文件:

$ dd if=/dev/zero of=/path/to/mbr count=2048

在这里,我们通过这个命令,使用现代化的磁盘分区程序创建了一个符合分区对齐策略的文件,总共大小为 1MiB(2048 * 512 字节)。为了兼容老式的磁盘分区程序,可能要求其为 63 个扇区而非 2048 个扇区。MBR 表仅占一块512字节的块,剩余的空间可以用于创建一个 BIOS 启动分区,或者在创建一个在混合分区方案中需要用到的 GUID 分区表。接着我们将 loop 设备附加到 MBR 文件上:

# losetup --show -f /path/to/mbr
/dev/loop0

在这个例子里,最终生成的设备为 /dev/loop0,设备映射器将会把 MBR 文件与分区结合起来:

# echo "0 2048 linear /dev/loop0 0
2048 `blockdev --getsz /dev/hdaN` linear /dev/hdaN 0" | dmsetup create qemu

生成的 /dev/mapper/qemu 可以被作为 QEMU 原生镜像使用了,在虚拟磁盘上创建一个分区表(可参阅关于线性 RAID 的部分),以及相应的引导加载器代码(代码会存放在 /path/to/mbr)需要额外的步骤。

下面的步骤中,/dev/hdaN 在虚拟磁盘和物理磁盘上的位置都相同,并且除了 MBR(副本)外磁盘的其余部分都是隐藏的:

# dd if=/dev/hda count=1 of=/path/to/mbr
# loop=`losetup --show -f /path/to/mbr`
# start=`blockdev --report /dev/hdaN | tail -1 | awk '{print $5}'`
# size=`blockdev --getsz /dev/hdaN`
# disksize=`blockdev --getsz /dev/hda`
# echo "0 1 linear $loop 0
1 $((start-1)) zero
$start $size linear /dev/hdaN 0
$((start+size)) $((disksize-start-size)) zero" | dmsetup create qemu

通过管道传入作为 dmsetup 标准输入的是一张表,该表的格式与 VBoxManage 创建的VMDK描述文件中的表格式相同, 即我们同样能通过 dmsetup create qemu --table table_file 加载VMDK描述文件中的表。 对于虚拟机来说, 仅 /dev/hdaN 能被访问, 而对其余部分, 除了第一个扇区外,对其他扇区的读操作都只会返回0, 写操作都会被丢弃。 可以用 dmsetup table qemu 显示 /dev/mapper/qemu 的表(用 udevadm info -rq name /sys/dev/block/major:minormajor:minor 转化为类似 /dev/blockdevice 的名字)。若要删除创建的设备,请使用 dmsetup remove qemulosetup -d $loop

设备映射器方法一个可能的应用场景与Windows XP有关,比如说要在Windows XP安装中进行多引导配置,并且有可能要采用混合分区方案(从物理硬件上说,Windows XP可能是唯一使用MBR分区表的系统,更现代化的操作系统能使用GUID分区表)。Windows XP支持硬件配置文件,因此同样的安装可以选择不同的硬件配置完成(在本例中是裸机与虚拟机),而Windows仅需为每个配置文件安装一次新检测到的硬件的驱动即可。注意在这个例子里, 要更新MBR副本中bootloader代码的部分,使得它直接从 /dev/hdaN 加载Windows XP而不是系统内自带的多引导bootloader(比如GRUB)。或者,将包含bootloader安装的启动分区之副本包含于虚拟磁盘中,同样也可起到类似MBR的作用。

使用线性 RAID[编辑 | 编辑源代码]

本文内容或本节内容已经过期。

原因: CONFIG_MD_LINEAR Removal Linear RAID has been deprecated since 2021 and removed on Kernel Version 6.8. (在Talk:QEMU讨论)

你同样能用 线性模式(需要 linear.ko 内核驱动)的RAID以及一块loop设备完成这件事。

首先创建一个用于容纳MBR的小文件:

$ dd if=/dev/zero of=/path/to/mbr count=32

这样就创建了一个 16 KiB(32 * 512字节)的小文件。该文件最好不要太小(尽管 MBR 只需要一个512字节大小的块),过小的话将会限制软 RAID 设备的 chunk 大小,对于性能是有影响的。将 loop 设备设置到 MBR 文件上。

# losetup -f /path/to/mbr

因为我们尚无法使用其他 loop 设备, 因此就先假设产生的设备名为 /dev/loop0。下一步将创建合并式 MBR + 使用软件 RAID 的磁盘镜像 /dev/hdaN

# modprobe linear
# mdadm --build --verbose /dev/md0 --chunk=16 --level=linear --raid-devices=2 /dev/loop0 /dev/hdaN

这一步产生的 /dev/md0 设备将在之后作为一个 QEMU 原始镜像(别忘了给模拟器相应的访问权限)。最后一步(也是比较取巧的一步)是设置磁盘配置,使得 MBR 内主分区的起始地址与 /dev/md0 中某一块 /dev/hdaN 的起始地址(在这个例子中是 16 * 512 = 16384 字节的偏移处)。请在宿主机上用 fdisk 完成该操作,别在虚拟机里面这么做,因为默认的 QEMU 内的磁盘检测程序常给出无法进行千位舍入的偏移量(比如 31.5 KiB),软件RAID无法处理这样的情况。因此请在主机上进行下面的操作:

# fdisk /dev/md0

按下 X 进入专家选单, 设置每个磁道上 's'ectors 的数目,使得一个柱面的大小与MBR文件中相符。对于双磁头柱面,且每个扇区为 512 字节的情况, 每个磁道上的扇区数当为 16, 因此我们通过计算 2x16x512=16k 得到柱面的大小。

按下 R 返回主界面。

按下 P 并检查柱面大小为 16k。

现在,来创建与 /dev/hdaN 对应的主分区。 设置它的起始地址为柱面2, 结束地址在磁盘的末端(注意现在的柱面编号已经与进入 fdisk 时的编号不同了)。

最后,把结果写入:完成~ 现在你拥有了一块可以直接从宿主机进行挂载的分区,该分区同样也是 QEMU 磁盘镜像的一部分。

$ qemu-system-x86_64 -hdc /dev/md0 [...]

现在,若原始的 /dev/hdaN 分区中包含必要的一些工具,你就可以安全地用 QEMU 在磁盘镜像上设置任何一个引导加载程序。

使用网络块设备[编辑 | 编辑源代码]

With Network Block Device, Linux can use a remote server as one of its block device. 你也可以用 nbd-server(在 nbd 包中)为 QEMU 创建一个 MBR 封装器。

假设你已经用上面所说的方法创建了一个方法创建了一个MBR封装文件,将其重命名为 wrapper.img.0。然后在同一个目录下创建一个名为 wrapper.img.1的符号链接,指向你选择的分区。然后,还是在这个文件夹下,创建如下的脚本:

#!/bin/sh
dir="$(realpath "$(dirname "$0")")"
cat >wrapper.conf <<EOF
[generic]
allowlist = true
listenaddr = 127.713705
port = 10809

[wrap]
exportname = $dir/wrapper.img
multifile = true
EOF

nbd-server \
    -C wrapper.conf \
    -p wrapper.pid \
    "$@"

.0 and .1 这两个后缀名是关键,名字的其他其他部分都可以更改。在运行上面的脚本(有可能需要以root身份运行,保证nbd-server能够去访问该分区)后,你可以用如下命令启动QEMU:

qemu-system-x86_64 -drive file=nbd:127.713705:10809:exportname=wrap [...]

Using an entire physical disk device inside the virtual machine[编辑 | 编辑源代码]

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:Duplicates #Using any real partition as the single primary partition of a hard disk image, libvirt instructions do not belong to this page.(在Talk:QEMU讨论)

You may have a second disk with a different OS (like Windows) on it and may want to gain the ability to also boot it inside a virtual machine. Since the disk access is raw, the disk will perform quite well inside the virtual machine.

Windows virtual machine boot prerequisites[编辑 | 编辑源代码]

Be sure to install the virtio drivers inside the OS on that disk before trying to boot it in the virtual machine. For Win 7 use version 0.1.173-4. Some singular drivers from newer virtio builds may be used on Win 7 but you will have to install them manually via device manager. For Win 10 you can use the latest virtio build.

Set up the windows disk interface drivers[编辑 | 编辑源代码]

You may get a 0x0000007B bluescreen when trying to boot the virtual machine. This means Windows can not access the drive during the early boot stage because the disk interface driver it would need for that is not loaded / is set to start manually.

The solution is to enable these drivers to start at boot.

In HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services, find the folders aliide, amdide, atapi, cmdide, iastor (may not exist), iastorV, intelide, LSI_SAS, msahci, pciide and viaide. Inside each of those, set all their "start" values to 0 in order to enable them at boot. If your drive is a PCIe NVMe drive, also enable that driver (should it exist).

Find the unique path of your disk[编辑 | 编辑源代码]

Run ls /dev/disk/by-id/: tere you pick out the ID of the drive you want to insert into the virtual machine, for example ata-TS512GMTS930L_C199211383. Now add that ID to /dev/disk/by-id/ so you get /dev/disk/by-id/ata-TS512GMTS930L_C199211383. That is the unique path to that disk.

Add the disk in QEMU CLI[编辑 | 编辑源代码]

In QEMU CLI that would probably be:

-drive file=/dev/disk/by-id/ata-TS512GMTS930L_C199211383,format=raw,media=disk

Just modify file= to be the unique path of your drive.

Add the disk in libvirt[编辑 | 编辑源代码]

In libvirt XML that translates to

$ virsh edit vmname
...
    <disk type="block" device="disk">
      <driver name="qemu" type="raw" cache="none" io="native"/>
      <source dev="/dev/disk/by-id/ata-TS512GMTS930L_C199211383"/>
      <target dev="sda" bus="sata"/>
      <address type="drive" controller="0" bus="0" target="0" unit="0"/>
    </disk>
...

Just modify "source dev" to be the unique path of your drive.

Add the disk in virt-manager[编辑 | 编辑源代码]

When creating a virtual machine, select "import existing drive" and just paste that unique path. If you already have the virtual machine, add a device, storage, then select or create custom storage. Now paste the unique path.

网络[编辑 | 编辑源代码]

警告: 本部分在翻译过程中,英文wiki产生style告警,这代表日后本节的内容将会有相应调整,暂停本节的翻译: 网络拓扑 (#仅主机 网络, #内部网络 节中以及其他节中引用的部分) 不应当根据不同的虚拟网络接口实现进行划分和描述, 例如#用户模式, #Tap 网络, #通过 VDE2 配置网络

采用TAP设备和网桥的虚拟网络的性能应该会比使用用户模式网络或VDE要好,原因在于TAP设备和网桥是在内核中实现的。

此外,虚拟网络的性能可以通过将 virtio 网络设备直接注册到虚拟机中改善,这比默认情况下模拟 e1000 NIC的性能表现要更好,参阅 #安装 virtio 驱动获得更多相关信息。

关于链路层地址的限制[编辑 | 编辑源代码]

若在QEMU启动中指定了 -net nic 参数,QEMU将会为虚拟机注册一块虚拟网卡,其链路层地址为 52:54:00:12:34:56 。然而,当在多台虚拟机之间搭建桥接网络时,每台虚拟机在tap设备的虚拟机端都需要拥有一个独一无二的链路层地址 (MAC),否则网桥会因为收到多个不同源却拥有相同MAC地址的数据包而无法正常工作。即使你为多个tap设备配置了不同的MAC地址也依旧会出现这个问题,因为当数据包通过tap设备时,tap设备并不会改写包内的链路层地址。

因此请确保每个虚拟机拥有自己独一无二的网卡地址, 并且它们都以 52:54: 开头。 可以通过如下命令手动设置虚拟机的MAC地址, 下面的'X'可以替换成任何16进制字符:

$ qemu-system-x86_64 -net nic,macaddr=52:54:XX:XX:XX:XX -net vde disk_image

生成不同的链路层地址有很多方法:

  • 手动为每个NIC设置独一无二的链路层地址,这么做的优点在于每次启动虚拟机时,DHCP服务器都会将相同的IP地址分配给对应的MAC地址,但是这个方法在需要大量虚拟机的情况下就不适用了。
  • 在每次启动虚拟机时随机生成链路层地址,实际情况下地址冲突的概率可视为0, 不过此方法的缺点在于DHCP服务器每次都会分配一个不同的IP地址。你可以用如下的这些脚本生成随机的链路层地址,并用于 macaddr 参数中。
printf -v macaddr "52:54:%02x:%02x:%02x:%02x" $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff )) $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff ))
qemu-system-x86_64 -net nic,macaddr="$macaddr" -net vde disk_image
  • 使用这个脚本 qemu-mac-hasher.py 可以根据虚拟机的名字进行Hash得到一个链路层地址。 只要每台虚拟机的名字是独一无二的, 这个方法就结合了上述两种方法的优点: 每次运行脚本生成的链路层地址都是相同的, 且冲突概率在实际应用中仍可视为0.
qemu-mac-hasher.py
#!/usr/bin/env python
# usage: qemu-mac-hasher.py <VMName>

import sys
import zlib

crc = str(hex(zlib.crc32(sys.argv[1].encode("utf-8")))).replace("x", "")[-8:]
print("52:54:%s%s:%s%s:%s%s:%s%s" % tuple(crc))

如果要在脚本调用这个方法,你可以参照下面的例子:

vm_name="VM Name"
qemu-system-x86_64 -name "$vm_name" -net nic,macaddr=$(qemu-mac-hasher.py "$vm_name") -net vde disk_image

用户模式[编辑 | 编辑源代码]

默认情况下,没有任何-netdev参数,QEMU将使用带有内置DHCP服务器的用户模式网络。当您的虚拟机运行其DHCP客户端时,将为其分配IP地址,它们将能够通过QEMU伪装的IP来访问物理主机的网络。

注意: ICMPv6 尚未完成实现,因此不会起效:Slirp: external icmpv6 not supported yet. Pinging an IPv6 address will not work.

如果主机已连接Internet,则此默认配置可以使您的虚拟机轻松访问Internet。但是如果您同时启动多个虚拟机,则虚拟机将无法在外部网络上直接看到,虚拟机也将无法相互通信。

QEMU的用户模式网络可以提供更多功能,例如内置TFTP或SMB服务器,将主机端口重定向到虚拟机(例如,允许SSH连接到虚拟机)或将虚拟机连接到VLAN,以便它们可以彼此通信。 有关更多详细信息,请参见-net user标志上的QEMU文档。

但是,用户模式网络在效用和性能上都有局限性。更高级的网络配置需要使用TAP设备或其他方法。

注意: 如果主机系统使用systemd-networkd,请确保按照systemd-networkd#Required services and setup中的描述对/etc/resolv.conf文件进行符号链接,否则虚拟机系统中的DNS查找将无法进行。
提示:
  • 使用此选项,虚拟机将使用 virtio 网络驱动程序来进行用户模式网络: -nic user,model=virtio-net-pci.
  • 使用此选项,用户模式网络将被隔离,与主机和外部网络隔离开来:-net user,restrict=y.

Tap 网络[编辑 | 编辑源代码]

Tap devices是一个Linux内核特性,允许您创建作为真实网络接口的虚拟网络接口。发送到tap接口的包将被传递到一个用户空间程序(如QEMU),该程序将自己绑定到该接口。

QEMU可以为虚拟机使用tap网络,因此发送到tap接口的包将被发送到虚拟机,并显示为来自虚拟机中的网络接口(通常是以太网接口)。相反,虚拟机通过其网络接口发送的所有内容都将出现在tap接口上。

Linux桥接驱动程序支持Tap设备,因此可以将Tap设备彼此桥接在一起,也可以连接其他主机接口,如eth0。如果您希望您的虚拟机能够相互通信,或者希望LAN上的其他机器能够与虚拟机通信,那么这是非常理想的方案。

警告: 如果您将tap设备和一些主机接口桥接在一起,例如eth0,您的虚拟机将直接出现在外部网络上,这将使它们遭受攻击的可能。根据您的虚拟机可以访问的资源,您可能需要采取所有precautions来保护您的虚拟机。如果风险太大,虚拟机没有资源或您设置多个虚拟机,一个更好的解决方案可能是使用host-only networking建立NAT。在这种情况下,您只需要在主机上安装一个防火墙,而不是为每个虚拟机安装多个防火墙。

正如在用户模式网络部分中指出的,tap设备提供比用户模式具有更高的网络性能。如果虚拟机中的操作系统支持virtio网络驱动程序,那么网络性能也会显著提高。假设使用tap0设备,virtio驱动程序在客户端上使用,并且没有使用脚本来帮助启动/停止网络,使用下面的qemu命令:

-device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no

但是,如果已经使用带有virtio网络驱动程序的Tap设备,则甚至可以通过启用vhost来提高网络性能,例如:

-device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no,vhost=on

详情请参考:https://web.archive.org/web/20160222161955/http://www.linux-kvm.com:80/content/how-maximize-virtio-net-performance-vhost-net

仅主机 网络[编辑 | 编辑源代码]

如果为网桥提供了IP地址,并且使能发往该网桥的流量允许,但没有实际接口(例如eth0)连接到网桥,则虚拟机与虚拟机间,虚拟机与主机间能够相互通信。但是,如果您没有在物理主机上设置IP掩蔽,则他们将无法与外部网络进行通信。 此配置被其他虚拟化软件(例如VirtualBox)称为“仅主机网络模式”。

提示:
  • 如果你想设置IP掩蔽,例如虚拟机的NAT,请查看Internet sharing#Enable NAT页面。
  • 您也许想在网桥接口上运行一个DHCP服务器来服务虚拟网络。例如,使用172.20.0.1/16子网,dnsmasq作为DHCP服务器:
# ip addr add 172.20.0.1/16 dev br0
# ip link set br0 up
# dnsmasq -C /dev/null --interface=br0 --bind-interfaces --dhcp-range=172.20.0.2,172.20.255.254

内部网络[编辑 | 编辑源代码]

如果您不为网桥提供IP地址并在iptables添加INPUT规则链,将所有流向网桥中的数据丢弃,则虚拟机将能够彼此通信,但无法与物理主机或外部网络通信。此配置被其他虚拟化软件(例如VirtualBox)称为“内部网络”。您将需要为虚拟机分配静态IP地址,或在其中一个虚拟机上运行DHCP服务器。

在默认情况下,iptables将丢弃桥接网络中的数据包。您可能需要使用这样的iptables规则来允许桥接网络中的数据包:

# iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

使用 qemu-bridge-helper 桥接网络[编辑 | 编辑源代码]

这种方法不需要启动脚本,并且很容易适应多个tap和多个桥。它使用/usr/lib/qemu/qemu-bridge-helper,允许在现有桥上创建tap设备。

提示:

首先,创建一个配置文件,包含QEMU使用的所有网桥的名称:

/etc/qemu/bridge.conf
allow br0
allow br1
...

Make sure /etc/qemu/ has 755 permissions. QEMU issues and GNS3 issues may arise if this is not the case.

Now start the virtual machine; the most basic usage to run QEMU with the default network helper and default bridge br0:

$ qemu-system-x86_64 -nic bridge [...]

Using the bridge br1 and the virtio driver:

$ qemu-system-x86_64 -nic bridge,br=br1,model=virtio-net-pci [...]

手工创建网桥[编辑 | 编辑源代码]

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:This section needs serious cleanup and may contain out-of-date information.(在Talk:QEMU讨论)
提示:自 QEMU 1.1 起,network bridge helper可以为您设置 tun/tap,而无需其他脚本。 请参阅#使用 qemu-bridge-helper 桥接网络

下面介绍如何将虚拟机连接到主机接口,如eth0,这可能是最常见的配置。这种配置使虚拟机看起来直接位于外部网络,与物理主机位于同一以太网段。

我们将用桥适配器替换普通的以太网适配器,然后将普通的以太网适配器绑定到它。

  • 启用 IPv4 转发:
# sysctl -w net.ipv4.ip_forward=1

要使更改永久生效,请将 /etc/sysctl.d/99-sysctl.conf 中的 net.ipv4.ip_forward = 0 更改为 net.ipv4.ip_forward = 1

  • 加载 tun 模块,并将其配置为在引导时加载。详见Kernel modules
  • 现在创建桥。有关详细信息,请参见Bridge with netctl。请记住网桥的命名,如 br0,或将以下脚本更改为网桥的名称。In the run-qemu script below, br0 is set up if not listed, as it is assumed that by default the host is not accessing network via the bridge.
  • 创建 QEMU 用于打开 tap 适配器的脚本,该脚本具有 root:kvm 750权限:
/etc/qemu-ifup
#!/bin/sh

echo "Executing /etc/qemu-ifup"
echo "Bringing up $1 for bridged mode..."
sudo /usr/bin/ip link set $1 up promisc on
echo "Adding $1 to br0..."
sudo /usr/bin/brctl addif br0 $1
sleep 2
  • 创建QEMU用于在/etc/qemu-ifdown中关闭tap适配器的脚本,该脚本具有root:kvm 750权限:
/etc/qemu-ifdown
#!/bin/sh

echo "Executing /etc/qemu-ifdown"
sudo /usr/bin/ip link set $1 down
sudo /usr/bin/brctl delif br0 $1
sudo /usr/bin/ip link delete dev $1
  • 使用 visudo 将以下内容添加到 sudoers 文件中:
Cmnd_Alias      QEMU=/usr/bin/ip,/usr/bin/modprobe,/usr/bin/brctl
%kvm     ALL=NOPASSWD: QEMU
  • 您可以使用以下 run-qemu 脚本启动QEMU:
run-qemu
#!/bin/bash
: '
e.g. with img created via:
qemu-img create -f qcow2 example.img 90G
run-qemu -cdrom archlinux-x86_64.iso -boot order=d -drive file=example.img,format=qcow2 -m 4G -enable-kvm -cpu host -smp 4
run-qemu -drive file=example.img,format=qcow2 -m 4G -enable-kvm -cpu host -smp 4
'

nicbr0() {
    sudo ip link set dev $1 promisc on up &> /dev/null
    sudo ip addr flush dev $1 scope host &>/dev/null
    sudo ip addr flush dev $1 scope site &>/dev/null
    sudo ip addr flush dev $1 scope global &>/dev/null
    sudo ip link set dev $1 master br0 &> /dev/null
}
_nicbr0() {
    sudo ip link set $1 promisc off down &> /dev/null
    sudo ip link set dev $1 nomaster &> /dev/null
}

HASBR0="$( ip link show | grep br0 )"
if [ -z $HASBR0 ] ; then
    ROUTER="192.168.1.1"
    SUBNET="192.168.1."
    NIC=$(ip link show | grep en | grep 'state UP' | head -n 1 | cut -d":" -f 2 | xargs)
    IPADDR=$(ip addr show | grep -o "inet $SUBNET\([0-9]*\)" | cut -d ' ' -f2)
    sudo ip link add name br0 type bridge &> /dev/null
    sudo ip link set dev br0 up
    sudo ip addr add $IPADDR/24 brd + dev br0
    sudo ip route del default &> /dev/null
    sudo ip route add default via $ROUTER dev br0 onlink
    nicbr0 $NIC
    sudo iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
fi

USERID=$(whoami)
precreationg=$(ip tuntap list | cut -d: -f1 | sort)
sudo ip tuntap add user $USERID mode tap
postcreation=$(ip tuntap list | cut -d: -f1 | sort)
TAP=$(comm -13 <(echo "$precreationg") <(echo "$postcreation"))
nicbr0 $TAP

printf -v MACADDR "52:54:%02x:%02x:%02x:%02x" $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff )) $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff ))
qemu-system-x86_64 -net nic,macaddr=$MACADDR,model=virtio \
    -net tap,ifname=$TAP,script=no,downscript=no,vhost=on \
    $@

_nicbr0 $TAP
sudo ip link set dev $TAP down &> /dev/null
sudo ip tuntap del $TAP mode tap

if [ -z $HASBR0 ] ; then
    _nicbr0 $NIC
    sudo ip addr del dev br0 $IPADDR/24 &> /dev/null
    sudo ip link set dev br0 down
    sudo ip link delete br0 type bridge &> /dev/null
    sudo ip route del default &> /dev/null
    sudo ip link set dev $NIC up
    sudo ip route add default via $ROUTER dev $NIC onlink &> /dev/null
fi

然后,要启动VM,可以这样做:

$ run-qemu -hda myvm.img -m 512
/etc/sysctl.d/10-disable-firewall-on-bridge.conf
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

In order to apply the parameters described above on boot, you will also need to load the br-netfilter module on boot. Otherwise, the parameters will not exist when sysctl will try to modify them.

/etc/modules-load.d/br_netfilter.conf
br_netfilter

运行 sysctl -p /etc/sysctl.d/10-disable-firewall-on-bridge.conf 立即应用更改。

参见 libvirt wikiFedora bug 512206。如果在引导过程中 sysctl 报出关于不存在文件的错误,请在引导时加载 bridge 模块。参见Kernel module#systemd

或者,您可以配置 iptables,通过添加类似这样的规则,允许所有流量通过桥进行转发:

-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

物理设备和 Tap 设备之间通过 iptables 进行网络共享[编辑 | 编辑源代码]

本文或本章节可能需要合并到Internet_sharing

附注: Duplication, not specific to QEMU.(在 Talk:QEMU 中讨论)

桥接网络能在有线接口(例如eth0)之间工作,并且很容易设置。但是,如果主机通过无线设备连接到网络,则无法进行桥接。

参见 Network bridge#Wireless interface on a bridge

解决这个问题的一种方法是,给tap设备设置一个静态IP,使linux自动处理它的路由,然后通过iptables规则转发tap接口和连接到网络的设备之间的通信。

参见 Internet sharing.

在那里你可以找到在设备之间共享网络所需要的东西,包括tap和tun。下面将进一步介绍所需的一些主机配置。如上所述,需要为静态IP配置客户机,使用分配给tap接口的IP作为网关。需要注意的是,如果客户机上的DNS服务器在从一个连接到网络的主机设备切换到另一个时发生了更改,那么它们可能需要手动编辑。

要在每次启动时允许 IP 转发,需要在 /etc/sysctl.d 中,向 sysctl 配置文件添加以下信息:

net.ipv4.ip_forward = 1
net.ipv6.conf.default.forwarding = 1
net.ipv6.conf.all.forwarding = 1

iptables规则如下:

# Forwarding from/to outside
iptables -A FORWARD -i ${INT} -o ${EXT_0} -j ACCEPT
iptables -A FORWARD -i ${INT} -o ${EXT_1} -j ACCEPT
iptables -A FORWARD -i ${INT} -o ${EXT_2} -j ACCEPT
iptables -A FORWARD -i ${EXT_0} -o ${INT} -j ACCEPT
iptables -A FORWARD -i ${EXT_1} -o ${INT} -j ACCEPT
iptables -A FORWARD -i ${EXT_2} -o ${INT} -j ACCEPT
# NAT/Masquerade (network address translation)
iptables -t nat -A POSTROUTING -o ${EXT_0} -j MASQUERADE
iptables -t nat -A POSTROUTING -o ${EXT_1} -j MASQUERADE
iptables -t nat -A POSTROUTING -o ${EXT_2} -j MASQUERADE

假设有3个设备连接到一个内部设备的网络共享流量,例如:

INT=tap0
EXT_0=eth0
EXT_1=wlan0
EXT_2=tun0

前面显示了一个转发,允许与tap设备共享有线和无线连接。

所示的转发规则是无状态的,用于纯转发。可以考虑限制特定的流量,设置防火墙来保护来宾和其他人。然而,这些会降低网络性能,而简单的网桥不包括这些。

好处:不管连接是有线还是无线,如果使用tun设备通过VPN连接到远程站点,假设为该连接打开的tun设备是tun0,并且应用了先前的iptables规则,那么远程连接也将与客户机共享。这避免了客户也需要打开VPN连接。同样,由于来宾网络需要是静态的,因此如果以这种方式远程连接主机,很可能需要编辑来宾网络上的DNS服务器。

通过 VDE2 配置网络[编辑 | 编辑源代码]

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:This section needs serious cleanup and may contain out-of-date information.(在Talk:QEMU讨论)

何为VDE?[编辑 | 编辑源代码]

VDE 全称为 Virtual Distributed Ethernet,作为 uml_switch 的一个扩展,是一个用于管理虚拟网络的工具包

其基本的思想是创建一个虚拟的开关,就如插座那样,允许虚拟机和物理机通过"插入"连接彼此。下面的配置非常简单,然而,VDE的功能远比展示的更强大,其能够接入虚拟开关,在不同的主机上运行它们并监听开关上的通信。恳请您阅读该项目的文档获取更多信息。

本方法的优点在于无需sudo特权,普通用户一般没有运行modprobe的权限。

基础操作[编辑 | 编辑源代码]

可以通过安装 vde2 包来获得 VDE 支持。

在此处配置中,我们使用tun/tap在主机上创建一块虚拟网卡,用如下命令加载tun模块(参阅Kernel modules获取更多信息)

# modprobe tun

现在来创建一个虚拟开关:

# vde_switch -tap tap0 -daemon -mod 660 -group users

这条命令完成了开关创建,tap0创建并将其"插入",然后允许 users 组内的用户使用之。

该网卡已插入,然而还没有进行配置。需要用下面的命令进行配置:

# ip addr add 192.168.100.254/24 dev tap0

现在,只需要以普通用户的身份,指定-net参数启动KVM即可:

$ qemu-system-x86_64 -net nic -net vde -hda [...]

接下来只需同配置物理机网络一般,对客户机网络进行配置就行。

提示:你可能想在tap设备上设置NAT,实现在虚拟机内访问互联网,那就参考Internet sharing#Enable NAT获取更多的帮助吧

启动脚本[编辑 | 编辑源代码]

启动VDE的一个示例脚本:

/etc/systemd/scripts/qemu-network-env
#!/bin/sh
# QEMU/VDE network environment preparation script

# The IP configuration for the tap device that will be used for
# the virtual machine network:

TAP_DEV=tap0
TAP_IP=192.168.100.254
TAP_MASK=24
TAP_NETWORK=192.168.100.0

# Host interface
NIC=eth0

case "$1" in
  start)
        echo -n "Starting VDE network for QEMU: "

        # If you want tun kernel module to be loaded by script uncomment here
	#modprobe tun 2>/dev/null
	## Wait for the module to be loaded
 	#while ! lsmod | grep -q "^tun"; do echo "Waiting for tun device"; sleep 1; done

        # Start tap switch
        vde_switch -tap "$TAP_DEV" -daemon -mod 660 -group users

        # Bring tap interface up
        ip address add "$TAP_IP"/"$TAP_MASK" dev "$TAP_DEV"
        ip link set "$TAP_DEV" up

        # Start IP Forwarding
        echo "1" > /proc/sys/net/ipv4/ip_forward
        iptables -t nat -A POSTROUTING -s "$TAP_NETWORK"/"$TAP_MASK" -o "$NIC" -j MASQUERADE
        ;;
  stop)
        echo -n "Stopping VDE network for QEMU: "
        # Delete the NAT rules
        iptables -t nat -D POSTROUTING -s "$TAP_NETWORK"/"$TAP_MASK" -o "$NIC" -j MASQUERADE

        # Bring tap interface down
        ip link set "$TAP_DEV" down

        # Kill VDE switch
        pgrep vde_switch | xargs kill -TERM
        ;;
  restart|reload)
        $0 stop
        sleep 1
        $0 start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart|reload}"
        exit 1
esac
exit 0

使用上面的脚本作为 systemd 服务:

/etc/systemd/system/qemu-network-env.service
[Unit]
Description=Manage VDE Switch

[Service]
Type=oneshot
ExecStart=/etc/systemd/scripts/qemu-network-env start
ExecStop=/etc/systemd/scripts/qemu-network-env stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

修改 qemu-network-env 权限至可执行

现在可以像其他服务一样,启动 qemu-network-env.service 服务。

备用方法[编辑 | 编辑源代码]

如果上面的方法不起作用,或者说不想折腾内核配置、TUN、dnsmasq 和 iptables,可以采用下面的方法,也可以获得同样的效果,

# vde_switch -daemon -mod 660 -group users
# slirpvde --dhcp --daemon

然后,启动 VM 并连接至主机网络中:

$ qemu-system-x86_64 -net nic,macaddr=52:54:00:00:EE:03 -net vde disk_image

VDE2 网桥[编辑 | 编辑源代码]

根据 quickhowto: qemu networking using vde, tun/tap, and bridge 所描述的. 任何连接到 vde 上的虚拟机都会暴露给外部。举个例子,每台虚拟机都能直接从 ADSL 路由器那收到 DHCP 的配置信息。

基础操作[编辑 | 编辑源代码]

首先,你需要有 tun 模块并安装了 bridge-utils 包。

创建一个 vde2/tap 设备:

# vde_switch -tap tap0 -daemon -mod 660 -group users
# ip link set tap0 up

创建一个网桥:

# brctl addbr br0

添加设备:

# brctl addif br0 eth0
# brctl addif br0 tap0

配置网桥接口:

# dhcpcd br0

启动脚本[编辑 | 编辑源代码]

所有设备应该都设置好了,且只有网桥拥有IP地址。对于网桥上的物理设备(比如eth0),可以通过netctl使用一个自定义的配置文件完成上面所述设置:

/etc/netctl/ethernet-noip
Description='A more versatile static Ethernet connection'
Interface=eth0
Connection=ethernet
IP=no

下面的systemd服务用于为users用户组内的成员创建和启动VDE2 tap网卡。

/etc/systemd/system/vde2@.service
[Unit]
Description=Network Connectivity for %i
Wants=network.target
Before=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/vde_switch -tap %i -daemon -mod 660 -group users
ExecStart=/usr/bin/ip link set dev %i up
ExecStop=/usr/bin/ip addr flush dev %i
ExecStop=/usr/bin/ip link set dev %i down

[Install]
WantedBy=multi-user.target

最后,通过 netctl 创建网桥

简化配置参数[编辑 | 编辑源代码]

如果你经常需要以不同的网络配置选项运行QEMU,就会发现时常得输入大量的 -netdev-device 选项组合,这些需要大量重复性的劳动。可以用 -nic 选项将二者结合,就如下面这样:

-netdev tap,id=network0,ifname=tap0,script=no,downscript=no,vhost=on -device virtio-net-pci,netdev=network0

可简化为:

-nic tap,script=no,downscript=no,vhost=on,model=virtio-net-pci

要注意的是缺失了网络ID,因此将会以model=创建这些设备。{ic|-nic}}命令的前半部分参数正是-netdev的参数,而后半部分参数(model=之后的部分)则与设备有关,原本设备所提供的参数同样可以在此使用(例如,可以指定smb=)。若要完全禁用网络,可以用-nic none

参阅 QEMU networking文档了解更多的相关参数。

图形[编辑 | 编辑源代码]

使用 -display curses 命令行选项,QEMU 可以模拟一个标准显卡文本模式。这允许用户可以直接在文本终端中直接输入文本或看见文本输出。 -nographic 选项起到了类似的作用。

QEMU 可以根据 -vga type 参数模拟多种不同的显卡,可用的选项为 std,qxl,vmware,virtio,cirrusnone

std[编辑 | 编辑源代码]

使用 -vga std 你可以得到最高 2560 x 1600 像素的分辨率。从 QEMU 2.2 开始是默认选项。

qxl[编辑 | 编辑源代码]

QXL是一个支持 2D 的并行虚拟化图形驱动。需要在客户机中安装驱动并在启动 QEMU 时设置 -vga qxl 选项。你可能也会想使用 #SPICE 优化 QXL 的图形表现。

在Linux客户机中,需要加载 qxlbochs_drm 这两个内核模块,以获得一个比较好的效果。

QXL 设备的默认 VGA 内存大小为 16M,这样的内存大小最高支持 QHD (2560x1440) 的分辨率,如果想要一个更高的分辨率,请增加vga_memmb

vmware[编辑 | 编辑源代码]

尽管Bug有点多,但相比于std和cirrus它的表现会更好。对于Arch Linux客户机来说可以安装 xf86-video-vmwarexf86-input-vmmouse 获取VMware驱动。

virtio[编辑 | 编辑源代码]

virtio-vga / virtio-gpu 是一个基于virgl的3D并行虚拟化图形驱动。目前它已经成熟;目前它仅支持Linux客户机,且需要以gallium-drivers=virgl选项编译mesa

若要在客户机上启用3D加速,那么需要用 -device virtio-vga-gl 选项选择此vga,并用 -display sdl,gl=on-display gtk,gl=on 在显示设备上启用 OpenGL 上下文,这两个选项分别适用于 SDL 输出和 GTK 输出。如果配置成功了,那么在客户机的 kernel log 里可以看到:

# dmesg | grep drm 
[drm] pci: virtio-vga detected
[drm] virgl 3d acceleration enabled

cirrus[编辑 | 编辑源代码]

cirrus 是2.2之前默认的图形选项,不应当在现代操作系统中使用它。

none[编辑 | 编辑源代码]

这就像一台完全没有 VGA 卡的 PC,无法通过 -vnc 访问它。另外,这种情况与使用 -nographic 选项不同,-nographic 会让 QEMU 模拟 VGA 卡,只是关闭了 SDL 输出。

SPICE[编辑 | 编辑源代码]

SPICE project 旨在为用户提供一种完全开源的方式,无缝地对虚拟机进行远程访问。

在宿主机上启用 SPICE 支持[编辑 | 编辑源代码]

下面是一个启用SPICE作为远程桌面协议的例子,并支持复制和粘贴操作:

$ qemu-system-x86_64 -vga qxl -device virtio-serial-pci -spice port=5930,disable-ticketing=on -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 -chardev spicevmc,id=spicechannel0,name=vdagent

这些参数的含义如下:

  1. -device virtio-serial-pci 添加一块 virtio-serial 设备
  2. -spice port=5930,disable-ticketing 在 TCP 5930 端口上进行 spice 频道的监听,允许客户端不经验证即可连接。
提示:使用 Unix 套接字 而非 TCP 端口不会涉及宿主机系统的网络栈,也不意味着可以对数据包进行封装和解封以使用网络和其它相关的协议。这些套接字仅通过硬盘上的 inode 进行表示,这么做是出于性能上的考虑。Unix 套接字可以使用 -spice unix=on,addr=/tmp/vm_spice.socket,disable-ticketing=on 代替上述参数。
  1. -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 在 virtio-serial 设备上为 spice vdagent 打开一个端口。
  2. -chardev spicevmc,id=spicechannel0,name=vdagent 为该端口添加一块 spicevmc 字符设备。virtserialportchardev= 选项需要与 chardevid= 相符,在本例中是 spicechannel0。将端口名字设置为 com.redhat.spice.0 也很重要,因为它就是 vdagent 在客户机上所搜索的命名空间。最后,指定 name=vdagent 选项使得 spice 知道该频道的服务对象。

通过 SPICE 客户端连接到客户机[编辑 | 编辑源代码]

若要连接到客户机上必须要有一个 SPICE 客户端。在 Arch 中,有如下可用的客户端:

  • virt-viewer — 协议开发者所推荐的 SPICE 客户端,是 virt-manager 项目的子集。
https://virt-manager.org/ || virt-viewer
  • spice-gtk — SPICE GTK 客户端,SPICE 项目的一个子集,作为小部件嵌入其它应用中。
https://www.spice-space.org/ || spice-gtk

若需要能在智能手机上运行的客户端,或者其他平台的客户端,参照 spice-space download 中的 Other clients 章节。

手动开启 SPICE 客户端[编辑 | 编辑源代码]

连接到一个监听在 Unix 套接字 /tmp/vm_spice.socket 上的客户机的方法是用 $ remote-viewer spice+unix:///tmp/vm_spice.socket$ spicy --uri="spice+unix:///tmp/vm_spice.socket" 命令手动运行 SPICE 客户端,使用的客户端取决于你的喜好。SPICE 模式下的 QEMU 就如一个远程桌面服务器,可能使用 -daemonize 参数以daemon 模式运行 QEMU 会更方便一些。

提示:可以用下面的命令,通过 SSH 隧道连接到客户机:
$ ssh -fL 5999:localhost:5930 my.domain.org sleep 10; spicy -h 127.0.0.1 -p 5999
。这个例子中 spicy 连接到了本地的 5999 端口,该端口通过 SSH 转发至 my.domain.org 上的 SPICE 服务器端口 5930。要注意 -f 选项让 ssh 在后台执行了 sleep 10 命令。这种情况下,在客户端存活时 ssh 会话保持运行,在客户端结束运行时将自动关闭 ssh 会话。

QEMU 运行时启动SPICE[编辑 | 编辑源代码]

QEMU 可以自动地开启一个 SPICE 客户端并创建一个合适的套接字。如果通过 -display spice-app 参数将显示设置为SPICE,那么将会使用系统默认的 SPICE 客户端作为 viewer,默认的客户端取决于你的mimeapps.list文件。

在客户机上开启SPICE的支持[编辑 | 编辑源代码]

对于Arch Linux客户机,如要支持多屏和共享剪贴板,需要安装以下的包:

  • spice-vdagent: Spice的xorg客户端代理,使得用户能够在客户端和X-session之间进行复制和粘贴等。(Refer to this issue, until fixed, for workarounds to get this to work on non-GNOME desktops.)
  • xf86-video-qxl: Xorg X11 qxl视频驱动
  • x-resizeAUR: 除 GNOME 之外的桌面环境在 SPICE 客户端窗口大小调整时不会自动反应。此软件包使用 udev 规则和 xrandr 为所有基于 X11 的桌面环境和窗口管理器实现自动调整大小。

对于其它的操作系统客户机, 参照spice-space downloadGuest章节。

开启 SPICE 口令验证[编辑 | 编辑源代码]

若要启用 SPICE 的口令验证,需要从 -spice 的参数中移除 disable-ticketing,改为 password=yourpassword,例如:

$ qemu-system-x86_64 -vga qxl -spice port=5900,password=yourpassword -device virtio-serial-pci -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 -chardev spicevmc,id=spicechannel0,name=vdagent

现在 SPICE 客户端在连接 SPICE 服务器的时候应该就会进行口令询问了。

用 TLS 对与 SPICE 的通信进行加密[编辑 | 编辑源代码]

同样可以为客户端与 SPICE 服务器的通信配置 TLS 加密。首先,需要有一个包含如下文件的目录(文件名必须与下面保持一致):

  • ca-cert.pem: CA 主证书。
  • server-cert.pem: ca-cert.pem 签名后的服务器证书。
  • server-key.pem: 服务器的私钥。

Spice User Manual还展示了使用服务器自己生成的CA生成自签名证书的例子。

这些完成后,你可以用前文描述的 -spice 的参数在 QEMU 启动时自动开启 SPICE:-spice tls-port=5901,password=yourpassword,x509-dir=/path/to/pki_certs,其中 /path/to/pki_certs 代表包含那三个文件的目录。

现在可以用 virt-viewer 连接到服务器了:

$ remote-viewer spice://hostname?tls-port=5901 --spice-ca-file=/path/to/ca-cert.pem --spice-host-subject="C=XX,L=city,O=organization,CN=hostname" --spice-secure-channels=all

要记住的是,--spice-host-subject 参数需要根据你的 server-cert.pem 中的子条目进行设置。此外还需要将 ca-cert.pem 复制到每个客户端上用于验证服务器证书。

提示:使用下面的命令,就可以获得服务器证书中的子条目格式,可用于 --spice-host-subject 参数中(以逗号进行分隔):
$ openssl x509 -noout -subject -in server-cert.pem | cut -d' ' -f2- | sed 's/\///' | sed 's/\//,/g'

等效的 spice-gtk 命令为:

$ spicy -h hostname -s 5901 --spice-ca-file=ca-cert.pem --spice-host-subject="C=XX,L=city,O=organization,CN=hostname" --spice-secure-channels=all

VNC[编辑 | 编辑源代码]

可以用-vnc :X选项将QEMU的VGA输出重定向至VNC会话中。将X替换为输出目标的编号(0代表之后监听在5900,1代表监听在5901...)。

$ qemu-system-x86_64 -vnc :0

#开机时启动QEMU虚拟机这一小节中同样提供了一个VNC的示例。

警告: 默认的 VNC 服务器没有使用任何验证手段,用户可以从任何主机上连接到 VNC。

基本的口令验证[编辑 | 编辑源代码]

可以通过使用password选项很容易地设置访问口令。必须在QEMU Monitor中指定口令,仅当用户提供口令时才有可能连接到VNC。

$ qemu-system-x86_64 -vnc :0,password -monitor stdio

在QEMU Monitor中设置口令需使用 change vnc password 命令,然后指定一个口令。

底下的命令将在启动 VNC 时直接为其设置口令:

$ printf "change vnc password\n%s\n" MYPASSWORD | qemu-system-x86_64 -vnc :0,password -monitor stdio
注意: 口令被限制在8个字符内,可以用暴力破解的方式猜到口令。因此在公网上推荐使用更细致的保护措施。

音频[编辑 | 编辑源代码]

创建音频后端[编辑 | 编辑源代码]

-audiodev 标识用于设定后端音频驱动及其相关选项。

To list availabe audio backend drivers:

$ qemu-system-x86_64 -audiodev help

qemu(1)man 页面中详细列出了可用的后端音频驱动以及可选的设置项。

最简单的情况下,你需要选择一个驱动并设置一个 id。以 PulseAudio 为例:

-audiodev pa,id=snd0

使用音频后端[编辑 | 编辑源代码]

Intel HD Audio[编辑 | 编辑源代码]

模拟 Intel HD Audio 需要添加控制器和编解码器设备。可以用如下命令列出可用的 Intel HDA Audio 设备:

$ qemu-system-x86_64 -device help | grep hda

添加音频控制器:

-device ich9-intel-hda

添加音频编解码器并将其映射到宿主机的音频后端 id 上。

-device hda-output,audiodev=snd0

Intel 82801AA AC97[编辑 | 编辑源代码]

模拟 AC97 需要添加声卡设备并将其映射到宿主机的一个音频后端 id 上。

-device AC97,audiodev=snd0
注意:
  • 如果没有提供 audiodev 后端,QEMU 会自动查找并添加它,这只适用于单个 audiodev。例如,-device intel-hda -device hda-duplex 将使用默认的 audiodev 后端在客户机上模拟 intel-hda
  • 客户机的显卡模拟驱动也可能导致声音质量的问题,需要逐一测试以使其正常工作。你可以用 qemu-system-x86_64 -h | grep vga 列出可能的选项。

VirtIO sound[编辑 | 编辑源代码]

自 QEMU 8.2.0 版本开始,VirtIO 声卡也可用。使用方式如下:

-device virtio-sound-pci,audiodev=my_audiodev -audiodev alsa,id=my_audiodev

更多信息可以在 QEMU 文档中找到。

安装 virtio 驱动[编辑 | 编辑源代码]

QEMU 为用户提供并行虚拟化块设备和网络设备的能力,其是借助 virtio 驱动实现的,拥有更好的性能表现以及更低的开销。

  • virtio块设备需要使用 -drive 指定一个 disk image 的参数,且需要带上if=virtio参数:
$ qemu-system-x86_64 -drive file=disk_image,if=virtio
  • 网络配置也是类似的:
$ qemu-system-x86_64 -nic user,model=virtio-net-pci
注意: 仅有当客户机有 virtio 设备对应的驱动时该方法才能起效,Linux 是有这方面支持的,而在 Arch Linux 中已经包含所需的驱动了,不过无法保证这些驱动能够兼容其他操作系统。

Arch Linux 客户机[编辑 | 编辑源代码]

要在Arch Linux客户机中使用virtio设备,必须在客户端加载以下模块:{virtiovirtio_pcivirtio_blkvirtio_netvirtio_ring。对于32位系统来说,不需要特定的“virtio”模块。

如果希望从 virtio 磁盘引导,ramdisk 必须包含必要的模块。默认情况下,这是由 mkinitcpioautodetect 钩子处理的。否则要在 /etc/mkinitcpio.conf 中使用 MODULES 数组包含必要的模块并重新构建 ramdisk。

/etc/mkinitcpio.conf
MODULES=(virtio virtio_blk virtio_pci virtio_net)

Virtio 磁盘公认的前缀为 v(例如:vdavdb,等等)。因此,当从 virtio 磁盘启动时,需要在 /etc/fstab/boot/grub/grub.cfg 中进行更改。

提示:当在 /etc/fstab 和引导加载程序中通过 UUID 引用磁盘时,不需要执行任何操作。

关于使用KVM进行半虚拟化的更多信息,可以参考这里

您可以安装 qemu-guest-agent 来实现对 QMP 命令的支持,从而增强管理程序的管理能力。安装完成后,您需要启动qemu-guest-agent.service

Windows 客户机[编辑 | 编辑源代码]

Windows 的 Virtio 驱动[编辑 | 编辑源代码]

Windows 系统不自带 virtio 驱动程序。最新和稳定版本的驱动程序由 Fedora 定期构建,有关下载驱动程序的详细信息可以在 GitHub 上的 virtio-win 项目中找到。 在以下部分中,我们将主要使用由 virtio-win.iso 提供的稳定 ISO 文件。或者,可以使用 virtio-winAUR

块设备驱动[编辑 | 编辑源代码]

安装一个新的Windows[编辑 | 编辑源代码]

需要在系统安装时通过 ISO 加载驱动,具体操作是在主磁盘设备和 Windows 安装盘外挂载一个额外的 cdrom 设备,将系统镜像与 virtio 驱动一同加载:

$ qemu-system-x86_64 ... \
-drive file=disk_image,index=0,media=disk,if=virtio \
-drive file=windows.iso,index=2,media=cdrom \
-drive file=virtio-win.iso,index=3,media=cdrom \
...

在安装过程中,Windows Installer 会询问你“Where do you want to install Windows?”,其会返回一个警告表示没有找到任何磁盘设备。接下来跟着如下示例中的步骤进行操作(基于Windows Server 2012 R2 with Update):

  • 选择选项加载驱动
  • 反选隐藏与这个计算机硬件不兼容的驱动
  • 按 浏览 按钮并选择 virtio iso 的 CDROM ,一般命名为 "virtio-win-XX"
  • 然后浏览 E:\viostor\[your-os]\amd64 ,选择它并确认

现在应该能看到 virtio 磁盘出现在列表中了,等待被选中、格式化并安装。

将现有的 Windows VM 转为使用 virtio[编辑 | 编辑源代码]

可将现有的 Windows 客户机改为从 virtio 磁盘中启动,前提是客户机中有 virtio 驱动,且该驱动在启动期间就被加载。

然后我们需要让 Windows 在 Windows 能以 virtio 模式启动一个磁盘镜像之前,在启动时加载 virtio 驱动。

先创建一个新的磁盘镜像,用于搜索 virtio 驱动:

$ qemu-img create -f qcow2 fake.qcow2 1G

挂载 fake 磁盘(处于virtio模式下)和带有驱动的CD-ROM,运行原本的Windows客户机(启动盘依旧是处于IDE模式中):

$ qemu-system-x86_64 -m 4G -drive file=disk_image,if=ide -drive file=fake.qcow2,if=virtio -cdrom virtio-win.iso

Windows会自动检测fake磁盘,并搜索适配的驱动。如果失败了,前往Device Manager,找到SCSI驱动器(带有感叹号图标,应处于打开状态),点击Update driver并选择虚拟的CD-ROM。不要定位到CD-ROM内的文件夹了,只选择CD-ROM设备就行,Windows会自动找到合适的驱动的。(已在Windows 7 SP1中完成测试)。

可以让Windows从下一次起为以安全模式启动,可以用Windows上的msconfig.exe工具完成该配置,在安全模式中所有的驱动都会在boot期间被加载,包括我们新装的virtio驱动。只要Windows知道boot期间需要加载virtio,在未来的boot过程中Windows都会记住该设置的。

将客户机配置为安全模式启动后,可以关机并重新启动它,现在可以以 virtio 模式挂载 boot 磁盘:

$ qemu-system-x86_64 -m 4G -drive file=disk_image,if=virtio

现在应当能携带 virtio 驱动启动到安全模式中,你可以返回到 msconfig.exe 中禁用安全模式并重启 Windows。

注意: 如果使用if=virtio参数时碰上了蓝屏问题,这代表virtio磁盘驱动可能没有在boot期间被安装,以安全模式重启之,然后检查你的驱动配置。

网络驱动[编辑 | 编辑源代码]

安装virtio网络驱动程序要容易一些,只需如上所述添加-net参数即可。

$ qemu-system-i386 -m 4G -vga std -drive file=windows_disk_image,if=virtio -net nic,model=virtio -cdrom virtio-win-0.1-185.iso

Windows将检测网络适配器并尝试为其找到驱动程序。如果失败,请转到“设备管理器”,找到带有感叹号图标的网络适配器(双击打开),切换到驱动程序并单击“更新驱动程序”,然后选择虚拟CD-ROM。别忘了选中显示要递归搜索目录的复选框。

Balloon 驱动[编辑 | 编辑源代码]

如果想要追踪客户机内存状态(比如通过virshdommemstat命令)或者在运行时改变客户机内存大小(尽管依然无法改变实际的内存大小,不过可以通过inflating balloon驱动限制内存的使用),那么请在客户机上安装balloon驱动吧。

安装该驱动需要前往Device Manager,其位于System devices内的PCI standard RAM Controller中(未识别的PCI控制设备则在Other devices中)。选择Update driver,在打开的窗口中选择Browse my computer...,然后选择CD-ROM(记得勾上Include subdirectories选项),安装完成后重启,驱动就安装成功了。现在可以如气球打气一般调整内存限制(例如通过hmp命令balloon memory_size,该命令会使balloon从客户机中尽可能地夺取内存,将客户机的内存大小限制至memory_size)。然而,现在依然无法追踪内存的状态,我们还需要正确地安装Balloon服务才行。以管理员身份启动命令行,前往CD-ROM中的Balloon目录,然后在目录下找到对应系统和架构的地方。当深入至 amd64 (x86) 目录时,运行blnsrv.exe -i进行安装,安装之后virsh命令dommemstat就会输出命令所支持的各种数据。

FreeBSD客户机[编辑 | 编辑源代码]

如果你使用的是FreeBSD 8.3之后10.0-CURRENT之间的版本, 那么无论内核中是否包含了virtio-kmod, 都需要安装emulators/virtio-kmod端口。安装之后请在/boot/loader.conf添加如下内容:

virtio_load="YES"
virtio_pci_load="YES"
virtio_blk_load="YES"
if_vtnet_load="YES"
virtio_balloon_load="YES"

按照下面修改 /etc/fstab

# sed -ibak "s/ada/vtbd/g" /etc/fstab

注意确认 /etc/fstab 内容与该命令的预期效果相同,如果出错了就通过 rescue CD 启动,然后将 /etc/fstab.bak 复制到 /etc/fstab

QEMU 监视器[编辑 | 编辑源代码]

QEMU运行时会提供一个监视器 console 界面以方便用户同虚拟机进行交互。QEMU 监视器提供了许多有趣的功能,例如获取当前虚拟机的信息,热插拔设备,创建快照等。在 QEMU 监视器 console 中运行 help? 命令,或者阅读官方 QEMU 文档获得完整的命令列表。

访问 QEMU 监视器 Console[编辑 | 编辑源代码]

图形化界面[编辑 | 编辑源代码]

当使用默认的 std 图形选项时,可以通过按下 Ctrl+Alt+2 组合键或从QEMU窗口上的 View > compatmonitor0 访问到 QEMU 监视器。若要返回到虚拟机的图形界面,那么按下 Ctrl+Alt+1 或者 View > VGA 就行。

然而,这种标准的访问方式不够方便,而且并不是在QEMU的所有图形化输出方式中都适用。

Telnet[编辑 | 编辑源代码]

启动 QEMU 时带上 -monitor telnet:127.0.0.1:port,server,nowait 参数可以启用 telnet。虚拟机启动后可以通过 telnet 访问到监视器:

$ telnet 127.0.0.1 port
注意: 如果指定 127.0.0.1 作为监听地址,那么只能在运行 QEMU 的宿主机上连接到该监视器。如果想要远程访问,QEMU需要在 0.0.0.0 上进行监听:-monitor telnet:0.0.0.0:port,server,nowait。还要记住的是,最好对防火墙进行配置,该连接是完全不进行认证和加密的,因此需要通过防火墙确保本地网络环境是可信的。

UNIX 套接字[编辑 | 编辑源代码]

通过 -monitor unix:socketfile,server,nowait 参数运行QEMU,之后就可以通过 socatnmapopenbsd-netcat 连接到监视器上。

例如,如果QEMU是通过如下命令启动:

$ qemu-system-x86_64 -monitor unix:/tmp/monitor.sock,server,nowait [...]

就可以像这样连接到监视器上:

$ socat - UNIX-CONNECT:/tmp/monitor.sock

或者通过这种方式:

$ nc -U /tmp/monitor.sock

也可以使用 nmap

$ ncat -U /tmp/monitor.sock

TCP[编辑 | 编辑源代码]

可以使用 -monitor tcp:127.0.0.1:port,server,nowait 参数将监视器暴露于TCP端口上,然后用 netcat(openbsd-netcatgnu-netcat 都可以)进行连接:

$ nc 127.0.0.1 port
注意: 为了能够从其它设备上通过 TCP 套接字访问到监视器,而不仅仅从运行QEMU的主机上连接,需要像前面Telnet中描述的那样,在0.0.0.0地址上进行监听。

标准 I/O[编辑 | 编辑源代码]

如果以 -monitor stdio 参数运行QEMU,那么其实是可以在运行 QEMU 的终端下访问到监视器的。

在 Monitor conosle 下向虚拟机发送按键行为[编辑 | 编辑源代码]

由于在某些配置下,宿主机可能会拦截一些按键组合另作他用,这导致要在虚拟机中触发一些特定按键组合变得有些困难(一个显然的例子就是Ctrl+Alt+F*组合,该组合用于改变当前的tty)。我们采用在monitor console下发送按键组合的方式解决该问题。只需切换到monitor console下,然后使用sendkey命令,即可将按键转发至虚拟机中,例如:

(qemu) sendkey ctrl-alt-f2

通过 monitor console 创建快照和管理快照[编辑 | 编辑源代码]

注意: 该特性支持 qcow2 格式的虚拟机磁盘镜像,对于 raw 是无效的。

有时候我们很需要将虚拟机的当前状态进行保存,或是将虚拟机重置到之前的快照状态,而且最好是随时能进行这些操作。QEMU monitor console为用户提供了必要的功能,进行快照创建,快照管理,以及快照恢复。

  • Use savevm name 用于创建一个名为name的快照。
  • Use loadvm name 用于将虚拟机状态恢复至快照name
  • Use delvm name 用于删除快照name
  • Use info snapshots 用于查看保存的快照列表,这些快照由一个自增长的ID和标签名(用户创建快照时赋予)进行标识。

以冻结模式运行虚拟机[编辑 | 编辑源代码]

QEMU支持以冻结态运行虚拟机(需使用-snapshot参数),换句话说,虚拟机关闭时,对于虚拟机的一切修改都会丢弃。当用户对磁盘镜像写入时,这些变动最终写入的位置是/tmp目录下的一个临时文件,QEMU关机时将会把他们丢弃。

不过,即使虚拟机运行于冻结状态下,依旧可以通过monitor console将这些变化写入磁盘镜像(如果你想的话)。使用下面的命令:

(qemu) commit all

另外如果在冻结状态下创建快照,这些快照在QEMU退出时都会被丢弃,除非你显式地commit了他们。

monitor console 中的开机和暂停命令[编辑 | 编辑源代码]

在QEMU monitor console下也可以模拟对物理机的一些操作:

  • system_powerdown 会向虚拟机发送ACPI关机信号,效果就类似物理机上按下电源按钮。
  • system_reset 会重置虚拟机,类似物理机上的重置按钮。该操作可能导致数据丢失或文件系统的损坏,这是因为虚拟机并不是"干净地"重启的。
  • stop 会暂停虚拟机。
  • cont 使暂停的虚拟机恢复运行。

虚拟机截屏[编辑 | 编辑源代码]

可以在monitor console下运行该命令,获取PPM格式的截屏图片:

(qemu) screendump file.ppm

QEMU 机器协议[编辑 | 编辑源代码]

QEMU机器协议(QMP)是一个基于JSON格式的协议,使得其他应用程序可以通过该协议控制QEMU实例。类似#QEMU 监视器,其提供了与运行中的虚拟机进行交互的能力,且能够编程进行控制。关于QMP各命令的描述可以在这个qmp-commands链接中找到。

启动 QMP[编辑 | 编辑源代码]

使用QMP协议来控制虚拟机的通常做法是在启动QEMU时使用-qmp打开一个 TCP 套接字。底下是一个使用 TCP 4444 端口的例子:

$ qemu-system-x86_64 [...] -qmp tcp:localhost:4444,server,nowait

而与QMP代理进行通信的一个选择是使用 netcat:

nc localhost 4444
{"QMP": {"version": {"qemu": {"micro": 0, "minor": 1, "major": 3}, "package": ""}, "capabilities": []} } 

在目前这个阶段,其能识别的命令仅有qmp_capabilities,QMP将进入了命令模式。敲下:

{"execute": "qmp_capabilities"}

现在,QMP可以接收命令了。要查看QMP接受的命令列表,使用:

{"execute": "query-commands"}

即时将子镜像合并至父镜像中[编辑 | 编辑源代码]

通过发起一个block-commit可以将一个正处于运行态的快照合并到其父结点上。下面的例子是其最简单的一种形式,将把子镜像提交至父镜像中:

{"execute": "block-commit", "arguments": {"device": "devicename"}}

QMP收到该命令后,处理程序将会寻找基镜像,把该镜像由只读转为可读写模式,然后完成commit的任务。

一旦block-commit操作完成,将会触发一个BLOCK_JOB_READY事件,发出同步完成的信号。当然也可以用一种更优雅的方式完成该操作,改为使用block-job-complete

{"execute": "block-job-complete", "arguments": {"device": "devicename"}}

在发出该命令之前,commit操作将会保持活动状态。 任务完成后,基镜像将会保持在可读写模式,并变为active层。另一方面,子镜像将变得不可使用,用户有责任将其清除。

提示:执行query-block命令并对输出进行解析可以获取设备列表和其对应名字。设备名在 device 字段中,例如本例中磁盘设备名就是 ide0-hd0
{"execute": "query-block"}
{"return": [{"io-status": "ok", "device": "ide0-hd0", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"backing-image": {"virtual-size": 27074281472, "filename": "parent.qcow2", ... } 

即时创建一个新的快照[编辑 | 编辑源代码]

为运行中的镜像创建一个快照:

{"execute": "blockdev-snapshot-sync", "arguments": {"device": "devicename","snapshot-file": "new_snapshot_name.qcow2"}}

该命令将会创建一个名为 new_snapshot_name.qcow2 的堆叠文件,该快照也将成为新的活动层。

技巧[编辑 | 编辑源代码]

改善虚拟机的性能表现[编辑 | 编辑源代码]

底下是一些可以改善虚拟机性能表现的技术,例如:

  • 启用#启用 KVM:QEMU的启动命令加上 -enable-kvm 选项。
  • 通过 -cpu host 选项让 QEMU 模拟宿主机上的特定 CPU,如果没有该选项 QEMU 尝试模拟的是一个更为通用的 CPU。
  • 特别的,如果客户机是 Windows,启用Hyper-V enlightenments可以改善性能:-cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time.有关更多信息和标志,请参阅 QEMU 文档
  • 可以使用 -smp cores=x,threads=y,sockets=1,maxcpus=z 选项为虚拟机分配多个核心。threads 参数用于分配 SMT 核心。为 QEMU、管理程序和主机系统保留一个物理核心不受干扰是非常有益的。
  • 检查是否为虚拟机分配的足够的内存。默认情况下,QEMU仅仅为每台虚拟机分配 128MiB 的内存,可以使用 -m 选项分配更多的内存。例如,-m 1024 代表启动一台内存为 1024MiB 的虚拟机,-m 4G则为4GiB的内存。
  • 如果客户机操作系统支持相关的驱动,可以使用virtio创建网络设备或块设备,详情可参考 #Installing virtio drivers
  • 使用 TAP 设备代替 user-mode 网络,参阅#Tap 网络
  • 如果客户机需要进行大量的磁盘写工作,在宿主机文件系统上设置合适的挂载选项可以优化该工作。例如,可以用barrier=0选项挂载一个 Ext4 文件系统。在使用这些性能强化选项之前最好阅读相关文档,因为性能上的提升通常伴随着数据完整性下降的代价。
  • 如果有一块原始磁盘或分区,你可能会想要禁用缓存:
    $ qemu-system-x86_64 -drive file=/dev/disk,if=virtio,cache=none
  • 使用原生的Linux AIO:
    $ qemu-system-x86_64 -drive file=disk_image,if=virtio,aio=native,cache.direct=on
  • 如果正同时运行多台虚拟机,而它们拥有同样的操作系统,可以通过启用内核页归并节省内存。参阅#开启 KSM
  • 在一些情况下,可以在运行时从安装了 balloon 驱动的客户机上回收内存,这需要 QEMU 启动该客户机时使用-device virtio-balloon选项。
  • 尽管不是很稳定,可以使用 ICH-9 AHCI 控制器的仿真层。AHCI 的仿真模拟支持 NCQ,因此可以同时处理多个读写请求:
    $ qemu-system-x86_64 -drive id=disk,file=disk_image,if=none -device ich9-ahci,id=ahci -device ide-drive,drive=disk,bus=ahci.0

参阅 https://www.linux-kvm.org/page/Tuning_KVM 获取更多信息。

开机时启动QEMU虚拟机[编辑 | 编辑源代码]

通过 libvirt 实现[编辑 | 编辑源代码]

如果虚拟机是通过 libvirt 设置的,可以用 virsh autostart 将其配置为开机自启,或者通过 virt-manager GUI 中虚拟机的 Boot Options,选择 "Start virtual machine on host boot up" 实现开机自启。

通过 systemd 服务实现[编辑 | 编辑源代码]

可以用如下的 systemd unit 和 config 配置开机时启动 QEMU VM。

/etc/systemd/system/qemu@.service
[Unit]
Description=QEMU virtual machine

[Service]
Environment="haltcmd=kill -INT $MAINPID"
EnvironmentFile=/etc/conf.d/qemu.d/%i
ExecStart=/usr/bin/qemu-system-x86_64 -name %i -enable-kvm -m 512 -nographic $args
ExecStop=/usr/bin/bash -c ${haltcmd}
ExecStop=/usr/bin/bash -c 'while nc localhost 7100; do sleep 1; done'

[Install]
WantedBy=multi-user.target
注意: 为了方便地结束任务,该 service 会等待至 console 端口被释放(这意味着 VM 已被关闭)。

接着按单个 VM 创建配置文件,命名为 /etc/conf.d/qemu.d/VM 名称,在其中设置好 argshaltcmd 变量,配置示例:

/etc/conf.d/qemu.d/one
args="-hda /dev/vg0/vm1 -serial telnet:localhost:7000,server,nowait,nodelay \
 -monitor telnet:localhost:7100,server,nowait,nodelay -vnc :0"

haltcmd="echo 'system_powerdown' | nc localhost 7100" # or netcat/ncat
/etc/conf.d/qemu.d/two
args="-hda /srv/kvm/vm2 -serial telnet:localhost:7001,server,nowait,nodelay -vnc :1"

haltcmd="ssh powermanager@vm2 sudo poweroff"

对该变量的描述如下:

  • args - 使用的 QEMU 命令行参数。
  • haltcmd - 安全关闭虚拟机的命令,在第一个例子中,QEMU monitor 是通过 -monitor telnet:.. 选项暴露至 telnet,因而关闭虚拟机是通过 nc 命令在 monitor console 中发送 system_powerdown,完成 ACPI 关机的工作。在另一个例子里,使用的则是 SSH。

若要设置启动时运行哪个虚拟机,启用 qemu@vm_name.service 这个 systemd 单元。

鼠标整合[编辑 | 编辑源代码]

添加 -usb -device usb-tablet 选项以避免点击客户机系统的窗口时鼠标被捕获。该选项代表 QEMU 能够在不捕获鼠标的情况下,向系统报告鼠标的位置,该选项启用时还会覆盖 PS/2 鼠标模拟功能。例如:

$ qemu-system-x86_64 -hda disk_image -m 512 -usb -device usb-tablet

如果该命令不起作用,试试 -vga qxl 参数,并看看#鼠标指针抖动或者不稳定的操作指导。

宿主机的USB设备传递至虚拟机[编辑 | 编辑源代码]

从客户机访问连接到宿主机USB口的设备是可能的,首先需要识别设备连接的位置,可以用lsusb命令找到设备连接位置,例如:

$ lsusb
...
Bus 003 Device 007: ID 0781:5406 SanDisk Corp. Cruzer Micro U3

上面以粗体显示的数字分别用于标识“ host_bus”和“ host_addr”或者“ vendor_id”和“ product_id”。

基本的思想是在 QEMU 中 -device usb-ehci,id=ehci-device qemu-xhci,id=xhci 分别对 EHCI (USB 2) 或 XHCI (USB 1.1,USB 2,USB 3) 控制器进行模拟,然后将物理设备通过 -device usb-host,.. 选项进行添加。在本节中的剩余部分,controller_id要么是 ehci,要么是 xhci

接着,这里有两种方法通过qemu连接到宿主机的USB:

  1. 识别出该设备,并将其连接至任一总线以及宿主机上的地址,通用的语法如下:
    -device usb-host,bus=controller_id.0,vendorid=0xvendor_id,productid=0xproduct_id
    。Applied to the device used in the example above, it becomes:
    -device usb-ehci,id=ehci -device usb-host,bus=ehci.0,vendorid=0x0781,productid=0x5406
    ;此外也可以在上面的选项中添加...,port=port_number 设置,用于指定设备添加至虚拟机控制器上的哪个物理端口。该设置在为 VM 添加多个设备时比很有用。另一个方案是使用 QEMU 5.1.0 之后出现的 usb-hosthostdevice属性,语法为:
    -device qemu-xhci,id=xhci -device usb-host,hostdevice=/dev/bus/usb/003/007
  2. 若要添加一个已连接至特定USB总线和地址的设备,语法为:
    -device usb-host,bus=controller_id.0,hostbus=host_bus,host_addr=host_addr
    ,将其中的参数修改至该总线和地址即可,拿上面的例子来说,需修改为:
    -device usb-ehci,id=ehci -device usb-host,bus=ehci.0,hostbus=3,hostaddr=7
注意: 如果运行 QEMU 时遇到了权限方面的错误,可以阅读 udev#About udev rules 获取更多信息,以了解如何为设备设定合适的权限。

使用 SPICE 进行 USB 重定向[编辑 | 编辑源代码]

使用#SPICE时可以将USB设备从客户端重定向至虚拟机中,无需使用QEMU命令。还支持为配置USB重定向插槽数(插槽数将决定可同时重定向的最大设备数)。相比于前面那种使用-usbdevice进行重定向的方法,SPICE方法的优势在于可以在虚拟机启动后USB设备热插拔,移除或添加USB设备时无需停机。这个方法还允许通过网络将客户端的USB设备重定向至服务端。总之,其是在QEMU虚拟机中使用USB设备最灵活的方法。

我们需要为每个所需的USB重定向插槽添加一个EHCI/UHCI控制器,以及每个插槽添加一个SPICE重定向通道。例如,将下面的选项加入到所使用的QEMU命令中,以SPICE模式启动虚拟机时将会添加三个用于重定向的USB插槽:

-device ich9-usb-ehci1,id=usb \
-device ich9-usb-uhci1,masterbus=usb.0,firstport=0,multifunction=on \
-device ich9-usb-uhci2,masterbus=usb.0,firstport=2 \
-device ich9-usb-uhci3,masterbus=usb.0,firstport=4 \
-chardev spicevmc,name=usbredir,id=usbredirchardev1 -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1 \
-chardev spicevmc,name=usbredir,id=usbredirchardev2 -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2 \
-chardev spicevmc,name=usbredir,id=usbredirchardev3 -device usb-redir,chardev=usbredirchardev3,id=usbredirdev3

参阅 SPICE/usbredir 获取更多信息。

spice-gtk中的spicy(Input > Select USB Devices for redirection)和virt-viewer中的remote-viewer(File > USB device selection)都支持该特性。请确保你已在客户机上安装必要的SPICE Guest Tools,以使得该功能正常运作(参见#SPICE节获得更多信息)。

警告: 需要牢记一点,当将USB设备从客户端重定向至服务端时,在客户端所在的操作系统上都无法使用该USB了,除非停止重定向。尤其重要的是千万不要将输入设备重定向(即不要将鼠标和键盘重定向),这么做会使得你难以访问到SPICE客户端的菜单而无法撤销该操作,因为在输入设备被重定向至虚拟机后,客户端无法响应它们的操作。

使用 udev 进行自动 USB 转发[编辑 | 编辑源代码]

通常,转发的设备必须在虚拟机启动时可用才能被转发。如果该设备断开连接,它将不再被转发。

您可以使用 udev rule 在设备上线时自动附加设备。在磁盘上的某个位置创建一个 hostdev 条目,并将其所有权 chown 为 root ,以防止其他用户修改它。

/usr/local/hostdev-mydevice.xml
<hostdev mode='subsystem' type='usb'>
  
    <vendor id='0x03f0'/>
    <product id='0x4217'/>
</hostdev>

然后创建一个用于附加或脱离设备的 udev 规则:

/usr/lib/udev/rules.d/90-libvirt-mydevice
ACTION=="add", \
    SUBSYSTEM=="usb", \
    ENV{ID_VENDOR_ID}=="03f0", \
    ENV{ID_MODEL_ID}=="4217", \
    RUN+="/usr/bin/virsh attach-device GUESTNAME /usr/local/hostdev-mydevice.xml"
ACTION=="remove", \
    SUBSYSTEM=="usb", \
    ENV{ID_VENDOR_ID}=="03f0", \
    ENV{ID_MODEL_ID}=="4217", \
    RUN+="/usr/bin/virsh detach-device GUESTNAME /usr/local/hostdev-mydevice.xml"

详阅这篇博客

开启KSM[编辑 | 编辑源代码]

Kernel Samepage Merging (KSM) 是Linux内核的一个特性,允许应用程序向内核申请同其他申请页归并的进程进行页归并,KSM机制允许客户虚拟机之间进行页共享。当许多客户机运行相似的操作系统时,这个机制可以节省客观的内存。

注意: 尽管KSM可能减小内存占用量,其增加了CPU负担。此外要注意的是使用该机制可能产生一些安全问题,参见Wikipedia:Kernel same-page merging

启用KSM:

# echo 1 > /sys/kernel/mm/ksm/run

使之常驻,使用 systemd 的临时文件

/etc/tmpfiles.d/ksm.conf
w /sys/kernel/mm/ksm/run - - - - 1

当 KSM 启用,而正好有准备进行归并的页,(比如,至少有两个相似的 VM 正在运行),那么 /sys/kernel/mm/ksm/pages_shared 应会是非零的。参阅 https://www.kernel.org/doc/html/latest/admin-guide/mm/ksm.html 了解更多信息。

提示:查看KSM性能的一种简单方法是列出此目录下的所有文件内容:
$ grep -r . /sys/kernel/mm/ksm/

多屏支持[编辑 | 编辑源代码]

Linux的QXL驱动支持默认支持四头(虚拟屏幕),可以通过qxl.heads=N这一内核参数进行变更。

QXL设备的默认VGA内存大小为16M(VRAM大小为64M),在想使用两块1920x1200显示器时(需要2 × 1920 × 4 (色深) × 1200 = 17.6 MiB VGA内存)是不够用的。用-vga none -device qxl-vga,vgamem_mb=32选项代替-vga qxl可以对该属性进行变更。如果你将vgamem_mb增大至64M以上,则还需要增大vram_size_mb选项的值。

自定义显示器分辨率[编辑 | 编辑源代码]

可以使用 -device VGA,edid=on,xres=1280,yres=720 来设置自定义显示器分辨率 (参见 EDIDdisplay resolution) 。

复制和粘贴[编辑 | 编辑源代码]

在宿主机和客户机之间共享剪贴板的方法之一是使用SPICE远程桌面协议,通过SPICE客户端访问客户机,你需要遵照#SPICE节中描述的步骤,通过该方式运行的客户机将支持与宿主机进行复制粘贴的操作。

SPICE[编辑 | 编辑源代码]

在主机和虚拟机之间共享剪贴板的一种方法是启用 SPICE 远程桌面协议,并使用 SPICE 客户端访问客户端。

请按照 #SPICE 中描述的步骤进行。以这种方式运行的虚拟机将支持与主机的复制粘贴功能。

qemu-vdagent[编辑 | 编辑源代码]

QEMU 提供了其自己的 spice vdagent 字符设备实现,称为 qemu-vdagent。它与 spice-vdagent 客户端服务接口,可以实现虚拟机和主机之间的剪贴板共享。

要使用 QEMU 的 GTK 显示器访问此共享剪贴板,你需要从源码编译 QEMU ,并使用 --enable-gtk-clipboard 配置参数。这需要替换已安装的 qemu-ui-gtk 包。

注意:

添加以下的 QEMU 命令行参数:

-device virtio-serial,packed=on,ioeventfd=on
-device virtserialport,name=com.redhat.spice.0,chardev=vdagent0
-chardev qemu-vdagent,id=vdagent0,name=vdagent,clipboard=on,mouse=off

这些参数如果转换为 libvirt 格式也是有效的。

注意: 虽然 spicevmc 字符设备会自动启动虚拟机的 spice-vdagent 服务,但 qemu-vdagent 字符设备可能不会。

在 Linux 虚拟机上,你可以手动启动 spice-vdagent.service 用户单元。在 Windows 虚拟机上,请将 spice-agent 的启动类型设置为自动。

Windows专用说明[编辑 | 编辑源代码]

QEMU可以运行从 Windows 95 至 Windows 11 的任何版本 Windows。

在QEMU中也可以运行 Windows PE

快速启动设置[编辑 | 编辑源代码]

注意: 改变电源设置需要一个 Administrtor 账户

根据此论坛帖子所解释的那样,对于Windows8及之后的客户机最好在Control Panel的Power Options中禁用"Turn on fast startup (recommended)",该设置导致客户机在每次启动时卡死。

为了正确应用对 -smp 选项进行的更改,可能也需要禁用快速启动。

远程桌面协议[编辑 | 编辑源代码]

如果你使用一个 MS Windows 客户机,可能会想使用 RDP 连接到客户机 VM。若你正使用 VLAN 或与客户机处于同一个网络中,先使用:

$ qemu-system-x86_64 -nographic -nic user,hostfwd=tcp::5555-:3389

接着通过 rdesktopfreerdp 连接到客户机。例如:

$ xfreerdp -g 2048x1152 localhost:5555 -z -x lan

在物理设备上克隆一个已安装的Linux系统[编辑 | 编辑源代码]

安装在物理设备上的Linux系统可以克隆至QEMU VM中运行,参阅Clone Linux system from hardware for QEMU virtual machine

从 x86_64 环境中 Chroot 至 arm/arm64 环境[编辑 | 编辑源代码]

有时候相比于在基于ARM架构的设备上工作,在磁盘镜像上进行工作更容易。可以通过挂载一个root分区的SD卡/存储设备,然后chroot到该设备实现该目的。

另一个使用 ARM chroot 的场景是在 x86_64 机器上构建 ARM 包。这种情况下,可以使用来自 Arch Linux ARM 的镜像包来创建 chroot 环境 - 具体操作可查阅 [2]

无论哪种方式,都应该可以从chroot运行pacman并安装更多软件包,编译大型库等。由于可执行文件是针对ARM体系结构的,因此需要QEMU转换为x86。

在 x86_64 宿主机上安装 binfmt-qemu-static,然后 qemu-user-static-binfmt 将负责 qemu 二进制文件注册至 binfmt 服务的部分。

需要使用 qemu-user-static 执行其他架构上的程序,这个包与 qemu-emulators-full 提供的程序很相似,只是对于 chroot 来说必须使用 "static" 变种。比如:

qemu-arm-static path_to_sdcard/usr/bin/ls
qemu-aarch64-static path_to_sdcard/usr/bin/ls

这两个命令分别用于执行 32 位 ARM 和 64ARM 的 {{ic|ls} }命令。注意如果没有 chroot,这些都不会生效,因为那样的话它们将会试图寻找不存在宿主机系统上的一些二进制文件。

qemu-user-static 允许自动地用为 ARM 可执行文件添加 qemu-arm-staticqemu-aarch64-static 前缀。

检查一下 ARM 可执行的支持是否被开启了:

$ ls /proc/sys/fs/binfmt_misc
qemu-aarch64  qemu-arm	  qemu-cris  qemu-microblaze  qemu-mipsel  qemu-ppc64	    qemu-riscv64  qemu-sh4    qemu-sparc	qemu-sparc64  status
qemu-alpha    qemu-armeb  qemu-m68k  qemu-mips	      qemu-ppc	   qemu-ppc64abi32  qemu-s390x	  qemu-sh4eb  qemu-sparc32plus	register

每种可执行的文件都须在此处列出。

如果未开启,请重启 systemd-binfmt.service

将SD卡挂载至 /mnt/sdcard(设备名可能不同):

# mount --mkdir /dev/mmcblk0p2 /mnt/sdcard

如有需要,挂载启动分区(同样,使用合适的设备名):

# mount /dev/mmcblk0p1 /mnt/sdcard/boot

最后如Change root#Using chroot所描述的那样,chroot 到 SD 卡的 root 中:

# chroot /mnt/sdcard /bin/bash

此外,可以使用 arch-install-scripts 提供的 arch-chroot 替代 chroot,可以方便地获取网络支持:

# arch-chroot /mnt/sdcard /bin/bash

还可以用 systemd-nspawn chroot 到 ARM 环境中:

# systemd-nspawn -D /mnt/sdcard -M myARMMachine --bind-ro=/etc/resolv.conf

--bind-ro=/etc/resolv.conf 是可选的,其在 chroot 中提供了一个可用的网络 DNS。

在 chroot 环境中使用 sudo[编辑 | 编辑源代码]

如果您在 chroot 环境中安装了 sudo,并在尝试使用它时收到以下错误:

sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?

那么您可能需要修改 binfmt 标志,例如对于 aarch64:

# cp /usr/lib/binfmt.d/qemu-aarch64-static.conf /etc/binfmt.d/
# vi /etc/binfmt.d/qemu-aarch64-static.conf

并在该文件末尾添加一个 C

:qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64-static:FPC

然后重新启动 systemd-binfmt.service 并检查更改是否生效(注意标志行上的 C):

# cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags: POCF
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff

有关更多信息,请参阅内核 binfmt 文档的“flags”部分。

处理常见问题[编辑 | 编辑源代码]

本文或本章节可能需要合并到QEMU/Troubleshooting

附注: This section is long enough to be split into a dedicated subpage.(在 Talk:QEMU 中讨论)

Not grabbing mouse input[编辑 | 编辑源代码]

本文或本章节的语言、语法或风格需要改进。参考:帮助:风格

原因:It is not explained what the option actually does. Is it causing or avoiding the side effect?(在Talk:QEMU讨论)

Tablet mode has side effect of not grabbing mouse input in QEMU window:

-usb -device usb-tablet

It works with several -vga backends one of which is virtio.

鼠标指针抖动或者不稳定[编辑 | 编辑源代码]

如果QEMU中鼠标指针不受控制地跳来跳去,那么启动QEMU之前在命令进行如下设置或许能解决该问题:

$ export SDL_VIDEO_X11_DGAMOUSE=0

如果成功了,可以把这条命令加入到你的 ~/.bashrc 文件中

看不见鼠标指针[编辑 | 编辑源代码]

使用-show-cursor选项启动QEMU以显示指针。

如果这么做不起作用的话,请确保你的显示设备被正确设置了。例如: -vga qxl

还可尝试 #鼠标整合中提到的 -usb -device usb-tablet。该选项会覆盖默认采用的PS/2鼠标仿真,将鼠标作为外设在宿主机和客户机之间同步指针位置。

有两个不同的鼠标指针[编辑 | 编辑源代码]

遵循 #鼠标整合提到的方法。

使用 VNC 时键盘出现问题[编辑 | 编辑源代码]

使用 VNC 时,您可能会遇到这里所描述的键盘问题。 解决的方法是不使用 QEMU 的 -k 选项, 并使用 gtk-vncgvncviewer 。另请参见在libvirt邮件列表中发布的消息

键盘像坏了一样或者方向键不起作用[编辑 | 编辑源代码]

如果发现某些键不起作用或按下一个按键,触发的却是其他按键,那么你可能需要指定键盘布局选项。在 /usr/share/qemu/keymaps 中可以找到键盘布局选项。

$ qemu-system-x86_64 -k keymap disk_image

无法读取键盘映射文件[编辑 | 编辑源代码]

qemu-system-x86_64: -display vnc=0.0.0.0:0: could not read keymap file: 'en'

这类问题产生的原因是将非法的键盘映射名作为-k的参数传递给qemu。例如,en是错误的键盘映射名称,en-us才是正确的。相关信息参见/usr/share/qemu/keymaps

客户机在调整窗口大小时一起被拉伸了[编辑 | 编辑源代码]

按下 Ctrl+Alt+u 可以恢复到原来的大小。

ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy[编辑 | 编辑源代码]

如果在以 -enable-kvm 选项启动QEMU时显示了这样一条错误信息:

ioctl(KVM_CREATE_VM) failed: 16 Device or resource busy
failed to initialize KVM: Device or resource busy

这意味着还有另一个 hypervisor 处于运行状态, 不推荐同时运行多个hypervisor。

libgfapi 的错误信息[编辑 | 编辑源代码]

启动时若显示如下错误信息:

Failed to open module: libgfapi.so.0: cannot open shared object file: No such file or directory

安装 glusterfs 或者直接忽略这个错误信息,GlusterFS 只是一个可选的依赖。

LIVE-environments 上发生内核错误[编辑 | 编辑源代码]

如果你启动一个 live-environment(或者启动一个系统),可能会遭遇如下错误:

[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown block(0,0)

或者其他阻碍启动过程的东西(比如无法解包initramfs, 无法启动foo服务之类的),试试启动VM的时候用 -m VALUE 分配合适的 RAM 大小,如果内存太小是很有可能遭遇上述的问题的。

Windows 7 客户机的音频质量差[编辑 | 编辑源代码]

为Windows7使用 hda 音频驱动可能造成音频质量降低, 通过QEMU的 -soundhw ac97 参数选择 ac97 作为音频驱动, 并从Realtek AC'97 Audio Codecs下载AC97驱动,然后在客户机中安装该驱动可能会解决这个问题。参见Red Hat Bugzilla – Bug 1176761以获取更多信息。

Could not access KVM kernel module: Permission denied[编辑 | 编辑源代码]

如果遇见了如下的错误信息:

libvirtError: internal error: process exited while connecting to monitor: Could not access KVM kernel module: Permission denied failed to initialize KVM: Permission denied

这是因为 Systemd 234 为 kvm 组注册了一个动态ID (参见FS#54943)。可以编辑 /etc/libvirt/qemu.confgroup = "78" 改为 group = "kvm" 避免产生这个错误。

启动 Windows VM 时产生 "System Thread Exception Not Handled"[编辑 | 编辑源代码]

Windows 8或Windows 10 guest虚拟机在启动时可能会引发通用兼容性异常,即“System Thread Exception Not Handled”,这通常是由实体机上旧版驱动程序的异常行为引起的。在KVM机器上,通常可以通过将CPU模型设置为 core2duo来解决此问题。

某些 Windows 游戏/程序导致蓝屏出现[编辑 | 编辑源代码]

有时,一些能在物理机上正常运行的程序在虚拟机中会意外地崩溃。如果在以 root 运行 dmesg -wH 时发现有 MSR 相关的错误,则导致这些崩溃的原因是 KVM 注入了 GPF(General protection fault),当客户机尝试访问不受支持的寄存器(MSR, Model-specific registers)时会导致客户机上的应用程序崩溃。将 ignore_msrs=1 传给 KVM 模块可以使其忽略不支持的 MSR, 从而解决大部分的同类问题。

/etc/modprobe.d/kvm.conf
...
options kvm ignore_msrs=1
...

一些添加该选项可能会起作用的场景:

  • GeForce Experience 反映没有可支持的CPU。
  • 星际争霸2和黑色洛城使用 KMODE_EXCEPTION_NOT_HANDLED 对 Windows 10 的蓝屏做了一些处理,蓝屏信息中将无法识别驱动。
警告: 虽然通常这么做是安全的,对于某些应用程序也可能不起作用,但默默忽略未知的 MSR 仍可能破坏 VM 或其他 VM 中的其他软件。

VM 中的应用程序有很高的延迟,或是需要等待很长的时间才会启动[编辑 | 编辑源代码]

本文内容或本节内容已经过期。

原因: No longer true since kernel 5.6 (在Talk:QEMU讨论)

这可能是由于VM中的可用熵不足所致。考虑通过将 VirtIO RNG 设备添加到 VM,或安装诸如 Haveged 之类的熵生成守护程序。

有趣的是,OpenSSH 需要一段时间才能在熵不足的情况下开始接受连接,而其日志却不会显示原因。

中断时间过长以及运行不流畅[编辑 | 编辑源代码]

此问题表现为小停顿(断断续续),在图形密集型应用程序(例如游戏)中尤其明显。

QXL 导致视频分辨率降低[编辑 | 编辑源代码]

QEMU 4.1.0版本带来了一个回退问题,当使用SPICE时,会将QXL视频降到低分辨率。 [3]例如,当KMS启动时,文本分辨率可能会低至4x10个字符。尝试提高GUI分辨率时,它可能会达到最低的支持分辨率。

解决方法是,以这种形式创建设备:

-device qxl-vga,max_outputs=1...

使用启用了安全启动的OVMF时,VM无法启动[编辑 | 编辑源代码]

edk2-ovmf 中的 OVMF_CODE.secboot.4m.fd 以及 OVMF_CODE.secboot.fd 文件是在 SMM 的支持下完成构建的,如果未在 VM 中禁用 S3 支持,则 VM 可能根本无法启动。

在QEMU命令中添加 -global ICH9-LPC.disable_s3=1 以解决该问题。

参见 FS#59465 以及 https://github.com/tianocore/edk2/blob/master/OvmfPkg/README 获取 QEMU 使用 Secure Boot 所需要的更多选项。

Virtual machine not booting into Arch ISO[编辑 | 编辑源代码]

When trying to boot the virtual machine for the first time from an Arch ISO image, the boot process hangs. Adding console=ttyS0 to kernel boot options by pressing e in the boot menu you will get more boot messages and the following error:

:: Mounting '/dev/disk/by-label/ARCH_202204' to '/run/archiso/bootmnt'
Waiting 30 seconds for device /dev/disk/by-label/ARCH_202204 ...
ERROR: '/dev/disk/by-label/ARCH_202204' device did not show up after 30 seconds...
   Falling back to interactive prompt
   You can try to fix the problem manually, log out when you are finished
sh: can't access tty; job control turned off

The error message does not give a good clue as to what the real issue is. The problem is with the default 128MB of RAM that QEMU allocates to the virtual machine. Increasing the limit to 1024MB with -m 1024 solves the issue and lets the system boot. You can continue installing Arch Linux as usual after that. Once the installation is complete, the memory allocation for the virtual machine can be decreased. The need for 1024MB is due to RAM disk requirements and size of the installation media. See this message on the arch-releng mailing list and this forum thread.

客户机上的CPU中断没有被触发[编辑 | 编辑源代码]

如果你是按照 OSDev Wiki 来编写自己的操作系统,或者只是使用 QEMU 的 gdb 接口和 -s 标志,您需要知道的是,很多仿真器(包括QEMU)通常会实现一些CPU中断,同时还有许多硬件中断没有被实现。了解代码是否触发中断的一种方法是使用:

-d int

可以在输出中查看 interrupts/exceptions。

如要查看QEMU还提供了什么客户机调试功能,使用如下命令:

qemu-system-x86_64 -d help

或者将 x86_64 替换为你选择的其他架构

KDE with sddm does not start spice-vdagent at login automatically[编辑 | 编辑源代码]

Remove or comment out X-GNOME-Autostart-Phase=WindowManager from /etc/xdg/autostart/spice-vdagent.desktop. [4]

Error starting domain: Requested operation is not valid: network 'default' is not active[编辑 | 编辑源代码]

If for any reason the default network is deactivated, you will not be able to start any guest virtual machines which are configured to use the network. Your first attempt can be simply trying to start the network with virsh.

# virsh net-start default

For additional troubleshooting steps, see [5].

参阅[编辑 | 编辑源代码]