QEMU
根据 QEMU 关于页面,“QEMU 是一个通用的开源机器模拟器和虚拟化器。”
当用作机器模拟器时,QEMU 可以在不同的机器(例如您的 x86 PC)上运行为一台机器(例如 ARM 板)制作的操作系统和程序。通过使用动态转换,它可以实现非常好的性能。
QEMU 可以使用其他虚拟机监控程序(如 Xen 或 KVM)来使用 CPU 扩展 (HVM) 进行虚拟化。当用作虚拟化器时,QEMU 通过直接在主机 CPU 上执行 guest 代码来实现接近原生性能的性能。
安装
安装 qemu-full 软件包(或者对于没有 GUI 的版本,安装 qemu-base,对于默认仅包含 x86_64 模拟的版本,安装 qemu-desktop),以及以下可选软件包以满足您的需求
- qemu-block-gluster - Glusterfs 块支持
- qemu-block-iscsi - iSCSI 块支持
- samba - SMB/CIFS 服务器支持
或者,qemu-user-static 作为用户模式和静态变体存在。
QEMU 变体
QEMU 提供多种变体,以适应不同的用例。
作为第一个分类,QEMU 提供全系统和用户模式模拟模式
- 全系统模拟
- 在这种模式下,QEMU 模拟一个完整的系统,包括一个或多个处理器和各种外围设备。它更准确但更慢,并且不要求模拟的操作系统是 Linux。
- 全系统模拟的 QEMU 命令命名为
qemu-system-target_architecture
,例如,qemu-system-x86_64
用于模拟 x86_64 CPU,qemu-system-i386
用于 Intel 32 位 x86 CPU,qemu-system-arm
用于 ARM (32 位),qemu-system-aarch64
用于 ARM64 等。 - 如果目标架构与主机 CPU 匹配,则此模式仍然可以通过使用虚拟机监控程序(如 KVM 或 Xen)获得显着的加速。
- 用户模式模拟
- 在这种模式下,QEMU 能够通过利用主机系统资源来调用为(可能)不同架构编译的 Linux 可执行文件。可能存在兼容性问题,例如,某些功能可能未实现,动态链接的可执行文件无法开箱即用(请参阅 #从 x86_64 环境 chroot 到 arm/arm64 环境 以解决此问题),并且仅支持 Linux(尽管 Wine 可能用于运行 Windows 可执行文件)。
- 用户模式模拟的 QEMU 命令命名为
qemu-target_architecture
,例如,qemu-x86_64
用于模拟 64 位 CPU。
QEMU 提供动态链接和静态链接的变体
- 动态链接(默认)
qemu-*
命令依赖于主机操作系统库,因此可执行文件更小。- 静态链接
qemu-*
命令可以复制到任何具有相同架构的 Linux 系统。
在 Arch Linux 的情况下,全系统模拟作为以下形式提供
- 非 Headless(默认)
- 此变体启用需要额外依赖项(如 SDL 或 GTK)的 GUI 功能。
- Headless
- 这是一个更精简的变体,不需要 GUI(例如,适用于服务器)。
请注意,headless 和 non-headless 版本安装的命令名称相同(例如 qemu-system-x86_64
),因此不能同时安装。
关于 Arch Linux 中可用软件包的详细信息
- qemu-desktop 软件包为全系统模拟提供
x86_64
架构模拟器 (qemu-system-x86_64
)。qemu-emulators-full 软件包提供x86_64
用户模式变体 (qemu-x86_64
),并且对于其余支持的架构,它包括全系统和用户模式变体(例如qemu-system-arm
和qemu-arm
)。 - 这些软件包的 headless 版本(仅适用于全系统模拟)是 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。
QEMU 的图形前端
与其他虚拟化程序(如 VirtualBox 和 VMware)不同,QEMU 不提供 GUI 来管理虚拟机(虚拟机运行时出现的窗口除外),也不提供创建具有已保存设置的持久虚拟机的方法。除非您创建了自定义脚本来启动虚拟机,否则必须在每次启动时在命令行上指定运行虚拟机的全部参数。
Libvirt 提供了一种管理 QEMU 虚拟机的便捷方法。有关可用的前端,请参阅 libvirt 客户端列表。
创建一个新的虚拟化系统
创建硬盘镜像
要运行 QEMU,您需要一个硬盘镜像,除非您从 CD-ROM 或网络启动实时系统(并且不是为了将操作系统安装到硬盘镜像而这样做)。硬盘镜像是一个文件,用于存储模拟硬盘的内容。
硬盘镜像可以是 raw 格式,因此它在字节级别上与 guest 所见内容相同,并且始终在主机上使用 guest 硬盘驱动器的全部容量。此方法提供的 I/O 开销最小,但可能会浪费大量空间,因为 guest 上未使用的空间无法在主机上使用。
或者,硬盘镜像可以是 qcow2 等格式,这种格式仅在 guest 操作系统实际写入其虚拟硬盘上的扇区时才为镜像文件分配空间。镜像在 guest 操作系统中显示为完整大小,即使它可能仅占用主机系统上非常小的空间。此镜像格式还支持 QEMU 快照功能(有关详细信息,请参阅 #通过监视器控制台创建和管理快照)。但是,使用此格式代替 raw 可能会影响性能。
QEMU 提供 qemu-img
命令来创建硬盘镜像。例如,要创建 4 GiB 的 raw 格式镜像
$ qemu-img create -f raw image_file 4G
您可以改用 -f qcow2
创建 qcow2 磁盘。
dd
或 fallocate
创建所需大小的文件来简单地创建 raw 镜像。$ qemu-img create -f qcow2 image_file -o nocow=on 4G
覆盖存储镜像
您可以创建一个存储镜像(“backing”镜像),并让 QEMU 将对此镜像的更改保存在覆盖镜像中。这允许您恢复到此存储镜像的先前状态。您可以在您希望恢复时,基于原始 backing 镜像创建一个新的覆盖镜像来进行恢复。
要创建覆盖镜像,请发出如下命令
$ qemu-img create -o backing_file=img1.raw,backing_fmt=raw -f qcow2 img1.cow
之后,您可以像往常一样运行您的 QEMU 虚拟机(请参阅 #运行虚拟化系统)
$ qemu-system-x86_64 img1.cow
backing 镜像将保持完整,并且对此存储的更改将记录在覆盖镜像文件中。
当 backing 镜像的路径更改时,需要修复。
确保原始 backing 镜像的路径仍然指向此镜像。如有必要,在原始路径处创建一个指向新路径的符号链接。然后发出如下命令
$ qemu-img rebase -b /new/img1.raw /new/img1.cow
您可以自行决定是否执行“不安全”的 rebase,其中不检查 backing 镜像的旧路径
$ qemu-img rebase -u -b /new/img1.raw /new/img1.cow
调整镜像大小
qemu-img
可执行文件具有 resize
选项,可以轻松调整硬盘驱动器镜像的大小。它适用于 raw 和 qcow2。例如,要将镜像空间增加 10 GiB,请运行
$ qemu-img resize disk_image +10G
扩大磁盘镜像后,您必须使用虚拟机内的文件系统和分区工具来实际开始使用新空间。
缩小镜像
缩小磁盘镜像时,您必须首先使用虚拟机内的文件系统和分区工具减小已分配的文件系统和分区大小,然后相应地缩小磁盘镜像。对于 Windows guest,可以从“创建并格式化硬盘分区”控制面板执行此操作。
然后,要将镜像空间减小 10 GiB,请运行
$ qemu-img resize --shrink disk_image -10G
转换镜像
您可以使用 qemu-img convert
将镜像转换为其他格式。此示例显示如何将 raw 镜像转换为 qcow2
$ qemu-img convert -f raw -O qcow2 input.img output.qcow2
这不会删除原始输入文件。
准备安装介质
要将操作系统安装到您的磁盘镜像中,您需要操作系统的安装介质(例如光盘、USB 驱动器或 ISO 镜像)。安装介质不应挂载,因为 QEMU 直接访问介质。
/dev/cdrom
,您可以使用以下命令将其转储到文件中$ dd if=/dev/cdrom of=cd_image.iso bs=4k
安装操作系统
这是您第一次需要启动模拟器。要在磁盘镜像上安装操作系统,您必须将磁盘镜像和安装介质都附加到虚拟机,并使其从安装介质启动。
例如,在 i386 guest 上,要从可启动 ISO 文件作为 CD-ROM 和原始磁盘镜像安装
$ qemu-system-x86_64 -cdrom iso_image -boot order=d -drive file=disk_image,format=raw
有关加载其他介质类型(如软盘、磁盘镜像或物理驱动器)的更多信息,请参阅 qemu(1),有关其他有用的选项,请参阅 #运行虚拟化系统。
操作系统安装完成后,可以直接启动 QEMU 镜像(请参阅 #运行虚拟化系统)。
-m
开关调整内存量,例如 -m 512M
或 -m 2G
。- 一些用户可能更喜欢使用启动菜单而不是指定
-boot order=x
:-boot menu=on
,至少在配置和实验期间是这样。 - 在 headless 模式下运行 QEMU 时,它默认在端口 5900 上启动本地 VNC 服务器。您可以使用 TigerVNC 连接到 guest 操作系统:
vncviewer :5900
- 如果需要在安装过程中更换软盘或 CD,您可以使用 QEMU 机器监视器(在虚拟机的窗口中按
Ctrl+Alt+2
)来删除和附加存储设备到虚拟机。键入info block
以查看块设备,并使用change
命令来更换设备。按Ctrl+Alt+1
返回虚拟机。
预制虚拟机镜像
在许多情况下,手动安装自己的操作系统不是必需的或不希望的,例如在云环境中。幸运的是,许多预制镜像可以从不同的提供商处下载。
对于 Arch Linux,我们有 arch-boxes 项目,其中包含 每周镜像发布。
运行虚拟化系统
qemu-system-*
二进制文件(例如 qemu-system-i386
或 qemu-system-x86_64
,取决于 guest 的架构)用于运行虚拟化 guest。用法是
$ qemu-system-x86_64 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
。
-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 工具启动虚拟机并遇到非常糟糕的性能,则应检查是否正确支持 KVM,因为 QEMU 可能会回退到软件模拟。
- 需要启用 KVM 才能正确启动 Windows 7 或 Windows 8,而不会出现蓝屏。
启用 IOMMU (Intel VT-d/AMD-Vi) 支持
首先启用 IOMMU,请参阅 通过 OVMF 的 PCI 直通#设置 IOMMU。
添加 -device intel-iommu
以创建 IOMMU 设备
$ qemu-system-x86_64 -enable-kvm -machine q35 -device intel-iommu -cpu host ..
-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 系统闪存。
OVMF 是一个 TianoCore 项目,旨在为虚拟机启用 UEFI 支持。可以使用 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.4m.fd
。
第二个要求是使用安装了安全启动密钥的 OVMF_VARS 文件,而上游项目[1]未提供该文件。
与某些其他发行版不同,Arch 尚未提供(截至 2024-12-06)包含预先注册密钥的自己的 /usr/share/edk2/x64/OVMF_VARS.secboot.4m.fd
文件:请参阅 archlinux/packaging/packages/edk2#1。
一个简单的解决方法是使用 Fedora 的版本,如 此论坛帖子中所述
- 从 构建列表下载 Fedora 的 edk2-ovmf 的最新 noarch RPM 包,适用于 x86_64(截至 2024-12-06 的 F42 版本的直接链接)
- 提取归档文件并将所需文件转换为合适的格式[2]
$ qemu-img convert -O raw -f qcow2 edk2-ovmf-*.noarch/usr/share/edk2/ovmf/OVMF_VARS_4M.secboot.qcow2 OVMF_VARS_4M.secboot.fd
现在您可以创建已创建的 OVMF_VARS_4M.secboot.fd
的副本,并将它们用于启用安全启动的虚拟机。
另请参阅 KVM#安全启动,了解如何手动将密钥注册到固件镜像中。
可信平台模块模拟
QEMU 可以模拟 可信平台模块,某些系统(如 Windows 11(需要 TPM 2.0))需要它。
安装 swtpm 包,它提供了软件 TPM 实现。创建一个目录来存储 TPM 数据(将使用 /path/to/mytpm
作为示例)。运行以下命令启动模拟器
$ swtpm socket --tpm2 --tpmstate dir=/path/to/mytpm --ctrl type=unixio,path=/path/to/mytpm/swtpm-sock
/path/to/mytpm/swtpm-sock
将由 swtpm 创建:这是一个 QEMU 将连接到的 UNIX 套接字。您可以将其放在任何目录中。
默认情况下,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 将自动终止。
有关更多信息,请参阅 QEMU 文档。
如果客户操作系统仍然无法识别 TPM 设备,请尝试调整CPU 型号和拓扑选项。这可能会导致问题。
主机和虚拟机之间的通信
网络
可以使用任何可以传输文件的网络协议在主机和虚拟机操作系统之间共享数据,例如 NFS、SMB、NBD、HTTP、FTP 或 SSH,前提是您已正确设置网络并启用了相应的服务。
默认的基于 SLIRP 的用户模式网络允许虚拟机通过 IP 地址 10.0.2.2 访问主机操作系统。您在主机操作系统上运行的任何服务器(例如 SSH 服务器或 SMB 服务器)都将可以通过此 IP 地址访问。因此,在虚拟机上,您可以挂载通过 SMB 或 NFS 在主机上导出的目录,或者您可以访问主机的 HTTP 服务器等。主机操作系统将无法访问在虚拟机操作系统上运行的服务器,但这可以通过其他网络配置来实现(请参阅 #使用 TAP 设备进行网络连接)。
QEMU 的端口转发
QEMU 可以转发从主机到虚拟机的端口,以实现例如从主机连接到在虚拟机上运行的 SSH 服务器。
例如,要将主机上的端口 60022 绑定到虚拟机上的端口 22 (SSH),请使用如下命令启动 QEMU
$ qemu-system-x86_64 disk_image -nic user,hostfwd=tcp::60022-:22
确保 sshd 在虚拟机上运行,并使用以下命令连接
$ 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
通过 vsock 访问 SSH
连接到虚拟机的一种安全便捷的方法是通过 vsock(7) 使用 SSH。您的虚拟机需要是基于 systemd 的才能开箱即用。
首先,使用特殊设备启动 QEMU
-device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=555
cid
需要用户选择一个有效的 32 位数字(请参阅 vsock(7))。当 systemd 检测到虚拟机已使用 vhost-vsock
设备启动时,它将通过 systemd-ssh-generator
自动启动 SSH 服务器。
然后,您可以像这样连接到虚拟机
$ ssh user@vsock/555
这之所以有效,是因为 /etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf
告诉您的 SSH 客户端使用 systemd-ssh-proxy
以允许 SSH 使用 vsock。
此外,使用 systemd.system-credentials(7) 我们可以为 root
用户注入一个 authorized keys 文件,如果您尝试运行下载的镜像,这将非常方便。可以像这样完成
-smbios type=11,value=io.systemd.credential.binary:ssh.authorized_keys.root=c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSU9sVFE4ejlpeWxoMTMreCtFVFJ1R1JEaHpIVVRnaCt2ekJLOGY3TEl5eTQ=
公钥行必须作为 base64 编码的字符串提供。可以像这样完成
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOlTQ8z9iylh13+x+ETRuGRDhzHUTgh+vzBK8f7LIyy4" | base64
相同的机制,通过 -smbios type=11,value=io.systemd...
,可以用于注入各种其他神奇的变量,这些变量将被 systemd 操作。另请参阅 systemd 文档:系统和服务凭据。
QEMU 的内置 SMB 服务器
QEMU 的文档说它有一个“内置” SMB 服务器,但实际上它只是在主机上启动 Samba,并在 /tmp/qemu-smb.random_string
中生成一个自动生成的 smb.conf
文件,并使其可以通过不同的 IP 地址(默认情况下为 10.0.2.4)供虚拟机访问。这仅适用于用户网络连接,并且当您不想在主机上启动正常的 Samba 服务时非常有用,如果您在主机上设置了共享,虚拟机也可以访问该服务。
只能使用选项 smb=
设置单个目录作为共享目录,但是如果 QEMU 配置 SMB 以跟随符号链接,则添加更多目录(即使在虚拟机运行时)可能就像在共享目录中创建符号链接一样容易。它没有这样做,但是可以更改正在运行的 SMB 服务器的配置,如下所述。
Samba 必须安装在主机上。要启用此功能,请使用如下命令启动 QEMU
$ qemu-system-x86_64 -nic user,id=nic0,smb=shared_dir_path disk_image
其中 shared_dir_path
是您要在虚拟机和主机之间共享的目录。
然后,在虚拟机中,您将能够通过共享名称“qemu”在主机 10.0.2.4 上访问共享目录。例如,在 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
,则它只会共享最后定义的一个。 - 如果您无法访问共享文件夹并且客户操作系统是 Windows,请检查是否启用了 NetBIOS 协议,以及防火墙是否阻止了 NetBIOS 协议使用的端口。
- 如果您无法访问共享文件夹并且客户操作系统是 Windows 10 企业版或教育版或 Windows Server 2016,请启用来宾访问。
- 如果您使用 #使用 TAP 设备进行网络连接,请使用
-device virtio-net,netdev=vmnic -netdev user,id=vmnic,smb=shared_dir_path
获取 SMB。
在虚拟机运行时共享多个目录并在其中添加或删除目录的一种方法是共享一个空目录,并在共享目录中创建/删除指向目录的符号链接。为了使其工作,可以使用以下脚本更改正在运行的 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
的形式提供。
使用 9pfs VirtFS 进行主机文件共享
请参阅 QEMU 文档。
使用 virtiofsd 进行主机文件共享
virtiofsd
随 virtiofsd 包一起提供。这是一种现代且高性能的方式,可以方便地在主机和虚拟机之间共享文件。有关可用选项的完整列表,请参阅 在线文档 或 /usr/share/doc/virtiofsd/README.md
。
您可以选择以 root 用户或普通用户身份运行 virtiofsd。
以普通用户身份运行 virtiofsd
首先,确保将执行 virtiofsd 的用户具有 subuid(5) 和 subgid(5) 配置条目。另请参阅 Podman 文章中的相关部分。
然后,启动 virtiofsd
$ unshare -r --map-auto -- /usr/lib/virtiofsd --socket-path=/tmp/vm-share.sock --shared-dir /tmp/vm-share --sandbox chroot
unshare -r
导致其后的命令在新用户命名空间中启动,当前用户在该新命令中映射到 root。这很重要,因为 virtiofsd 期望从其角度来看以 root 身份运行。/tmp/vm-share.sock
是一个套接字文件/tmp/vm-share
是主机和虚拟机之间的共享目录
以 root 用户身份运行 virtiofsd
将运行 QEMU 的用户添加到 kvm
用户组,因为它需要访问 virtiofsd 套接字。您可能需要注销才能使更改生效。
以 root 用户身份启动 virtiofsd
# /usr/lib/virtiofsd --socket-path=/tmp/vm-share.sock --shared-dir /tmp/vm-share
其中
/tmp/vm-share.sock
是一个套接字文件/tmp/vm-share
是主机和虚拟机之间的共享目录
创建的套接字文件仅具有 root 访问权限。使用以下命令为组 kvm
提供对其的访问权限
# chgrp kvm /tmp/vm-share.sock; chmod g+rxw /tmp/vm-share.sock
启动 QEMU
启动虚拟机时添加以下配置选项
-m 4G -object memory-backend-memfd,id=mem,size=4G,share=on -numa node,memdev=mem \ -chardev socket,id=char0,path=/tmp/vm-share.sock -device vhost-user-fs-pci,chardev=char0,tag=myfs
其中
size=4G
必须与-m 4G
选项指定的尺寸匹配/tmp/vm-share.sock
指向之前启动的套接字文件myfs
是一个标识符,您稍后将在虚拟机中使用它来挂载共享
以 root 身份登录虚拟机后,您只需在任何现代发行版上挂载共享即可
# mount -t virtiofs myfs /mnt
此目录现在应在主机和虚拟机之间共享。
请参阅 相关的 Windows 部分。
在主机上挂载虚拟机的分区
在主机系统下挂载驱动器镜像可能很有用,它可以作为进出虚拟机传输文件的一种方式。这应在虚拟机未运行时完成。
在主机上挂载驱动器的过程取决于 qemu 镜像的类型,raw 或 qcow2。我们将在 #从 raw 镜像挂载分区 和 #从 qcow2 镜像挂载分区 中详细说明以两种格式挂载驱动器的步骤。有关完整文档,请参阅 Wikibooks:QEMU/Images#在主机上挂载镜像。
从 raw 镜像挂载分区
可以通过将 raw 磁盘镜像文件中的分区设置为环回设备来挂载它们。
手动指定字节偏移量
挂载磁盘镜像分区的一种方法是使用如下命令在特定偏移量挂载磁盘镜像
# mount -o loop,offset=32256 disk_image mountpoint
offset=32256
选项实际上是传递给 losetup
程序的,以设置一个环回设备,该设备从文件的字节偏移量 32256 处开始并持续到结尾。然后挂载此环回设备。您也可以使用 sizelimit
选项来指定分区的确切大小,但这通常是不必要的。
根据您的磁盘镜像,所需的分区可能不是从偏移量 32256 开始的。运行 fdisk -l disk_image
以查看镜像中的分区。fdisk 以 512 字节扇区为单位给出起始和结束偏移量,因此乘以 512 以获得传递给 mount
的正确偏移量。
使用循环模块自动检测分区
Linux 循环驱动程序实际上支持环回设备中的分区,但默认情况下禁用它。要启用它,请执行以下操作
- 摆脱所有环回设备(卸载所有已挂载的镜像等)。
- 卸载
loop
内核模块,并使用设置的max_part=15
参数加载它。此外,可以使用max_loop
参数控制环回设备的最大数量。
/etc/modprobe.d
中放置一个条目,以便每次都加载带有 max_part=15
的 loop 模块,或者您可以将 loop.max_part=15
放在内核命令行上,具体取决于您的 loop.ko
模块是否内置到您的内核中。将您的镜像设置为环回设备
# losetup -f -P disk_image
然后,如果创建的设备是 /dev/loop0
,则将自动创建其他设备 /dev/loop0pX
,其中 X 是分区号。这些分区环回设备可以直接挂载。例如
# mount /dev/loop0p1 mountpoint
要使用 udisksctl 挂载磁盘镜像,请参阅 Udisks#挂载环回设备。
使用 kpartx
来自 multipath-tools 包的 kpartx 可以读取设备上的分区表,并为每个分区创建一个新设备。例如
# kpartx -a disk_image
这将设置环回设备并在 /dev/mapper/
中创建必要的分区设备。
从 qcow2 镜像挂载分区
我们将使用 qemu-nbd
,它允许我们使用 NBD(网络块设备)协议来共享磁盘镜像。
首先,我们需要加载 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
网络
与用户模式网络或 vde 相比,使用 tap 设备和网桥的虚拟网络性能应该更好,因为 tap 设备和网桥是在内核中实现的。
此外,可以通过为虚拟机分配 virtio 网络设备而不是默认的 e1000 NIC 仿真来提高网络性能。有关更多信息,请参阅 #使用 virtio 驱动程序。
链路层地址注意事项
通过向 QEMU 提供 -net nic
参数,默认情况下,它将为虚拟机分配一个网络接口,其链路层地址为 52:54:00:12:34:56
。但是,当使用桥接网络连接多个虚拟机时,每个虚拟机在 tap 设备的虚拟机侧都必须具有唯一的链路层 (MAC) 地址。否则,网桥将无法正常工作,因为它将接收来自具有相同链路层地址的多个源的数据包。即使 tap 设备本身具有唯一的链路层地址,也会发生此问题,因为源链路层地址在数据包通过 tap 设备时不会被重写。
确保每个虚拟机都具有唯一的链路层地址,但它应始终以 52:54:
开头。使用以下选项,将 X 替换为任意十六进制数字
$ qemu-system-x86_64 -net nic,macaddr=52:54:XX:XX:XX:XX -net vde disk_image
生成唯一的链路层地址可以通过多种方式完成
- 手动为每个 NIC 指定唯一的链路层地址。好处是 DHCP 服务器将在每次虚拟机运行时分配相同的 IP 地址,但它不适用于大量虚拟机。
- 每次虚拟机运行时生成随机链路层地址。碰撞的概率实际上为零,但缺点是 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
使用哈希函数从虚拟机名称生成链路层地址。假设虚拟机名称是唯一的,则此方法结合了上述方法的优点:它在每次脚本运行时生成相同的链路层地址,但它保留了碰撞的概率实际上为零。
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
用户模式网络
SLIRP
默认情况下,在没有任何 -netdev
参数的情况下,QEMU 将使用 基于 SLIRP 的 用户模式网络,并带有内置 DHCP 服务器。当虚拟机运行其 DHCP 客户端时,将为其分配 IP 地址,并且它们将能够通过 QEMU 完成的 IP 伪装访问物理主机的网络。
Slirp: external icmpv6 not supported yet
。 Ping IPv6 地址将不起作用。此默认配置允许您的虚拟机轻松访问 Internet,前提是主机已连接到 Internet,但是虚拟机在外部网络上将不可见,如果同时启动多个虚拟机,虚拟机也无法相互通信。
QEMU 的用户模式网络可以提供更多功能,例如内置 TFTP 或 SMB 服务器,将主机端口重定向到虚拟机(例如,允许 SSH 连接到虚拟机)或将虚拟机连接到 VLAN,以便它们可以相互通信。有关更多详细信息,请参阅 QEMU 文档中关于 -net user
标志的部分。
但是,用户模式网络在实用性和性能方面都有限制。更高级的网络配置需要使用 tap 设备或其他方法。
/etc/resolv.conf
文件,否则虚拟机系统中的 DNS 查找将无法工作。- 要将 virtio 驱动程序与用户模式网络一起使用,选项是:
-nic user,model=virtio-net-pci
。 - 您可以通过添加
restrict=y
将用户模式网络与主机和外部世界隔离,例如:-net user,restrict=y
passt
用户可以选择使用 基于 passt 的 用户模式网络。passt 比 SLIRP 具有多个优势,例如更好的性能、完整的 IPv6 支持(包括 ICMPv6)、更好的安全性和更多的控制。
要开始使用,请安装 passt。有两种启动它的方法:通过基于套接字的通信或通过共享 vhost-user。后一种方法具有更好的性能。
对于基于套接字的方式,首先启动 passt
$ passt -f
然后,对于您的 QEMU 命令,添加这些参数
-device virtio-net-pci,netdev=s -netdev stream,id=s,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket
对于 vhost-user 方式,使用 --vhost-user
启动 passt
$ passt -f --vhost-user
然后,对于您的 QEMU 命令,添加这些参数
-m 4G -chardev socket,id=chr0,path=/tmp/passt_1.socket -netdev vhost-user,id=netdev0,chardev=chr0 -device virtio-net,netdev=netdev0 -object memory-backend-memfd,id=memfd0,share=on,size=4G -numa node,memdev=memfd0
请注意,-m 4G
和 size=4G
的内存大小必须完全匹配。
使用 TAP 设备进行网络连接
TAP 设备是 Linux 内核功能,允许您创建显示为真实网络接口的虚拟网络接口。发送到 tap 接口的数据包将传递到用户空间程序(例如 QEMU),该程序已将其自身绑定到该接口。
QEMU 可以将 tap 网络用于虚拟机,以便发送到 tap 接口的数据包将发送到虚拟机,并在虚拟机中显示为来自网络接口(通常是以太网接口)。相反,虚拟机通过其网络接口发送的所有内容都将显示在 tap 接口上。
Linux 网桥驱动程序支持 Tap 设备,因此可以将 tap 设备彼此桥接,并可能与其他主机接口(例如 eth0
)桥接。如果您希望虚拟机能够相互通信,或者如果您希望 LAN 上的其他计算机能够与虚拟机通信,则这是理想的。
eth0
)桥接在一起,则您的虚拟机将直接出现在外部网络上,这将使它们暴露于可能的攻击。根据您的虚拟机可以访问的资源,您可能需要采取通常用于保护计算机的所有预防措施来保护您的虚拟机。如果风险太大,虚拟机资源很少,或者您设置了多个虚拟机,则更好的解决方案可能是使用 #仅主机网络 并设置 NAT。在这种情况下,您只需要在主机上设置一个防火墙,而不是为每个虚拟机设置多个防火墙。如用户模式网络部分所示,tap 设备提供比用户模式更高的网络性能。如果客户操作系统支持 virtio 网络驱动程序,则网络性能也将大大提高。假设使用 tap0 设备,客户操作系统上使用了 virtio 驱动程序,并且没有脚本用于帮助启动/停止网络,则接下来是 qemu 命令中应该看到的部分
-device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no
但是,如果已经将 tap 设备与 virtio 网络驱动程序一起使用,则甚至可以通过启用 vhost 来提高网络性能,例如
-device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no,vhost=on
有关更多信息,请参阅 [4]。
仅主机网络
如果为网桥分配了 IP 地址并允许了发往该地址的流量,但没有将真实接口(例如 eth0
)连接到网桥,则虚拟机将能够相互通信和与主机系统通信。但是,如果不在物理主机上设置 IP 伪装,它们将无法与外部网络上的任何内容通信。其他虚拟化软件(如 VirtualBox)将此配置称为仅主机网络。
- 如果您想为虚拟机设置 IP 伪装(例如 NAT),请参阅 Internet sharing#启用 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 地址,并在 INPUT 链中添加 iptables 规则以丢弃所有发往网桥的流量,则虚拟机将能够相互通信,但不能与物理主机或外部网络通信。其他虚拟化软件(如 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 网络助手的更多信息,请参阅 https://wiki.qemu.org/Features/HelperNetworking。
首先,创建一个配置文件,其中包含 QEMU 要使用的所有网桥的名称
/etc/qemu/bridge.conf
allow br0 allow br1 ...
确保 /etc/qemu/
具有 755
权限。如果情况并非如此,可能会出现 QEMU 问题 和 GNS3 问题。
现在启动虚拟机;使用默认网络助手和默认网桥 br0
运行 QEMU 的最基本用法
$ qemu-system-x86_64 -nic bridge [...]
使用网桥 br1
和 virtio 驱动程序
$ qemu-system-x86_64 -nic bridge,br=br1,model=virtio-net-pci [...]
手动创建网桥
以下描述了如何将虚拟机桥接到主机接口,例如 eth0
,这可能是最常见的配置。此配置使虚拟机看起来直接位于外部网络上,与物理主机在同一个以太网段上。
我们将用桥接适配器替换正常的以太网适配器,并将正常的以太网适配器绑定到桥接适配器。
- 安装 bridge-utils,它提供了用于操作桥接的
brctl
工具。
- 启用 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
模块并配置为在启动时加载。有关详细信息,请参阅 内核模块。
- 可选地创建桥接。有关详细信息,请参阅 使用 netctl 创建桥接。请记住将您的桥接命名为
br0
,或将下面的脚本更改为您桥接的名称。在下面的run-qemu
脚本中,如果未列出br0
,则会设置br0
,因为假设默认情况下主机不通过桥接访问网络。
- 创建 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 用于关闭 tap 适配器的脚本,位于
/etc/qemu-ifdown
,权限为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
然后要启动虚拟机,请执行类似以下的操作
$ 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
为了在启动时应用上述参数,您还需要在启动时加载 br-netfilter 模块。否则,当 sysctl 尝试修改它们时,这些参数将不存在。
/etc/modules-load.d/br_netfilter.conf
br_netfilter
运行 sysctl -p /etc/sysctl.d/10-disable-firewall-on-bridge.conf
以立即应用更改。
请参阅 libvirt wiki 和 Fedora bug 512206。如果您在启动期间因文件不存在而收到 sysctl 错误,请使 bridge
模块在启动时加载。请参阅 内核模块#systemd。
或者,您可以配置 iptables 以允许所有流量通过桥接转发,方法是添加类似这样的规则
-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
物理设备和 Tap 设备之间通过 iptables 进行网络共享
桥接网络在有线接口(例如 eth0)之间工作良好,并且易于设置。但是,如果主机通过无线设备连接到网络,则无法进行桥接。
请参阅 网络桥接#桥接上的无线接口 作为参考。
克服这个问题的一种方法是设置具有静态 IP 的 tap 设备,使 Linux 自动处理其路由,然后在 tap 接口和通过 iptables 规则连接到网络的设备之间转发流量。
请参阅 Internet 共享 作为参考。
您可以在那里找到在设备之间共享网络所需的内容,包括 tap 和 tun 设备。以下内容仅进一步提示了一些所需的主机配置。如上述参考资料所示,客户端需要配置为静态 IP,使用分配给 tap 接口的 IP 作为网关。需要注意的是,如果 DNS 服务器在从一个连接到网络的主机设备切换到另一个主机设备时发生更改,则可能需要在客户端上手动编辑 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 设备共享有线和无线连接。
显示的转发规则是无状态的,并且仅用于纯转发。可以考虑限制特定流量,设置防火墙以保护访客和其他人。然而,这些会降低网络性能,而简单的桥接不包含任何这些。
奖励:无论连接是有线还是无线,如果通过 VPN 连接到具有 tun 设备的远程站点,假设为该连接打开的 tun 设备是 tun0,并且应用了先前的 iptables 规则,则远程连接也会与访客共享。这避免了访客也需要打开 VPN 连接的需求。同样,由于访客网络需要是静态的,那么如果以这种方式远程连接主机,则很可能需要编辑访客上的 DNS 服务器。
使用 VDE2 进行网络连接
什么是 VDE?
VDE 代表虚拟分布式以太网。它最初是 uml_switch 的增强版。它是一个用于管理虚拟网络的工具箱。
这里的想法是创建虚拟交换机,它们基本上是套接字,并将物理和虚拟机“插入”其中。我们在此处显示的配置非常简单;但是,VDE 比这强大得多,它可以将虚拟交换机连接在一起,在不同的主机上运行它们并监控交换机中的流量。欢迎您阅读 该项目的文档。
此方法的优点是您不必向用户添加 sudo 权限。不应允许普通用户运行 modprobe。
基础知识
在我们的配置中,我们使用 tun/tap 在我的主机上创建一个虚拟接口。加载 tun
模块(有关详细信息,请参阅 内核模块)
# 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 [...]
像在物理网络中一样配置访客的网络连接。
启动脚本
启动 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
然后,启动虚拟机并连接到主机的网络
$ qemu-system-x86_64 -net nic,macaddr=52:54:00:00:EE:03 -net vde disk_image
VDE2 桥接
基于 quickhowto:使用 vde、tun/tap 和桥接的 qemu 网络 图形。连接到 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
和 -device
组合在一起,因此,例如,这些参数
-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=
创建的。-nic
参数的前半部分是 -netdev
参数,而后半部分(在 model=
之后)与设备相关。使用相同的参数(例如,smb=
)。要完全禁用网络连接,请使用 -nic none
。
有关您可以使用的参数的更多信息,请参阅 QEMU 网络文档。
显卡
QEMU 可以使用 -display curses
命令行选项模拟标准显卡文本模式。这允许在文本终端内直接键入文本并查看文本输出。或者,-nographic
也具有类似的目的。
QEMU 可以模拟多种类型的 VGA 卡。卡类型在 -vga type
命令行选项中传递,可以是 std
、qxl
、vmware
、virtio
、cirrus
或 none
。
std
使用 -vga std
,您可以获得高达 2560 x 1600 像素的分辨率,而无需访客驱动程序。这是自 QEMU 2.2 以来的默认设置。
qxl
QXL 是具有 2D 支持的半虚拟化图形驱动程序。要使用它,请传递 -vga qxl
选项并在访客中安装驱动程序。当使用 QXL 时,您可能需要使用 #SPICE 以获得更好的图形性能。
在 Linux 访客中,必须加载 qxl
和 bochs_drm
内核模块才能获得良好的性能。
QXL 设备的默认 VGA 内存大小为 16M,足以驱动大约高达 QHD (2560x1440) 的分辨率。要启用更高的分辨率,请增加 vga_memmb。
vmware
虽然它有点错误,但它的性能优于 std 和 cirrus。为 Arch Linux 访客安装 VMware 驱动程序 xf86-video-vmware 和 xf86-input-vmmouse。
virtio
virtio-vga
/ virtio-gpu
是基于 virgl 的半虚拟化 3D 图形驱动程序。它已经成熟,目前仅支持带有 mesa(编译时带有选项 gallium-drivers=virgl
)的 Linux 访客。
要在访客系统上启用 3D 加速,请使用 -device virtio-vga-gl
选择此 vga,并在显示设备中使用 -display sdl,gl=on
或 -display gtk,gl=on
分别为 SDL 和 GTK 显示输出启用 OpenGL 上下文。可以通过查看访客中的内核日志来确认配置是否成功
# dmesg | grep drm
[drm] pci: virtio-vga detected [drm] virgl 3d acceleration enabled
cirrus
cirrus 图形适配器是 2.2 之前的默认设置。它不应在现代系统上使用。
none
这就像一台根本没有 VGA 卡的 PC。您甚至无法使用 -vnc
选项访问它。此外,这与 -nographic
选项不同,后者让 QEMU 模拟 VGA 卡,但禁用 SDL 显示。
SPICE
SPICE 项目旨在为无缝远程访问虚拟机提供完整的开源解决方案。
在主机上启用 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
这些参数具有以下含义
-device virtio-serial-pci
添加 virtio-serial 设备-spice port=5930,disable-ticketing=on
将 TCP 端口5930
设置为 spice 通道侦听,并允许客户端在无需身份验证的情况下连接提示:使用 Unix 套接字 而不是 TCP 端口不涉及在主机系统上使用网络堆栈。它并不意味着数据包被封装和解封装以使用网络和相关协议。套接字仅通过硬盘驱动器上的 inode 进行标识。因此,它被认为更适合性能。请改用-spice unix=on,addr=/tmp/vm_spice.socket,disable-ticketing=on
。-device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0
在 virtio-serial 设备中为 spice vdagent 打开一个端口,-chardev spicevmc,id=spicechannel0,name=vdagent
为该端口添加一个 spicevmc chardev。重要的是virtserialport
设备的chardev=
选项与赋予chardev
选项的id=
选项(在本例中为spicechannel0
)匹配。同样重要的是端口名称为com.redhat.spice.0
,因为那是 vdagent 在访客中查找的命名空间。最后,指定name=vdagent
,以便 spice 知道此通道的用途。
使用 SPICE 客户端连接到访客
需要 SPICE 客户端才能连接到访客。在 Arch 中,可以使用以下客户端
- virt-viewer — 协议开发人员推荐的 SPICE 客户端,virt-manager 项目的子集。
- spice-gtk — SPICE GTK 客户端,SPICE 项目的子集。嵌入到其他应用程序中作为小部件。
对于在智能手机或其他平台上运行的客户端,请参阅 spice-space 下载中的其他客户端部分。
手动运行 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
参数运行 QEMU 可能会更方便。
$ 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 会话在客户端处于活动状态时运行,并在客户端结束后自动关闭。
使用 QEMU 运行 SPICE 客户端
如果显示设置为 SPICE 且带有 -display spice-app
参数,则 QEMU 可以自动启动带有适当套接字的 SPICE 客户端。这将使用系统的默认 SPICE 客户端作为查看器,由您的 mimeapps.list 文件确定。
在访客上启用 SPICE 支持
对于 Arch Linux 访客,为了改进对多显示器或剪贴板共享的支持,应安装以下软件包
- spice-vdagent:Spice agent xorg 客户端,可实现客户端和 X 会话之间的复制和粘贴等功能。(请参阅此问题,直到修复为止,了解如何在非 GNOME 桌面环境上使其工作的解决方法。)
- xf86-video-qxl:Xorg X11 qxl 视频驱动程序
- x-resizeAUR:GNOME 以外的桌面环境在 SPICE 客户端窗口大小调整时不会自动反应。此软件包使用 udev 规则和 xrandr 为所有基于 X11 的桌面环境和窗口管理器实现自动调整大小。
对于 其他操作系统 下的访客,请参阅 spice-space 下载中的访客部分。
使用 SPICE 进行密码验证
如果您想使用 SPICE 启用密码验证,则需要从 -spice
参数中删除 disable-ticketing
,并改为添加 password=您的密码
。例如
$ 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 服务器。
使用 SPICE 进行 TLS 加密通信
您还可以配置 TLS 加密以与 SPICE 服务器通信。首先,您需要有一个目录,其中包含以下文件(名称必须与指示的完全相同)
ca-cert.pem
:CA 主证书。server-cert.pem
:使用ca-cert.pem
签名的服务器证书。server-key.pem
:服务器私钥。
有关使用您自己生成的 CA 为您的服务器生成自签名证书的示例,请参见 Spice 用户手册。
之后,您可以如上所述运行带有 SPICE 的 QEMU,但使用以下 -spice
参数:-spice tls-port=5901,password=您的密码,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
请记住,需要根据您的 server-cert.pem
主题设置 --spice-host-subject
参数。您还需要将 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 虚拟机 部分中也提供了一个示例。
基本密码验证
可以使用 password
选项轻松设置访问密码。密码必须在 QEMU 监视器中指示,并且只有在提供密码后才能进行连接。
$ qemu-system-x86_64 -vnc :0,password -monitor stdio
在 QEMU 监视器中,使用命令 change vnc password
设置密码,然后指示密码。
以下命令行直接运行带有密码的 vnc
$ printf "change vnc password\n%s\n" MYPASSWORD | qemu-system-x86_64 -vnc :0,password -monitor stdio
音频
创建音频后端
-audiodev
标志设置主机上的音频后端驱动程序及其选项。
要列出可用的音频后端驱动程序
$ qemu-system-x86_64 -audiodev help
它们的可选设置在 qemu(1) 手册页中详细说明。
最基本的要求是,需要选择一个音频后端并设置一个 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 声音
自 QEMU 8.2.0 起,VirtIO 声音也可用。用法是
-device virtio-sound-pci,audiodev=my_audiodev -audiodev alsa,id=my_audiodev
更多信息可以在 QEMU 文档中找到。
使用 virtio 驱动程序
QEMU 为访客提供了使用半虚拟化块和网络设备的能力,这些设备使用 virtio 驱动程序,从而提供更好的性能和更低的开销。
- virtio 块设备需要
-drive
选项来传递磁盘映像,参数为if=virtio
$ qemu-system-x86_64 -drive file=disk_image,if=virtio
- 网络也几乎相同
$ qemu-system-x86_64 -nic user,model=virtio-net-pci
准备 Arch Linux 访客
要在安装 Arch Linux 访客后使用 virtio 设备,必须在访客中加载以下模块:virtio
、virtio_pci
、virtio_blk
、virtio_net
和 virtio_ring
。对于 32 位访客,不需要特定的“virtio”模块。
如果您想从 virtio 磁盘启动,则初始 ramdisk 必须包含必要的模块。默认情况下,这由 mkinitcpio 的 autodetect
挂钩处理。否则,请使用 /etc/mkinitcpio.conf
中的 MODULES
数组包含必要的模块并重建初始 ramdisk。
/etc/mkinitcpio.conf
MODULES=(virtio virtio_blk virtio_pci virtio_net)
Virtio 磁盘使用前缀 v
识别(例如 vda
、vdb
等);因此,从 virtio 磁盘启动时,必须至少在 /etc/fstab
和 /boot/grub/grub.cfg
中进行更改。
/etc/fstab
和引导加载程序中引用磁盘时,无需执行任何操作。有关使用 KVM 进行半虚拟化的更多信息,请在此处找到。
您可能还想安装 qemu-guest-agent 以实现对 QMP 命令的支持,这将增强虚拟机管理程序管理功能。
内存气球
为了允许从主机看到访客的内存占用量缩小,它需要向主机报告访客不再需要的页面。内核有一个名为 Free Page Reporting 的 API,由于它是内置的,因此启动 QEMU 非常容易,如下所示
$ qemu-system-x86_64 ... -device virtio-balloon,free-page-reporting=on
之后,您应该会看到访客内存增加,然后在访客中运行工作负载后再次缩小。
准备 Windows 访客
Windows 的 Virtio 驱动程序
Windows 不附带 virtio 驱动程序。Fedora 定期构建驱动程序的最新稳定版本,有关下载驱动程序的详细信息,请参见 GitHub 上的 virtio-win。在以下各节中,我们将主要使用此处提供的稳定 ISO 文件:virtio-win.iso。或者,使用 virtio-winAUR。
块设备驱动程序
Windows 全新安装
驱动程序需要在安装期间加载,步骤是将带有 virtio 驱动程序的 ISO 映像加载到 cdrom 设备中,同时加载主磁盘设备和 Windows ISO 安装介质
$ 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 安装程序会询问“您想将 Windows 安装在哪里?”,它会发出警告,指出未找到磁盘。按照以下示例说明操作(基于带有更新的 Windows Server 2012 R2)。
- 选择加载驱动程序选项。
- 取消选中隐藏与此计算机硬件不兼容的驱动程序的框。
- 单击浏览按钮,然后打开 virtio iso 的 CDROM,通常命名为“virtio-win-XX”。
- 现在浏览到
E:\viostor\[your-os]\amd64
,选择它,然后确认。
您现在应该在此处看到列出的 virtio 磁盘,可以随时选择、格式化和安装到其中。
更改现有 Windows 虚拟机以使用 virtio
修改现有 Windows 访客以从 virtio 磁盘启动需要访客在启动时加载 virtio 驱动程序。因此,我们需要教 Windows 在启动时加载 virtio 驱动程序,然后才能在 virtio 模式下启动磁盘映像。
为了实现这一点,首先创建一个新的磁盘映像,该映像将以 virtio 模式附加并触发对驱动程序的搜索
$ qemu-img create -f qcow2 dummy.qcow2 1G
在 IDE 模式下使用启动磁盘、virtio 模式下的虚拟磁盘和驱动程序 ISO 映像运行原始 Windows 访客。
$ qemu-system-x86_64 -m 4G -drive file=disk_image,if=ide -drive file=dummy.qcow2,if=virtio -cdrom virtio-win.iso
Windows 将检测到虚拟磁盘并查找合适的驱动程序。如果失败,请转到设备管理器,找到带有感叹号图标的 SCSI 驱动器(应为打开状态),单击更新驱动程序,然后选择虚拟 CD-ROM。不要导航到 CD-ROM 中的驱动程序文件夹,只需选择 CD-ROM 驱动器,Windows 将自动找到合适的驱动程序(已在 Windows 7 SP1 上测试)。
请求 Windows 在下次启动时以安全模式启动。这可以使用 Windows 中的 msconfig.exe 工具完成。在安全模式下,所有驱动程序都将在启动时加载,包括新的 virtio 驱动程序。一旦 Windows 知道启动时需要 virtio 驱动程序,它就会记住它以供将来启动使用。
一旦指示以安全模式启动,您就可以关闭虚拟机并再次启动它,现在启动磁盘以 virtio 模式附加
$ qemu-system-x86_64 -m 4G -drive file=disk_image,if=virtio
您应该以加载 virtio 驱动程序的安全模式启动,现在您可以返回到 msconfig.exe 禁用安全模式启动并重新启动 Windows。
if=virtio
参数时遇到蓝屏死机,则可能意味着 virtio 磁盘驱动程序未安装或未在启动时加载,请在安全模式下重新启动并检查您的驱动程序配置。网络驱动程序
使用 virtio 网络驱动程序更容易一些,只需添加 -nic
参数即可。
$ qemu-system-x86_64 -m 4G -drive file=windows_disk_image,if=virtio -nic user,model=virtio-net-pci -cdrom virtio-win.iso
Windows 将检测到网络适配器并尝试为其查找驱动程序。如果失败,请转到设备管理器,找到带有感叹号图标的网络适配器(应为打开状态),单击更新驱动程序,然后选择虚拟 CD-ROM。不要忘记选中表示递归搜索目录的复选框。
气球驱动程序
如果您想跟踪访客内存状态(例如通过 virsh
命令 dommemstat
)或在运行时更改访客的内存大小(您仍然无法更改内存大小,但可以通过膨胀气球驱动程序来限制内存使用量),则需要安装访客气球驱动程序。
为此,您需要转到设备管理器,在系统设备中找到PCI 标准 RAM 控制器(或其他设备中未识别的 PCI 控制器),然后选择更新驱动程序。在打开的窗口中,您需要选择浏览我的电脑...并选择 CD-ROM(并且不要忘记包括子目录复选框)。安装后重新启动。这将安装驱动程序,您将能够膨胀气球(例如通过 hmp 命令 balloon memory_size
,这将导致气球占用尽可能多的内存,以便将访客的可用内存大小缩小到 memory_size)。但是,您仍然无法跟踪访客内存状态。为了做到这一点,您需要正确安装气球服务。为此,以管理员身份打开命令行,转到 CD-ROM、Balloon 目录和更深层次的目录,具体取决于您的系统和体系结构。进入 amd64 (x86) 目录后,运行 blnsrv.exe -i
,这将完成安装。之后,virsh
命令 dommemstat
应该输出所有支持的值。
在您继续本节之前,请确保您首先遵循了有关使用 virtiofsd 设置主机文件共享的部分。
首先,按照上游说明进行操作。配置完成后,Windows 将自动映射 Z:
驱动器,其中包含共享目录内容。
如果您的 Windows 11 访客系统具有以下内容,则表示配置正确
- VirtioFSSService Windows 服务,
- WinFsp.Launcher Windows 服务,
- Windows“设备管理器”中“系统设备”下的 VirtIO FS 设备驱动程序。
如果以上各项已安装,但仍未列出 Z:
驱动器,请尝试在 Windows 添加/删除程序中修复“Virtio-win-guest-tools”。
准备 FreeBSD 访客
如果您使用的是 FreeBSD 8.3 或更高版本,直到 10.0-CURRENT,其中它们包含在内核中,请安装 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
是否一致。如果出现任何问题,只需启动到救援 CD 并将 /etc/fstab.bak
复制回 /etc/fstab
。
QEMU 监视器
在 QEMU 运行时,提供了一个监视器控制台,以便提供多种与正在运行的虚拟机交互的方式。QEMU 监视器提供了有趣的功能,例如获取有关当前虚拟机的信息、热插拔设备、创建虚拟机当前状态的快照等。要查看所有命令的列表,请在 QEMU 监视器控制台中运行 help
或 ?
,或查看 官方 QEMU 文档的相关部分。
访问监视器控制台
图形视图
当使用 std
默认图形选项时,可以通过按 Ctrl+Alt+2
或单击 QEMU 窗口中的视图 > compatmonitor0 来访问 QEMU 监视器。要返回到虚拟机图形视图,请按 Ctrl+Alt+1
或单击视图 > VGA。
但是,访问监视器的标准方法并不总是方便,并且并非在 QEMU 支持的所有图形输出中都有效。
Telnet
要启用 telnet,请使用 -monitor telnet:127.0.0.1:port,server,nowait
参数运行 QEMU。虚拟机启动后,您将能够通过 telnet 访问监视器
$ telnet 127.0.0.1 port
127.0.0.1
指定为监听 IP,则只能从 QEMU 运行的同一主机连接到监视器。如果需要从远程主机连接,则必须告知 QEMU 监听 0.0.0.0
,如下所示:-monitor telnet:0.0.0.0:port,server,nowait
。请记住,在这种情况下,建议配置防火墙,或者确保您的本地网络完全可信,因为此连接完全未经身份验证且未加密。UNIX 套接字
使用 -monitor unix:socketfile,server,nowait
参数运行 QEMU。然后,您可以使用 socat、nmap 或 openbsd-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-netcat 或 gnu-netcat 运行以下命令
$ nc 127.0.0.1 port
0.0.0.0
。在这种情况下,同样适用相同的安全警告。标准 I/O
可以通过使用参数 -monitor stdio
运行 QEMU,从运行 QEMU 的同一终端自动访问监视器。
使用监视器控制台向虚拟机发送键盘按键
由于主机在某些配置中会拦截某些按键组合(一个值得注意的例子是 Ctrl+Alt+F*
组合键,它会更改活动的 tty),因此在虚拟机上可能难以执行某些按键组合。为了避免这个问题,可以通过监视器控制台发送有问题的按键组合。切换到监视器并使用 sendkey
命令将必要的按键转发到虚拟机。例如
(qemu) sendkey ctrl-alt-f2
通过监视器控制台创建和管理快照
有时,需要保存虚拟机的当前状态,并能够在任何时候将虚拟机的状态恢复到先前保存的快照状态。QEMU 监视器控制台为用户提供了创建快照、管理快照以及将机器状态恢复到已保存快照的必要实用程序。
- 使用
savevm name
以创建带有标签 name 的快照。 - 使用
loadvm name
将虚拟机恢复到快照 name 的状态。 - 使用
delvm name
删除标记为 name 的快照。 - 使用
info snapshots
查看已保存快照的列表。快照由自动递增的 ID 号和文本标签(用户在创建快照时设置)标识。
在不可变模式下运行虚拟机
可以通过使用 -snapshot
参数运行 QEMU,在冻结状态下运行虚拟机,这样,当虚拟机关闭电源时,所有更改都将被丢弃。当访客操作系统写入磁盘映像时,更改将保存在 /tmp
中的临时文件中,并在 QEMU 停止时丢弃。
但是,如果机器在冻结模式下运行,仍然可以使用监视器控制台并通过运行以下命令将更改保存到磁盘映像(如果之后需要)。
(qemu) commit all
如果在冻结模式下创建快照,它们将在 QEMU 退出后立即被丢弃,除非更改也显式提交到磁盘。
通过监视器控制台暂停和电源选项
物理机器的某些操作可以通过 QEMU 使用一些监视器命令来模拟
system_powerdown
将向虚拟机发送 ACPI 关闭请求。此效果类似于物理机器上的电源按钮。system_reset
将重置虚拟机,类似于物理机器上的重置按钮。此操作可能会导致数据丢失和文件系统损坏,因为虚拟机未干净地重启。stop
将暂停虚拟机。cont
将恢复先前暂停的虚拟机。
拍摄虚拟机屏幕截图
可以通过在监视器控制台中运行以下命令,以 PPM 格式获取虚拟机图形显示的屏幕截图
(qemu) screendump file.ppm
QEMU 机器协议
QEMU 机器协议 (QMP) 是一种基于 JSON 的协议,允许应用程序控制 QEMU 实例。与 #QEMU 监视器 类似,它提供了与正在运行的机器交互的方式,JSON 协议允许以编程方式执行此操作。所有 QMP 命令的描述都可以在 qmp-commands 中找到。
启动 QMP
使用 QMP 协议控制访客操作系统的常用方法是在启动机器时使用 -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 已准备好接收命令,要检索已识别命令的列表,请使用
{"execute": "query-commands"}
将子镜像实时合并到父镜像中
可以通过发出 block-commit
命令将正在运行的快照合并到其父镜像中。在其最简单的形式中,以下行将子镜像提交到其父镜像中
{"execute": "block-commit", "arguments": {"device": "devicename"}}
收到此命令后,处理程序将查找基本镜像,并将其从只读模式转换为读写模式,然后运行提交作业。
block-commit 操作完成后,将发出事件 BLOCK_JOB_READY
,表示同步已完成。然后可以通过发出命令 block-job-complete
来优雅地完成作业
{"execute": "block-job-complete", "arguments": {"device": "devicename"}}
在发出此类命令之前,commit 操作将保持活动状态。成功完成后,基本镜像将保持读写模式,并成为新的活动层。另一方面,子镜像将变为无效,用户有责任清理它。
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 以进行完全虚拟化。
- 使用
-cpu host
选项使 QEMU 模拟主机的确切 CPU,而不是更通用的 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、hypervisor 和主机系统不受阻碍地运行非常有益。 - 确保您已为虚拟机分配了足够的内存。默认情况下,QEMU 仅为每个虚拟机分配 128 MiB 的内存。使用
-m
选项分配更多内存。例如,-m 1024
运行一个具有 1024 MiB 内存的虚拟机。 - 如果访客操作系统中的驱动程序支持,请对网络和/或块设备使用 virtio,请参阅 #使用 virtio 驱动程序。
- 使用 TAP 设备而不是用户模式网络,请参阅 #使用 QEMU 的 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。
- 在某些情况下,可以通过在访客操作系统中运行内存气球驱动程序来回收正在运行的虚拟机中的内存。请参阅 #内存气球。
- 可以使用 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 中使用系统的某个分区。对虚拟机使用原始分区将提高性能,因为读取和写入操作不会通过物理主机上的文件系统层。这样的分区还提供了一种在主机和访客操作系统之间共享数据的方式。
在 Arch Linux 中,原始分区的设备文件默认情况下由 root 和 disk 组拥有。如果您希望非 root 用户能够读取和写入原始分区,则必须将分区设备文件的所有者更改为该用户,将该用户添加到 disk 组,或者使用 ACL 进行更细粒度的访问控制。
- 尽管有可能,但不建议允许虚拟机更改主机系统上的关键数据,例如根分区。
- 您不得同时在主机和访客操作系统上以读写方式挂载分区上的文件系统。否则,将导致数据损坏。
这样做之后,您可以将分区作为虚拟磁盘附加到 QEMU 虚拟机。
但是,如果您希望将整个虚拟机都包含在一个分区中,情况会稍微复杂一些。在这种情况下,将没有磁盘映像文件来实际启动虚拟机,因为您无法将引导加载程序安装到本身格式化为文件系统而不是格式化为带有 MBR 的分区设备的的分区。可以通过以下方式引导此类虚拟机:#手动指定内核和 initrd、#模拟带有 MBR 的虚拟磁盘、#使用设备映射器、#使用线性 RAID 或 #使用网络块设备。
手动指定内核和 initrd
QEMU 支持直接加载 Linux 内核 和 init ramdisk,从而绕过诸如 GRUB 之类的引导加载程序。然后可以使用包含根文件系统的物理分区作为虚拟磁盘启动它,该虚拟磁盘将不会显示为已分区。这可以通过发出类似于以下内容的命令来完成
/dev/sda3
(以保护文件系统免受主机影响)并指定 /full/path/to/images
或在访客操作系统中使用一些 kexec hackery 重新加载访客操作系统的内核(延长启动时间)。$ qemu-system-x86_64 -kernel /boot/vmlinuz-linux -initrd /boot/initramfs-linux.img -append root=/dev/sda /dev/sda3
在上面的示例中,用于访客操作系统根文件系统的物理分区是主机上的 /dev/sda3
,但在访客操作系统上显示为 /dev/sda
。
当然,您可以指定任何您想要的内核和 initrd,而不仅仅是 Arch Linux 附带的那些。
当有多个 内核参数 要传递给 -append
选项时,它们需要使用单引号或双引号引起来。例如
... -append 'root=/dev/sda1 console=ttyS0'
模拟带有 MBR 的虚拟磁盘
使虚拟机使用物理分区的更复杂方法是模拟 MBR 以使其可以使用诸如 GRUB 之类的引导加载程序启动,同时保持该分区格式化为文件系统,而不是仅让访客操作系统像对待磁盘一样对分区进行分区。
对于以下内容,假设您有一个普通的、未挂载的 /dev/hdaN
分区,其上有一些您希望使其成为 QEMU 磁盘映像一部分的文件系统。诀窍是将主引导记录 (MBR) 动态地添加到您希望嵌入 QEMU 原始磁盘映像中的真实分区的前面。更一般地,该分区可以是更大的模拟磁盘的任何部分,特别是模拟原始物理磁盘但仅将 /dev/hdaN
公开给虚拟机的块设备。
这种类型的虚拟磁盘可以用 VMDK 文件表示,该文件包含对 MBR 和分区的引用(副本),但 QEMU 不支持此 VMDK 格式。例如,虚拟磁盘 由 创建
$ VBoxManage internalcommands createrawvmdk -filename /path/to/file.vmdk -rawdisk /dev/hda
将被 QEMU 拒绝,并显示错误消息
Unsupported image type 'partitionedDevice'
请注意,VBoxManage
创建了两个文件,file.vmdk
和 file-pt.vmdk
,后者是 MBR 的副本,文本文件 file.vmdk
指向该副本。在目标分区或 MBR 之外的读取操作将给出零,而写入的数据将被丢弃。
使用设备映射器
一种类似于使用 VMDK 描述符文件的方法是使用 设备映射器 将附加到 MBR 文件的循环设备添加到目标分区的前面。如果我们不需要虚拟磁盘具有与原始磁盘相同的大小,我们首先创建一个文件来保存 MBR
$ dd if=/dev/zero of=/path/to/mbr count=2048
此处,根据现代磁盘分区工具使用的分区对齐策略,创建了一个 1 MiB(2048 * 512 字节)的文件。为了与旧的分区软件兼容,可能需要 63 个扇区而不是 2048 个扇区。MBR 只需要一个 512 字节的块,额外的可用空间可以用于 BIOS 引导分区,以及在混合分区方案的情况下,用于 GUID 分区表。然后,我们将循环设备附加到 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
从文件加载。对于虚拟机,只能访问 /dev/hdaN
,而硬盘驱动器的其余部分读取为零并丢弃写入的数据,除了第一个扇区。我们可以使用 dmsetup table qemu
打印 /dev/mapper/qemu
的表(使用 udevadm info -rq name /sys/dev/block/major:minor
将 major:minor
转换为相应的 /dev/blockdevice
名称)。使用 dmsetup remove qemu
和 losetup -d $loop
删除创建的设备。
此示例有用的情况是在多引导配置中现有的 Windows XP 安装,以及可能是混合分区方案(在物理硬件上,Windows XP 可能是唯一使用 MBR 分区表的操作系统,而安装在同一台计算机上的更现代的操作系统可能使用 GUID 分区表)。Windows XP 支持硬件配置文件,因此可以交替地将相同的安装用于不同的硬件配置(在本例中为裸机与虚拟),Windows 只需要为每个配置文件安装一次新检测到的硬件的驱动程序。请注意,在本示例中,需要更新复制的 MBR 中的引导加载程序代码,以直接从 /dev/hdaN
加载 Windows XP,而不是尝试启动原始系统中存在的多引导功能引导加载程序(如 GRUB)。或者,可以将包含引导加载程序安装的引导分区的副本以与 MBR 相同的方式包含在虚拟磁盘中。
使用线性 RAID
您也可以使用软件 RAID 以线性模式执行此操作(您需要 linear.ko
内核驱动程序)和一个环回设备
首先,您创建一些小文件来保存 MBR
$ dd if=/dev/zero of=/path/to/mbr count=32
此处,创建了一个 16 KiB(32 * 512 字节)的文件。重要的是不要使其太小(即使 MBR 只需要一个 512 字节的块),因为文件越小,软件 RAID 设备的块大小就必须越小,这可能会对性能产生影响。然后,您为 MBR 文件设置一个环回设备
# losetup -f /path/to/mbr
假设生成的设备是 /dev/loop0
,因为我们尚未在使用其他环回设备。下一步是使用软件 RAID 创建“合并”的 MBR + /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”ector 数量,以便一个柱面大小与您的 MBR 文件大小匹配。对于两个磁头和 512 的扇区大小,每个磁道的扇区数应为 16,因此我们得到的柱面大小为 2x16x512=16k。
现在,按 R
返回主菜单。
按 P
并检查柱面大小现在为 16k。
现在,创建一个与 /dev/hdaN
对应的单个主分区。它应从柱面 2 开始,到磁盘末尾结束(请注意,柱面数现在与您进入 fdisk 时的柱面数不同)。
最后,'w'rite 结果到文件:您已完成。您现在有一个可以直接从主机挂载的分区,以及 QEMU 磁盘映像的一部分
$ qemu-system-x86_64 -hdc /dev/md0 [...]
当然,您可以使用 QEMU 在此磁盘映像上安全地设置任何引导加载程序,前提是原始 /dev/hdaN
分区包含必要的工具。
使用网络块设备
使用 网络块设备,Linux 可以使用远程服务器作为其块设备之一。您可以使用 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
和 .1
后缀是必不可少的;其余的可以更改。运行上述脚本后(您可能需要以 root 身份执行该脚本以确保 nbd-server 能够访问该分区),您可以使用以下命令启动 QEMU
qemu-system-x86_64 -drive file=nbd:127.713705:10809:exportname=wrap [...]
在启动时启动 QEMU 虚拟机
使用 libvirt
如果使用 libvirt 设置了虚拟机,则可以使用 virsh autostart
或通过 virt-manager GUI 进行配置,以在主机启动时启动虚拟机,方法是转到虚拟机的引导选项并选择“在主机启动时启动虚拟机”。
使用 systemd 服务
要在启动时运行 QEMU 虚拟机,您可以使用以下 systemd 单元和服务配置。
/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
然后创建每个虚拟机的配置文件,命名为 /etc/conf.d/qemu.d/vm_name
,并设置变量 args
和 haltcmd
。示例配置
/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 监视器通过 telnet 使用-monitor telnet:..
公开,虚拟机通过 ACPI 关闭电源,方法是使用nc
命令将system_powerdown
发送到监视器。在另一个示例中,使用了 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
模拟 EHCI (USB 2) 控制器或使用 -device qemu-xhci,id=xhci
模拟 XHCI (USB 1.1 USB 2 USB 3) 控制器,然后使用选项 -device usb-host,..
将物理设备连接到它。在本节的其余部分中,我们将认为 controller_id 是 ehci
或 xhci
。
然后,有两种方法可以使用 qemu 连接到主机的 USB
- 识别设备并连接到它在主机上连接的任何总线和地址,通用语法是
-device usb-host,bus=controller_id.0,vendorid=0xvendor_id,productid=0xproduct_id
应用于上面示例中使用的设备,它变为-device usb-ehci,id=ehci -device usb-host,bus=ehci.0,vendorid=0x0781,productid=0x5406
还可以将...,port=port_number
设置添加到上一个选项,以指定设备应连接到虚拟控制器的哪个物理端口,这在想要向虚拟机添加多个 USB 设备的情况下很有用。另一个选项是使用usb-host
的新hostdevice
属性,该属性自 QEMU 5.1.0 起可用,语法为-device qemu-xhci,id=xhci -device usb-host,hostdevice=/dev/bus/usb/003/007
- 附加连接到给定 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/USB 仿真。
使用 SPICE 进行 USB 重定向
当使用 #SPICE 时,可以将 USB 设备从客户端重定向到虚拟机,而无需在 QEMU 命令中指定它们。可以配置可用于重定向设备的 USB 插槽数量(插槽数量将决定可以同时重定向的最大设备数量)。与前面提到的 -usbdevice
方法相比,使用 SPICE 进行重定向的主要优点是可以在虚拟机启动后热插拔 USB 设备,而无需停止虚拟机即可从重定向中移除 USB 设备或添加新设备。这种 USB 重定向方法还允许我们通过网络将 USB 设备从客户端重定向到服务器。总而言之,它是 QEMU 虚拟机中使用 USB 设备的最灵活方法。
我们需要为每个所需的可用 USB 重定向插槽添加一个 EHCI/UHCI 控制器,以及每个插槽一个 SPICE 重定向通道。例如,将以下参数添加到您用于在 SPICE 模式下启动虚拟机的 QEMU 命令中,将启动虚拟机,并提供三个可用于重定向的 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
(输入 > 选择用于重定向的 USB 设备)和来自 virt-viewer 的 remote-viewer
(文件 > USB 设备选择)都支持此功能。请确保您已在虚拟机上安装了必要的 SPICE Guest Tools,以使此功能按预期工作(有关更多信息,请参阅 #SPICE 部分)。
使用 udev 自动 USB 转发
通常,转发的设备必须在虚拟机启动时可用才能被转发。如果该设备断开连接,则将不再转发。
您可以使用 udev 规则 在设备上线时自动附加设备。在磁盘上的某个位置创建 hostdev
条目。chown 它给 root 以防止其他用户修改它。
/usr/local/hostdev-mydevice.xml
<hostdev mode='subsystem' type='usb'> <source> <vendor id='0x03f0'/> <product id='0x4217'/> </source> </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
内核同页合并 (KSM) 是 Linux 内核的一项功能,它允许应用程序向内核注册,以将其页面与其他也注册要合并其页面的进程合并。KSM 机制允许访客虚拟机相互共享页面。在许多访客操作系统相似的环境中,这可以显着节省内存。
要启用 KSM
# echo 1 > /sys/kernel/mm/ksm/run
要使其永久生效,请使用 systemd 的临时文件
/etc/tmpfiles.d/ksm.conf
w /sys/kernel/mm/ksm/run - - - - 1
如果 KSM 正在运行,并且有要合并的页面(即至少有两个相似的虚拟机正在运行),则 /sys/kernel/mm/ksm/pages_shared
应为非零值。有关更多信息,请参阅 https://docs.linuxkernel.org.cn/admin-guide/mm/ksm.html。
$ 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 qxl
替换为 -vga none -device qxl-vga,vgamem_mb=32
来更改此设置。如果您将 vgamem_mb 增加到 64M 以上,那么您还必须增加 vram_size_mb
选项。
自定义显示分辨率
可以使用 -device VGA,edid=on,xres=1280,yres=720
设置自定义显示分辨率(请参阅 EDID 和 显示分辨率)。
复制和粘贴
SPICE
在主机和 guest 之间共享剪贴板的一种方法是启用 SPICE 远程桌面协议,并使用 SPICE 客户端访问客户端。需要按照 #SPICE 中描述的步骤操作。以这种方式运行的 guest 将支持与主机的复制粘贴。
qemu-vdagent
QEMU 提供了其自己的 spice vdagent chardev 实现,称为 qemu-vdagent
。它与 spice-vdagent guest 服务接口,并允许 guest 和主机共享剪贴板。
要使用 QEMU 的 GTK 显示访问此共享剪贴板,您需要使用 --enable-gtk-clipboard
配置参数从源代码编译 QEMU。替换已安装的 qemu-ui-gtk
软件包就足够了。
- 功能请求 FS#79716 已提交以在官方软件包中启用此功能。
- qemu-ui-gtk 中的共享剪贴板已被重新推回到实验性阶段,因为它可能在某些情况下冻结 guest。已提出修复方案以解决上游问题。
添加以下 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 形式,这些参数也有效。
在 Linux guest 系统上,您可以手动启动 spice-vdagent.service
用户单元。在 Windows guest 系统上,将 spice-agent 启动类型设置为自动。
Windows 特定注意事项
QEMU 可以运行从 Windows 95 到 Windows 11 的任何 Windows 版本。
可以在 QEMU 中运行 Windows PE。
快速启动
对于 Windows 8(或更高版本)guest 系统,最好从控制面板的电源选项中禁用“启用快速启动(推荐)”,如以下论坛页面中所述,因为它会导致 guest 系统在每次其他启动时挂起。
可能还需要禁用快速启动,以使 -smp
选项的更改能够正确应用。
远程桌面协议
如果您使用 MS Windows guest 系统,您可能希望使用 RDP 连接到您的 guest 虚拟机。如果您正在使用 VLAN 或者与 guest 不在同一网络中,请使用
$ qemu-system-x86_64 -nographic -nic user,hostfwd=tcp::5555-:3389
然后使用 rdesktop 或 freerdp 连接到 guest 系统。例如
$ xfreerdp -g 2048x1152 localhost:5555 -z -x lan
克隆安装在物理设备上的 Linux 系统
安装在物理设备上的 Linux 系统可以被克隆以在 QEMU 虚拟机上运行。请参阅 从硬件为 QEMU 虚拟机克隆 Linux 系统
从 x86_64 环境 chroot 进入 arm/arm64 环境
有时直接在磁盘映像上工作比在真实的 ARM 设备上工作更容易。这可以通过挂载包含 root 分区的 SD 卡/存储并 chroot 进入它来实现。
ARM chroot 的另一个用例是在 x86_64 机器上构建 ARM 软件包。在这里,chroot 环境可以从 Arch Linux ARM 的映像 tarball 创建 - 有关此方法的详细描述,请参阅 [5]。
无论哪种方式,从 chroot 中都应该可以运行 pacman 并安装更多软件包、编译大型库等。由于可执行文件是为 ARM 架构设计的,因此需要 QEMU 执行到 x86 的转换。
在 x86_64 机器/主机上安装 qemu-user-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 和 64 位 ARM 编译的 ls
命令。请注意,这在没有 chroot 的情况下将无法工作,因为它将查找主机系统中不存在的库。
qemu-user-static-binfmt 允许自动为 ARM 可执行文件添加 qemu-arm-static
或 qemu-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 卡根目录
# chroot /mnt/sdcard /bin/bash
或者,您可以使用 arch-install-scripts 中的 arch-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
并检查更改是否已生效(注意 flags
行上的 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 窗口中鼠标输入的副作用
-usb -device usb-tablet
它适用于几个 -vga
后端,其中一个是 virtio。
故障排除
鼠标光标抖动或不稳定
如果光标在屏幕上不受控制地跳动,在启动 QEMU 之前在终端上输入此命令可能会有所帮助
$ export SDL_VIDEO_X11_DGAMOUSE=0
如果这有帮助,您可以将其添加到您的 ~/.bashrc
文件中。
没有可见的光标
将 -display default,show-cursor=on
添加到 QEMU 的选项中以查看鼠标光标。
如果仍然无效,请确保您已正确设置显示设备,例如:-vga qxl
。
另一个可以尝试的选项是 -usb -device usb-tablet
,如 #鼠标集成 中所述。这会覆盖默认的 PS/2 鼠标仿真,并同步主机和 guest 之间的指针位置,作为额外的奖励。
可见两个不同的鼠标光标
应用提示 #鼠标集成。
使用 VNC 时出现键盘问题
使用 VNC 时,您可能会遇到键盘问题,详细描述(在可怕的细节中)此处。解决方案是不要在 QEMU 上使用 -k
选项,并使用 gtk-vnc 中的 gvncviewer
。另请参阅 此 邮件,发布在 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
参数的无效键盘映射引起的。例如,en
无效,但 en-us
有效 - 请参阅 /usr/share/qemu/keymaps/
。
Guest 显示在窗口大小调整时拉伸
要恢复默认窗口大小,请按 Ctrl+Alt+u
。
ioctl(KVM_CREATE_VM) 失败:16 设备或资源繁忙
如果在启动带有 -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 环境上的内核崩溃
如果您启动 live 环境(或更好:启动系统),您可能会遇到这种情况
[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown block(0,0)
或其他一些启动阻碍过程(例如,无法解压缩 initramfs,无法启动服务 foo)。尝试使用 -m VALUE
开关和适当大小的 RAM 启动虚拟机,如果 ram 太低,您可能会遇到与上面/没有内存开关类似的问题。
Windows 7 guest 系统声音质量低
为 Windows 7 guest 系统使用 hda
音频驱动程序可能会导致声音质量低。通过将 -soundhw ac97
参数传递给 QEMU 并从 Realtek AC'97 Audio Codecs[死链接 2025-01-22 ⓘ] 安装 AC97 驱动程序,可以解决该问题。有关更多信息,请参阅 Red Hat Bugzilla – Bug 1176761。
无法访问 KVM 内核模块:权限被拒绝
如果您遇到以下错误
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.conf
并将包含 group = "78"
的行更改为 group = "kvm"
。
启动 Windows 虚拟机时出现“System Thread Exception Not Handled”
Windows 8 或 Windows 10 guest 系统可能会在启动时引发通用兼容性异常,即“System Thread Exception Not Handled”,这通常是由传统驱动程序在真实机器上表现异常引起的。在 KVM 机器上,通常可以通过将 CPU 模型设置为 core2duo
来解决此问题。
某些 Windows 游戏/应用程序崩溃/导致蓝屏
有时,在虚拟机中运行的应用程序可能会意外崩溃,而在物理机上它们会正常运行。如果在以 root 身份运行 dmesg -wH
时,您遇到提及 MSR
的错误,则这些崩溃的原因是 KVM 在 guest 尝试访问不受支持的 Model-specific registers (MSRs) 时注入了 General protection fault (GPF) - 这通常会导致 guest 应用程序/操作系统崩溃。可以通过将 ignore_msrs=1
选项传递给 KVM 模块来解决许多此类问题,这将忽略未实现的 MSR。
/etc/modprobe.d/kvm.conf
... options kvm ignore_msrs=1 ...
添加此选项可能有帮助的情况
- GeForce Experience 抱怨存在不受支持的 CPU。
- StarCraft 2 和 L.A. Noire 可靠地使 Windows 10 蓝屏,并显示
KMODE_EXCEPTION_NOT_HANDLED
。在这些情况下,蓝屏信息未识别驱动程序文件。
高中断延迟和微卡顿
此问题表现为小的停顿(卡顿),在图形密集型应用程序(如游戏)中尤其明显。
- 原因之一是 CPU 节能功能,这些功能由 CPU 频率调整控制。将所有处理器核心的频率调整更改为
performance
。 - 另一个可能的原因是 PS/2 输入。从 PS/2 切换到 Virtio 输入,请参阅 PCI passthrough via OVMF#通过 Evdev 传递键盘/鼠标。
QXL 视频导致低分辨率
QEMU 4.1.0 引入了一个回归,其中 QXL 视频在通过 spice 显示时可能会回退到低分辨率。[6] 例如,当 KMS 启动时,文本分辨率可能会低至 4x10 字符。当尝试增加 GUI 分辨率时,它可能会变为最低支持的分辨率。
作为一种解决方法,请以这种形式创建您的设备
-device qxl-vga,max_outputs=1...
使用启用安全启动的 OVMF 时虚拟机无法启动
来自 edk2-ovmf 的 OVMF_CODE.secboot.4m.fd
和 OVMF_CODE.secboot.fd
文件是使用 SMM 支持构建的。如果在虚拟机中未禁用 S3 支持,则虚拟机可能根本无法启动。
将 -global ICH9-LPC.disable_s3=1
选项添加到 qemu 命令。
有关更多详细信息以及在 QEMU 中使用安全启动所需的选项,请参阅 FS#59465 和 https://github.com/tianocore/edk2/blob/master/OvmfPkg/README。
虚拟机无法启动进入 Arch ISO
首次尝试从 Arch ISO 映像启动虚拟机时,启动过程会挂起。通过在启动菜单中按 e
将 console=ttyS0
添加到内核启动选项,您将获得更多启动消息和以下错误
:: 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
错误消息没有很好地说明真正的问题是什么。问题在于 QEMU 为虚拟机分配的默认 128MB RAM。将限制增加到 1024MB (-m 1024
) 可以解决此问题并使系统启动。之后您可以像往常一样继续安装 Arch Linux。安装完成后,可以减少虚拟机的内存分配。需要 1024MB 是由于 RAM 磁盘要求和安装介质的大小。请参阅 arch-releng 邮件列表上的此消息和此论坛帖子。
Guest CPU 中断未触发
如果您正在按照 OSDev wiki 编写自己的操作系统,或者只是使用 -s
标志通过 QEMU 的 gdb
接口单步执行 guest 架构汇编代码,那么了解许多模拟器(包括 QEMU)通常实现一些 CPU 中断,而留下许多硬件中断未实现是很有用的。了解您的代码是否触发中断的一种方法是使用
-d int
启用在 stdout 上显示中断/异常。
要查看 QEMU 提供的其他 guest 调试功能,请参阅
qemu-system-x86_64 -d help
或将 x86_64
替换为您选择的 guest 架构。
带有 sddm 的 KDE 未在登录时自动启动 spice-vdagent
从 /etc/xdg/autostart/spice-vdagent.desktop
中删除或注释掉 X-GNOME-Autostart-Phase=WindowManager
。[7]
另请参阅
- QEMU 官方网站
- KVM 官方网站
- QEMU 模拟器用户文档
- QEMU Wikibook
- 使用 QEMU 进行硬件虚拟化,作者 AlienBOB(上次更新于 2008 年)
- 构建虚拟军队,作者 Falconindy
- QEMU 文档
- Windows 上的 QEMU
- 维基百科
- Debian Wiki - QEMU
- 网络 QEMU 虚拟 BSD 系统
- gnu.org 上的 QEMU
- FreeBSD 上作为主机的 QEMU
- 使用 QEMU 管理虚拟机 - openSUSE 文档
- IBM Knowledge Center 上的 KVM