QEMU
来自 QEMU 关于页面:“QEMU”是一个广泛使用的开源计算机模拟器和虚拟机。"
当作为模拟器时,可以在一种架构(如x86 PC)下运行另一种架构(如ARM)下的操作系统和程序。通过使用动态转换,它可以获得非常好的性能。
作为虚拟机时,QEMU可以使用其他虚拟机管理程序(如 Xen 或 KVM)来使用 CPU 扩展(HVM)进行虚拟化,通过在主机 CPU 上直接执行客户机代码来获得接近于宿主机的性能。
安装[编辑 | 编辑源代码]
安装 qemu-full包,(或 qemu-base包,一个没有GUI的版本)并根据需要安装下面的可选软件包:
- qemu-block-gluster包 - Glusterfs 设备块支持
- qemu-block-iscsi包 - iSCSI 设备块支持
- samba包 - SMB/CIFS 服务器支持
除了这些之外, qemu-user-static包 提供了 user-mode 下运行静态编译程序的支持。
QEMU 变种[编辑 | 编辑源代码]
QEMU提供了多个变种以供用户在不同场景中使用。
粗略地说,QEMU 有两种运行模式:
- 全系统模拟模式(full-system emulation)
- 在该模式下, QEMU 将会模拟一个完整的系统,包含一个或多个处理器以及各种外围设备。这种模式更加贴近真实的系统,且这种模式不要求被模拟的客户机系统是 Linux,但它的速度较慢。
- QEMU 中启用 full-system 模式的命令依照如下规则进行命名
qemu-system-目标机器架构
,例如qemu-system-x86_64
用于模拟 64 位架构 CPU,qemu-system-i386
模拟 32 位 Intel 架构 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-arm
和qemu-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。
QEMU 的图形前端[编辑 | 编辑源代码]
与其他的虚拟化程序如 VirtualBox 和 VMware 不同, QEMU不提供管理虚拟机的GUI(运行虚拟机时出现的窗口除外),也不提供创建具有已保存设置的持久虚拟机的方法。除非您已创建自定义脚本以启动虚拟机,否则必须在每次启动时在命令行上指定运行虚拟机的所有参数。
Libvirt提供了一种管理 QEMU 虚拟机的便捷方式。有关可用的前端,请参阅 libvirt 客户端列表。
QEMU的其他图形化前端:
- AQEMU — QEMU GUI Qt5编写.
创建新虚拟系统[编辑 | 编辑源代码]
创建硬盘镜像[编辑 | 编辑源代码]
除非直接从 CD-ROM 或网络引导(并且不安装系统到本地),运行 QEMU 时都需要硬盘镜像。硬盘镜像是一个文件,存储虚拟机硬盘上的内容。
一个硬盘镜像可能是 raw镜像, 和客户机器上看到的内容一模一样,并且将始终使用主机上的来宾硬盘驱动器的全部容量。此方法提供的I / O开销最小,但可能会浪费大量空间,因为guest虚拟机上未使用的空间无法在主机上使用。
另外一种方式是qcow2 格式,仅当客户系统实际写入内容的时候,才会分配镜像空间。对客户机器来说,硬盘大小表现为完整大小,即使它可能仅占用主机系统上的非常小的空间。此映像格式还支持QEMU快照功能(有关详细信息,请参阅 #通过 monitor console 创建快照和管理快照)。但是,使用此格式而不是 raw 可能会影响性能。
QEMU 提供 qemu-img
命令创建硬盘镜像.例如创建一个 4 GB raw 格式的镜像:
$ qemu-img create -f raw image_file 4G
您也可以用 -f qcow2
创建一个 qcow2 镜像。
用 dd
或 fallocate
也可以创建一个 raw 镜像。
dd
或 fallocate
创建一个所需大小的 raw 镜像。覆写(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
调整镜像大小[编辑 | 编辑源代码]
执行 qemu-img
带 resize
选项调整硬盘驱动镜像的大小.它适用于 raw 和 qcow2. 例如, 增加镜像 10 GB 大小, 运行:
$ qemu-img resize disk_image +10G
在磁盘映像扩容后,必须使用虚拟机内部系统的分区工具对该镜像进行分区并格式化后才能真正开始使用新空间。 在收缩磁盘映像时,必须首先使用虚拟机内部系统的分区工具减少分该分区的大小,然后相应地收缩磁盘映像,否则收缩磁盘映像将导致数据丢失!
准备安装介质[编辑 | 编辑源代码]
要将操作系统安装到您的磁盘镜像, 你需要操作系统的安装介质 (例如 光盘, USB设备, 或 ISO 镜像). 不要挂载安装介质,因为 QEMU 要直接访问媒体。
/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镜像内启动了。(参阅 #运行虚拟化的系统 )
-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-i386
或 qemu-system-x86_64
, 取决于客户机架构)用来运行虚拟化的客户机. 用法是:
$ qemu-system-i386 options disk_image
所有 qemu-system-*
的选项是相同的,参见 qemu(1) 查看文档和所有选项
默认 QEMU会在窗口中显示虚拟机的视频输出.有一点要记住:当您单击QEMU窗口,鼠标指针被捕获。要放开,按 Ctrl+Alt+g
.
-runas
选项让QEMU放弃root权限启用 KVM[编辑 | 编辑源代码]
KVM(基于内核的虚拟机)完全虚拟化必须要您的 Linux 内核和硬件支持,并且必须加载必要的内核模块。更多信息参见 KVM。
要在 KVM 模式中启动 QEMU, 追加 -enable-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 ..
-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
。宿主机和虚拟机数据交互[编辑 | 编辑源代码]
网络[编辑 | 编辑源代码]
我们可以利用任何支持文件传输的网络协议实现客户机和宿主机之间的数据交互, 例如 NFS, SMB, NBD, HTTP, FTP, 或 SSH, 当然这么做的前提是你已经配置好二者之间的网络,且在系统上启动了相应的服务程序。
在默认情况下,用户模式的客户机能够通过10.0.2.2这个IP访问到宿主机。任何运行于宿主机上的服务端程序都可以通过这个地址被访问到,比如说我们可以通过这个IP访问到宿主机上的SSH服务器或SMB服务器。因此在这种情况下,客户机能够挂载宿主机通过SMB or NFS暴露出来的目录,也可以访问宿主机上的HTTP服务器等。 通常情况下宿主机无法访问客户机上的服务,不过你也可以通过一些特殊的网络配置达到这个目的 (参阅#Tap 网络)
QEMU 端口转发[编辑 | 编辑源代码]
QEMU能够将宿主机的端口转发到客户机上以实现一些功能,例如从宿主机上访问客户机的SSH端口。
举个例子,将宿主机上的10022端口与客户机上的22 (SSH) 端口进行绑定, 对应的QEMU命令如下:
$ qemu-system-x86_64 disk_image -nic user,hostfwd=tcp::10022-:22
确认你客户机上的sshd程序正在运行,然后可以通过如下命令连接到客户机的SSH端口
$ ssh guest-user@localhost -p 10022
你可以用 SSHFS 把客户机的整个文件系统都挂到宿主机上,这样就可以在宿主机上对客户机的文件系统进行读写了。
想进行多端口转发的话, 只需要在-nic
参数中指定多个hostfwd
, 以VNC端口为例:
$ qemu-system-x86_64 disk_image -nic user,hostfwd=tcp::10022-: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 disk_image -net nic -net user,smb=shared_dir_path
shared_dir_path
就是你想要在宿主机和客户机之间共享的目录。
接着,在客户机内,你应该能够通过10.0.2.4访问到名为qemu的共享文件夹。例如在Windows Explorer中前往 \\10.0.2.4\qemu
这个地址。
- 如果你像这样多次指定共享选项
-net user,smb=shared_dir_path1 -net user,smb=shared_dir_path2
or-net user,smb=shared_dir_path1,smb=shared_dir_path2
qemu只会共享参数中最后的一个目录。 - 如果你不能访问共享文件夹且客户机系统为 Windows, 请检查 NetBIOS 协议是否被启用 并确认防火墙没有屏蔽NetBIOS协议的 端口
- 如果你不能访问共享文件夹且客户机系统为 Windows 10 Enterprise 或 Education 或 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启动的SMB服务器。共享多文件的另一个方法是在配置文件里加入额外的共享路径,就像下面这样
echo "[myshare] path=another_path read only=no guest ok=yes force user=username" >> $conf
这个共享文件夹可以在客户机内通过\\10.0.2.4\myshare
访问。
使用直通式文件系统和VirtFS[编辑 | 编辑源代码]
参阅 QEMU 文档.
在宿主机上挂载客户机的分区[编辑 | 编辑源代码]
在虚拟机运行时,我们仍可以将磁盘镜像内的分区作为loop设备挂载到宿主机,不过一些特殊的磁盘镜像文件并不支持这样的操作,比如说qcow2格式的文件,尽管说在平时能够使用qemu-nbd
将qcow2挂载到文件系统上。
手动指出偏移量[编辑 | 编辑源代码]
挂载磁盘镜像中分区的一个方法是手动指定挂载的偏移量,你可以使用类似下面的命令完成这个操作:
# 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驱动支持loop设备的分区,不过默认情况下它是关闭的,可以通过下面的方法启用:
- 卸载所有loop设备 (比如说卸载所有挂载的镜像)。
- 卸下 the
loop
内核模块, 以max_part=15
参数重新加载该模块。 此外,你可以用max_loop
参数设置loop设备的最大数量。
/etc/modprobe.d
增加一个条目使得每次加载loop模块时带上 max_part=15
参数, 或者把 loop.max_part=15
加入内核命令行, 这得看你的内核是否有编译 loop.ko
模块将镜像文件设置为loop设备:
# 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
这条命令将会为你设置loop设备,并在 /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的分区设备,我们便无法在这个分区上安装bootloader程序,进而也就没有办法启动虚拟机了。要启动这类虚拟机你需要手动指定 kernel 和 initrd, 或者用 Device-mapper/线性 RAID/Linux网络块设备模拟一块带MBR的磁盘。
手动指定 kernel 和 initrd[编辑 | 编辑源代码]
QEMU支持直接加载 Linux kernels 和 init ramdisks, 从而绕过了类似 GRUB 这类的bootloader程序。它会把包含根文件系统的物理分区作为虚拟机的虚拟磁盘,然后启动虚拟机。通过类似下面的命令可以完成这些操作:
/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.vmdk
和 file-pt.vmdk
, 后一个是MBR的副本, file.vmdk
。 对目标分区外的数据或MBR本身的读操作将会返回0, 而写操作则会被丢弃。
设备映射器[编辑 | 编辑源代码]
与使用VMDK描述文件类似的方法就是使用设备映射器,设备映射器会在目标分区内添加一份带有有loop设备的MBR文件。在此种情况下,不要求虚拟磁盘与原始分区拥有同样的大小。首先创建一个包含MBR的文件:
$ dd if=/dev/zero of=/path/to/mbr count=2048
在这里,我们通过这个命令,使用现代化的磁盘分区程序创建了一个符合分区对齐策略的文件,总共大小为1MB (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原生镜像使用了,额外的步骤需要你去在虚拟磁盘上创建一个分区表(参阅关于 linear RAID 的部分, 它可以作为创建分区表的一个范例), 以及相应的bootloader代码 (代码会存放在
/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:minor
将 major:minor
转化为类似 /dev/blockdevice
的名字)。若要删除创建的设备,请使用 dmsetup remove qemu
和 losetup -d $loop
。
设备映射器方法一个可能的应用场景与Windows XP有关,比如说要在Windows XP安装中进行多引导配置,并且有可能要采用混合分区方案(从物理硬件上说,Windows XP可能是唯一使用MBR分区表的系统,更现代化的操作系统能使用GUID分区表)。Windows XP支持硬件配置文件,因此同样的安装可以选择不同的硬件配置完成(在本例中是裸机与虚拟机),而Windows仅需为每个配置文件安装一次新检测到的硬件的驱动即可。注意在这个例子里, 要更新MBR副本中bootloader代码的部分,使得它直接从 /dev/hdaN
加载Windows XP而不是系统内自带的多引导bootloader(比如GRUB)。或者,将包含bootloader安装的启动分区之副本包含于虚拟磁盘中,同样也可起到类似MBR的作用。
线性 RAID[编辑 | 编辑源代码]
你同样能用 线性模式(需要 linear.ko
内核驱动)的RAID以及一块loop设备完成这件事。
首先创建一个用于容纳MBR的小文件:
$ dd if=/dev/zero of=/path/to/mbr count=32
这样就创建了一个 16 KB(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 KB),软件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在磁盘镜像上设置任何一个bootloader。
网络块设备[编辑 | 编辑源代码]
除了上面描述的各种方法外,你也可以用 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 [...]
网络[编辑 | 编辑源代码]
采用TAP设备和网桥的虚拟网络的性能应该会比使用用户模式网络或VDE要好,原因在于TAP设备和网桥是在内核中实现的。
此外,虚拟网络的性能可以通过将网络设备直接注册到虚拟机中改善,这比默认情况下模拟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 import sys import zlib if len(sys.argv) != 2: print("usage: %s <VM Name>" % sys.argv[0]) sys.exit(1) crc = zlib.crc32(sys.argv[1].encode("utf-8")) & 0xffffffff crc = str(hex(crc))[2:] 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来访问物理主机的网络。
ping
)将不起作用。 请勿使用ping
测试网络连接。 要在来宾中执行ping操作,请参阅Sysctl#Allow unprivileged users to create IPPROTO_ICMP sockets。如果主机已连接Internet,则此默认配置可以使您的虚拟机轻松访问Internet。但是如果您同时启动多个虚拟机,则虚拟机将无法在外部网络上直接看到,虚拟机也将无法相互通信。
QEMU的用户模式网络可以提供更多功能,例如内置TFTP或SMB服务器,将主机端口重定向到虚拟机(例如,允许SSH连接到虚拟机)或将虚拟机连接到VLAN,以便它们可以彼此通信。 有关更多详细信息,请参见-net user
标志上的QEMU文档。
但是,用户模式网络在效用和性能上都有局限性。更高级的网络配置需要使用TAP设备或其他方法。
/etc/resolv.conf
文件进行符号链接,否则虚拟机系统中的DNS查找将无法进行。Tap 网络[编辑 | 编辑源代码]
Tap devices是一个Linux内核特性,允许您创建作为真实网络接口的虚拟网络接口。发送到tap接口的包将被传递到一个用户空间程序(如QEMU),该程序将自己绑定到该接口。
QEMU可以为虚拟机使用tap网络,因此发送到tap接口的包将被发送到虚拟机,并显示为来自虚拟机中的网络接口(通常是以太网接口)。相反,虚拟机通过其网络接口发送的所有内容都将出现在tap接口上。
Linux桥接驱动程序支持Tap设备,因此可以将Tap设备彼此桥接在一起,也可以连接其他主机接口,如eth0
。如果您希望您的虚拟机能够相互通信,或者希望LAN上的其他机器能够与虚拟机通信,那么这是非常理想的方案。
eth0
,您的虚拟机将直接出现在外部网络上,这将使它们遭受攻击的可能。根据您的虚拟机可以访问的资源,您可能需要采取所有precautions来保护您的虚拟机。如果风险太大,虚拟机没有资源或您设置多个虚拟机,一个更好的解决方案可能是使用host-only networking建立NAT。在这种情况下,您只需要在主机上安装一个防火墙,而不是为每个虚拟机安装多个防火墙。正如在用户模式网络部分中指出的,tap设备提供比用户模式具有更高的网络性能。如果虚拟机中的操作系统支持virtio网络驱动程序,那么网络性能也会显著提高。假设使用tap0设备,virtio驱动程序在客户端上使用,并且没有使用脚本来帮助启动/停止网络,使用下面的qemu命令:
-net nic,model=virtio -net tap,ifname=tap0,script=no,downscript=no
但是,如果已经使用带有virtio网络驱动程序的Tap设备,则甚至可以通过启用vhost来提高网络性能,例如:
-net nic,model=virtio -net tap,ifname=tap0,script=no,downscript=no,vhost=on
详情请参考:http://www.linux-kvm.com/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 --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 bridge0 allow bridge1 ...
现在启动虚拟机:
$ qemu-system-i386 -net nic -net bridge,br=bridge0 [...]
在多个TAP设备的情况下,最基本的用法是要为所有NIC指定VLAN:
$ qemu-system-i386 -net nic -net bridge,br=bridge0 -net nic,vlan=1 -net bridge,vlan=1,br=bridge1 [...]
手工创建网桥[编辑 | 编辑源代码]
下面介绍如何将虚拟机连接到主机接口,如eth0
,这可能是最常见的配置。这种配置使虚拟机看起来直接位于外部网络,与物理主机位于同一以太网段。
我们将用桥适配器替换普通的以太网适配器,然后将普通的以太网适配器绑定到它。
- 安装bridge-utils包,它提供
brctl
来操作网桥。
- 启用IPv4转发:
# sysctl 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
,或将以下脚本更改为网桥的名称。 - 创建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 USERID=$(whoami) # Get name of newly created TAP device; see https://bbs.archlinux.org/viewtopic.php?pid=1285079#p1285079 precreationg=$(/usr/bin/ip tuntap list | /usr/bin/cut -d: -f1 | /usr/bin/sort) sudo /usr/bin/ip tuntap add user $USERID mode tap postcreation=$(/usr/bin/ip tuntap list | /usr/bin/cut -d: -f1 | /usr/bin/sort) IFACE=$(comm -13 <(echo "$precreationg") <(echo "$postcreation")) # This line creates a random MAC address. The downside is the DHCP server will assign a different IP address each time printf -v macaddr "52:54:%02x:%02x:%02x:%02x" $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff )) $(( $RANDOM & 0xff)) $(( $RANDOM & 0xff )) # Instead, uncomment and edit this line to set a static MAC address. The benefit is that the DHCP server will assign the same IP address. # macaddr='52:54:be:36:42:a9' qemu-system-i386 -net nic,macaddr=$macaddr -net tap,ifname="$IFACE" $* sudo ip link set dev $IFACE down &> /dev/null sudo ip tuntap del $IFACE mode tap &> /dev/null
然后,要启动VM,可以这样做:
$ run-qemu -hda myvm.img -m 512 -vga std
- 出于性能和安全原因,建议禁用网桥上的防火墙[1]:
/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
运行sysctl -p /etc/sysctl.d/10-disable-firewall-on-bridge.conf
立即应用更改。
参见libvirt wiki和Fedora bug 512206。如果在引导过程中sysctl发现关于不存在文件的错误,请在引导时加载bridge
模块。参见Kernel module#Automatic module loading with systemd[损坏的链接:无效的章节]。
或者,您可以配置iptables,通过添加类似这样的规则,允许所有流量通过桥进行转发:
-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT
物理设备和Tap设备之间通过iptables进行网络共享[编辑 | 编辑源代码]
桥接网络能在有线接口(例如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 配置网络[编辑 | 编辑源代码]
何为VDE?[编辑 | 编辑源代码]
VDE全称为Virtual Distributed Ethernet,作为uml_switch的一个扩展,是一个用于管理虚拟网络的工具包
其基本的思想是创建一个虚拟的开关,就如插座那样,允许虚拟机和物理机通过"插入"连接彼此。下面的配置非常简单,然而,VDE的功能远比展示的更强大,其能够接入虚拟开关,在不同的主机上运行它们并监听开关上的通信。恳请您阅读该项目的文档获取更多信息。
本方法的优点在于无需sudo特权,普通用户一般没有运行modprobe的权限。
基础操作[编辑 | 编辑源代码]
可以通过安装official repositories中的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-i386 -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 "$TAP_NETWORK"/"$TAP_MASK" -o "$NIC" -j MASQUERADE # Bring tap interface down ip link set "$TAP_DEV" down # Kill VDE switch pgrep -f 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
权限至可执行
# chmod u+x /etc/systemd/scripts/qemu-network-env
可以像其他服务一样,start qemu-network-env.service
服务。
备用方法[编辑 | 编辑源代码]
如果上面的方法不起作用,或者说不想折腾内核配置、TUN、dnsmasq和iptables,可以采用下面的方法,也可以获得同样的效果,
# vde_switch -daemon -mod 660 -group users # slirpvde --dhcp --daemon
然后,启动VM并连接至主机网络中:
$ qemu-system-i386 -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文档了解更多的相关参数。
图形[编辑 | 编辑源代码]
QEMU 可以使用一下几个图形输出:std, cirrus, vmware, qxl, xenfs 和 vnc。
使用 vnc
选项,你可以单独运行客户机,并且通过 VNC 连接。其他选项是使用std
, vmware
, cirrus
:
std[编辑 | 编辑源代码]
使用 -vga std
你可以得到最高 2560 x 1600 像素的分辨率。从 QEMU 2.2 开始是默认选项。
qxl[编辑 | 编辑源代码]
QXL是一个支持2D的并行虚拟化图形驱动。需要在客户机中安装驱动并在启动QEMU时设置-vga qxl
选项。你可能也会想使用#SPICE优化QXL的图形表现。
在Linux客户机中,需要加载qxl
和bochs_drm
这两个内核模块,以获得一个比较好的效果。
QXL设备的默认VGA内存大小为16M,这样的内存大小最高支持QHD (2560x1440)的分辨率,如果想要一个更高的分辨率,请增加vga_memmb。
vmware[编辑 | 编辑源代码]
尽管Bug有点多,但相比于std和cirrus它的表现会更好。对于Arch Linux客户机来说可以安装xf86-video-vmware包和xf86-input-vmmouse包获取VMware驱动。
virtio[编辑 | 编辑源代码]
virtio-vga
/ virtio-gpu
是一个基于virgl的3D并行虚拟化图形驱动。目前依旧处于开发中,仅支持最近的(>= 4.4)的Linux客户机,且需要以gallium-drivers=virgl
选项编译mesa包 (>=11.2)。
若要在客户机上启用3D加速,那么需要用-vga virtio
选项选择此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 -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
在 TCP5930
端口上进行 spice 频道的监听,允许客户端不经验证即可连接。
-spice unix,addr=/tmp/vm_spice.socket,disable-ticketing
代替上述参数。-device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0
在 virtio-serial 设备上为 spice vdagent 打开一个端口。-chardev spicevmc,id=spicechannel0,name=vdagent
为该端口添加一块 spicevmc 字符设备。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 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 -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之间进行复制和粘贴等。
- xf86-video-qxl包: Xorg X11 qxl视频驱动
对于其它的操作系统客户机, 参照spice-space download的Guest章节。
开启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的示例。
基本的口令验证[编辑 | 编辑源代码]
可以通过使用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
音频[编辑 | 编辑源代码]
创建音频后端[编辑 | 编辑源代码]
-audiodev
标识用于设定后端音频驱动及其相关选项。在 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 驱动[编辑 | 编辑源代码]
QEMU为用户提供并行虚拟化块设备和网络设备的能力,其是借助virtio驱动实现的,拥有更好的性能表现以及更低的开销。
- virtio块设备需要使用
-drive
指定一个disk image的参数,且需要带上if=virtio
参数:
$ qemu-system-x86_64 -boot order=c -drive file=disk_image,if=virtio
- 网络配置也是类似的:
$ qemu-system-x86_64 -nic user,model=virtio-net-pci
(Arch) Linux 客户机[编辑 | 编辑源代码]
要在Arch Linux客户机中使用virtio设备,必须在客户端加载以下模块:virtio_pci
、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”
(例如:“v”da
, “v”db
,等等)。因此,当从virtio磁盘启动时,需要在/etc/fstab
和/boot/grub/grub.cfg
中进行更改。。
/etc/fstab
和bootloader中通过UUID引用磁盘时,不需要执行任何操作。关于使用KVM进行半虚拟化的更多信息,可以参考Boot_from_virtio_block_device。
您可以安装qemu-guest-agent包来实现对QMP命令的支持,从而增强管理程序的管理能力。安装完成后,您需要启动qemu-guest-agent.service
。
Windows 客户机[编辑 | 编辑源代码]
块设备驱动[编辑 | 编辑源代码]
安装一个新的Windows[编辑 | 编辑源代码]
Windows没有自带virtio驱动,因此需要在安装时加载该驱动。通常有两种方法:通过Floppy Disk或通过ISO文件加载。这两种镜像文件都可以从Fedora 仓库下载。
通过floppy加载的方法可能比较困难,因为你需要在QEMU一启动的时候就按下F6(Windows主机上需要按下Shift-F6),但是由于我们是通过VNC console连接到Windows的,这个过程会消耗一些时间,导致我们难以办到这一点。不过你可以试试在boot序列中加入delay延迟,参阅qemu(1)了解如何在boot中加入delay。
通过ISO加载是一个比较好的方法,但该方法只对Windows Vista和Windows Server 2008及其之后的版本有效。这个方法的具体操作是在主磁盘设备和Windows安装盘外挂载一个额外的cdrom设备,将系统镜像与virtio驱动一同加载:
$ qemu-system-x86_64 ... \ -drive file=windows_disk_image,index=0,media=disk,if=virtio \ -drive file=windows.iso,index=2,media=cdrom \ -drive file=virtio.iso,index=3,media=cdrom \ ...
在安装过程中,Windows Installer会询问你“Where do you want to install Windows?”,其会返回一个警告表示没有找到任何磁盘设备。接下来跟着如下示例中的步骤进行操作(基于Windows Server 2012 R2 with Update):
- Select the option Load Drivers.
- Uncheck the box for Hide drivers that are not compatible with this computer's hardware.
- Click the browse button and open the CDROM for the virtio iso, usually named "virtio-win-XX".
- Now browse to
E:\viostor\[your-os]\amd64
, select it, and confirm.
现在应该能看到virtio磁盘出现在列表中了,等待着被选中、格式化并安装。
将现有的Windows VM转为使用virtio[编辑 | 编辑源代码]
若要将现有的Windows客户机改为从virtio磁盘中启动,前提是客户机中有virtio驱动,且该驱动在boot期间就被加载。
可以在Fedora 仓库内找到virtio磁盘驱动。
现在,先创建一个新的磁盘镜像,用于搜索virtio驱动:
$ qemu-img create -f qcow2 fake.qcow2 1G
挂载fake磁盘(处于virtio模式下),带有驱动的CD-ROM,运行原本的Windows客户机(boot磁盘依旧是处于IDE模式中):
$ qemu-system-x86_64 -m 4G -drive file=windows_disk_image,if=ide -drive file=fake.qcow2,if=virtio -cdrom virtio-win-0.1-185.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=windows_disk_image,if=virtio
加载virtio驱动需要以安全模式进行boot,若不需要则可以返回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 驱动[编辑 | 编辑源代码]
如果想要追踪客户机内存状态(比如通过virsh
的dommemstat
命令)或者在运行时改变客户机内存大小(尽管依然无法改变实际的内存大小,不过可以通过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
或?
命令,或者阅读official QEMU documentation获得完整的命令列表。
访问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
。还要记住的是,最好对firewall进行配置,该连接是完全不进行认证和加密的,因此需要通过防火墙确保本地网络环境是可信的。UNIX 套接字[编辑 | 编辑源代码]
通过-monitor unix:socketfile,server,nowait
参数运行QEMU,之后就可以通过socat包或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
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的终端下访问到监视器的。
在Monitor conosle下向虚拟机发送按键行为[编辑 | 编辑源代码]
由于在某些配置下,宿主机可能会拦截一些按键组合另作他用,这导致要在虚拟机中触发一些特定按键组合变得有些困难(一个显然的例子就是Ctrl+Alt+F*
组合,该组合用于改变当前的tty)。我们采用在monitor console下发送按键组合的方式解决该问题。只需切换到monitor console下,然后使用sendkey
命令,即可将按键转发至虚拟机中,例如:
(qemu) sendkey ctrl-alt-f2
通过 monitor console 创建快照和管理快照[编辑 | 编辑源代码]
有时候我们很需要将虚拟机的当前状态进行保存,或是将虚拟机重置到之前的快照状态,而且最好是随时能进行这些操作。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
的堆叠文件,该快照也将成为新的active层
技巧[编辑 | 编辑源代码]
改善虚拟机的性能表现[编辑 | 编辑源代码]
底下是一些可以改善虚拟机性能表现的技术,例如:
- 启用#启用 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
. - 如果宿主机有多个核心,可以用
-smp
选项为客户机分配更多核心。 - 检查是否为虚拟机分配的足够的内存。默认情况下,QEMU仅仅为每台虚拟机分配128MiB的内存,可以使用
-m
选项分配更多的内存。例如,-m 1024
代表启动一台内存为1024MiB的虚拟机。 - 如果客户机操作系统支持相关的驱动,可以使用virtio创建网络设备或块设备,例如:
$ qemu-system-x86_64 -net nic,model=virtio -net tap,if=tap0,script=no -drive file=disk_image,media=disk,if=virtio
- 使用TAP设备代替user-mode网络,参阅#Tap 网络。
- 如果客户机需要进行大量的磁盘写工作,在宿主机文件系统上设置合适的挂载选项可以优化该工作。例如,可以用
barrier=0
选项挂载一个ext4 file system。在使用这些性能强化选项之前最好阅读相关文档,因为性能上的提升通常伴随着数据完整性下降的代价。 - 如果有一块原始磁盘镜像,你可能会想要禁用cache:
$ qemu-system-x86_64 -drive file=disk_image,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-managerGUI中虚拟机的Boot Options,选择"Start virtual machine on host boot up"实现开机自启。
通过systemd service实现[编辑 | 编辑源代码]
可以用如下的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
接着创建per-VM配置文件,命名为/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 monitor是通过-monitor telnet:..
选项暴露至telnet,因而关闭虚拟机是通过nc
命令在monitor console中发送system_powerdown
,完成ACPI关机的工作。在另一个例子里,使用的则是SSH。
若要设置启动时运行哪个虚拟机,enableqemu@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 3)控制器进行模拟,然后将物理设备通过-device usb-host,..
选项进行添加。在本节中的剩余部分,controller_id要么是ehci
,要么是xhci
。
接着,这里有两种方法通过qemu连接到宿主机的USB:
- 识别出该设备,并将其连接至任一总线以及宿主机上的地址,通用的语法如下:
-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-host
hostdevice
属性,语法为:-device qemu-xhci,id=xhci -device usb-host,bus=xhci.0,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
使用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-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节获得更多信息)。
开启KSM[编辑 | 编辑源代码]
Kernel Samepage Merging (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启用,而正好有准备进行归并的页,(比如,至少有两个相似的VM正在运行),那么/sys/kernel/mm/ksm/pages_shared
应会是非零的。参阅https://www.kernel.org/doc/html/latest/admin-guide/mm/ksm.html了解更多信息。
$ grep . /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
选项的值。
复制和粘贴[编辑 | 编辑源代码]
在宿主机和客户机之间共享剪贴板的方法之一是使用SPICE远程桌面协议,通过SPICE客户端访问客户机,你需要遵照#SPICE节中描述的步骤,通过该方式运行的客户机将支持与宿主机进行复制粘贴的操作。
Windows专用说明[编辑 | 编辑源代码]
QEMU可以运行从Windows 95至Windows 10的任何版本Windows。
在QEMU中也可以运行Windows PE。
尝试使一些不支持虚拟机的软件正常运行[编辑 | 编辑源代码]
关闭虚拟cpu的hypervisor
在 -cpu 参数后加入 -hypervisor
如果你使用 libvirt 在 <cpu> 下加入 <feature policy='disable' name='hypervisor'/>
快速启动设置[编辑 | 编辑源代码]
根据此论坛帖子所解释的那样,对于Windows8及之后的客户机最好在Control Panel的Power Options中禁用"Turn on fast startup (recommended)",该设置导致客户机在每次启动时卡死。
为了正确应用对-smp
选项进行的更改,可能也需要禁用快速启动。
远程桌面协议[编辑 | 编辑源代码]
如果你使用一个MS Windows客户机,可能会想使用RDP连接到客户机VM。若你正使用VLAN或与客户机处于同一个网络中,先使用:
$ qemu-system-x86_64 -nographic -net user,hostfwd=tcp::5555-:3389
接着通过rdesktop包或freerdp包连接到客户机。例如:
$ 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包 - armutils-gitAUR可以完成该工作。其中,chroot环境是通过Arch Linux ARM的一个镜像tarball文件创建的 - 参阅[2]了解此种方法的细节描述。
无论哪种方式,都应该可以从chroot运行pacman并安装更多软件包,编译大型库等。由于可执行文件是针对ARM体系结构的,因此需要QEMU转换为x86。 。
在x86_64宿主机上从AUR安装binfmt-qemu-staticAUR[损坏的链接:package not found]和qemu-user-static包。binfmt-qemu-static 将会负责qemu二进制文件注册至binfmt服务的部分。
Restart systemd-binfmt.service
需要qemu-user-static包执行其他架构上的程序,这个包与qemu-arch-extra包[损坏的链接:replaced by 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的ls
命令。注意如果没有chroot,这些都不会生效,因为那样的话它们将会试图寻找不存在宿主机系统上的一些二进制文件。
qemu-user-static包 允许自动地用为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
每种可执行的文件都须在此处列出。
如果未开启,请reinstall binfmt-qemu-staticAUR[损坏的链接:package not found]并restartsystemd-binfmt.service
。
将SD卡挂载至/mnt/sdcard
(设备名可能不同):
# mkdir -p /mnt/sdcard # mount /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-nspawnchroot到ARM环境中:
# systemd-nspawn -D /mnt/sdcard -M myARMMachine --bind-ro=/etc/resolv.conf
--bind-ro=/etc/resolv.conf
是可选的,其在chroot中提供了一个可用的网络DNS。
处理常见问题[编辑 | 编辑源代码]
鼠标指针抖动或者不稳定[编辑 | 编辑源代码]
如果QEMU中鼠标指针不受控制地跳来跳去,那么启动QEMU之前在命令进行如下设置或许能解决该问题:
$ export SDL_VIDEO_X11_DGAMOUSE=0
如果成功了,可以把这条命令加入到你的 ~/.bashrc
文件中
看不见鼠标指针[编辑 | 编辑源代码]
使用-show-cursor
选项启动QEMU以显示指针。
如果这么做不起作用的话,请确保你的显示设备被正确设置了。例如: -vga qxl
。
还可尝试 #鼠标整合 中提到的 -usb -device usb-tablet
。该选项会覆盖默认采用的PS/2鼠标仿真,将鼠标作为外设在宿主机和客户机之间同步指针位置。
有两个不同的鼠标指针[编辑 | 编辑源代码]
遵循 #鼠标整合 提到的方法。
使用VNC时键盘出现问题[编辑 | 编辑源代码]
使用VNC时,您可能会遇到链接中所描述的键盘问题(详细信息)here。 解决的方法是不使用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
的参数传递给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.conf
将 group = "78"
改为 group = "kvm"
避免产生这个错误。
启动Windows VM时产生 "System Thread Exception Not Handled"[编辑 | 编辑源代码]
Windows 8或Windows 10 guest虚拟机在启动时可能会引发通用兼容性异常,即“System Thread Exception Not Handled”,这通常是由实体机上旧版驱动程序的异常行为引起的。在KVM机器上,通常可以通过将CPU模型设置为 core2duo
来解决此问题。
某些Windows游戏/程序导致蓝屏出现[编辑 | 编辑源代码]
有时,一些能在物理机上正常运行的程序在虚拟机中会意外地崩溃。如果在运行 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的蓝屏做了一些处理,蓝屏信息中将无法识别驱动。
VM中的应用程序有很高的延迟,或是需要等待很长的时间才会启动[编辑 | 编辑源代码]
这可能是由于VM中的可用熵不足所致。考虑通过将VirtIO RNG设备添加到VM,或安装诸如Haveged之类的熵生成守护程序,来允许来宾访问主机的熵池。有趣的是,OpenSSH需要一段时间才能在熵不足的情况下开始接受连接,而其日志却不会显示原因。
中断时间过长以及运行不流畅[编辑 | 编辑源代码]
此问题表现为小停顿(断断续续),在图形密集型应用程序(例如游戏)中尤其明显。
- 原因之一是CPU的节能功能,该功能由CPU frequency scaling控制。请对所有处理器核心进行修改,将其更改为
performance
。 - 另一个可能的原因是PS/2输入。从PS/2切换到Virtio输入,请参阅PCI passthrough via OVMF#Passing keyboard/mouse via Evdev。
QXL导致视频分辨率降低[编辑 | 编辑源代码]
QEMU 4.1.0版本带来了一个回退问题,当使用SPICE时,会将QXL视频降到低分辨率。 [3]例如,当KMS启动时,文本分辨率可能会低至4x10个字符。尝试提高GUI分辨率时,它可能会达到最低的支持分辨率。
解决方法是,以这种形式创建设备:
-device qxl-vga,max_outputs=1...
在 VM initramfs 时挂起[编辑 | 编辑源代码]
Linux 5.2.11带来了KVM回退的问题。某些情况下,在加载或运行initramfs的早期引导阶段,VM可能会永久挂起。 [4] Linux 5.3修复了该问题。主机会显示QEMU使用了100%CPU * 虚拟CPU数量。报告反馈的情况是主机使用了超线程,且给虚拟机分配了超过主机上 nproc
/2 个虚拟CPU。尚不清楚究竟是什么情况触发某个线程删除了内存区域而导致此情况。解决方法是:
- Upgrade to Linux 5.3.
- Downgrade to Linux 5.2.10
- 修复之前, 不要给虚拟机分配超过
nproc
/2 个虚拟CPU。 - 自定义Linux进行编译, 回退到commit 2ad350fb4c之前 (注意,这个操作会将一个移除内存插槽时触发的回退Bug再次引入)。
使用启用了安全启动的OVMF时,VM无法启动[编辑 | 编辑源代码]
edk2-ovmf包中的 /usr/share/edk2-ovmf/x64/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所需要的更多选项。
客户机上的中断没有被触发[编辑 | 编辑源代码]
如果你是按照OSDev Wiki来编写自己的操作系统,或者只是使用QEMU的gdb
接口和-s
标志,您需要知道的是,很多仿真器(包括QEMU)通常会实现一些CPU中断,同时还有许多硬件中断没有被实现。了解代码是否触发中断的一种方法是使用:
-d int
可以在输出中查看 interrupts/exceptions。
如要查看QEMU还提供了什么客户机调试功能,使用如下命令:
qemu-system-x86_64 -d help
或者将 x86_64
替换为你选择的其他架构
参阅[编辑 | 编辑源代码]
- Official QEMU website
- Official KVM website
- QEMU Emulator User Documentation
- QEMU Wikibook
- Hardware virtualization with QEMU by AlienBOB (last updated in 2008)
- Building a Virtual Army by Falconindy
- Lastest docs
- QEMU on Windows
- Wikipedia
- Debian Wiki - QEMU
- QEMU Networking on gnome.org[失效链接 2022-09-22 ⓘ]
- Networking QEMU Virtual BSD Systems
- QEMU on gnu.org
- QEMU on FreeBSD as host
- KVM/QEMU Virtio Tuning and SSD VM Optimization Guide[失效链接 2022-09-22 ⓘ]
- Managing Virtual Machines with QEMU - openSUSE documentation
- KVM on IBM Knowledge Center