2.2 内存
内存是一个非常重要的部件,它是与 CPU 沟通的一个桥梁。
在通过 QEMU 命令行启动客户机时设置内存的参数是-m:
-mmegs # 设置客户机的内存为 megs MB 大小
EPT 和 VPID
EPT(Extended Page Tables,扩展页表),属于 Intel 的第二代硬件虚拟化技术,它是针对内存管理单元(MMU)的虚拟化扩展。如果只是一台物理服务器,这个物理地址就只为一个操作系统服务,但如果进行了虚拟化部署,有多个虚拟机时,就存在着稳定性的隐患。因为在进行 VM Entry(虚拟机进入)与 VM Exit(虚拟机退出)时(尤其是后者),都要对内存页进行修改。但物理内存是多个虚拟机共享的,因此不能让虚拟机直接访问物理地址,否则一个虚拟机出现内存错误,就会殃及整个物理服务器的运行。所以必须要采取虚拟地址,而 EPT 的作用就在于加速从虚拟机地址至主机物理地址的转换过程,节省传统软件处理方式的系统开销。
VPID(Virtual-Processor Identifiers,虚拟处理器标识)。是对现在的 CPUID 功能的一个强化,因为在每个 CPU 中都有一个 TLB,用来缓存逻辑地址到物理地址的转换表,而每个虚拟机都有自己的虚拟 CPU 来对应。所以,在进行迁移时要进行 TLB 的转存和清除。而 VPID 则会跟踪每个虚拟 CPU 的 TLB,当进行虚拟机迁移或 VM Entry 与 VM Exit 时,VMM可以动态的分配非零虚拟处理器的 ID 来迅速匹配(0 ID 给 VMM 自己使用),从而避免了 TLB 的转存与清除的操作,节省了系统开销,并提高了迁移速度,同时也降低对系统性能的影响。
# grep -E 'ept|vpid' /proc/cpuinfo # 查看 cpu 是否支持相应特性# cat /sys/module/kvm_intel/parameters/{ept,vpid} # 确认是否开启 ept 和 vpidY Y
2.3 存储
QEMU 提供了对多种块存储设备的模拟,包括 IDE、SCSI、软盘、U盘、virtio 磁盘等。
qemu-kvm 提供-drive参数来详细定义一个存储驱动器:
-drive option[,option[,option[,...]]]
Define a new drive. Valid options are: file=file # 指定硬盘镜像,file=镜像文件名
if=interface # 指定驱动器使用的接口类型,如 ide, scsi, sd, mtd, floppy, pflash, virtio
snapshot=snapshot # 是否启动快照
snapshot is "on" or "off" and allows to enable snapshot for given drive.
Write to temporary files instead of disk image files. In this case, the
raw disk image you use is not written back. You can however force the
write back by pressing C-a s. cache=cache # 设置宿主机对块设备数据访问中的 cache 情况
cache is "none", "writeback", "unsafe", or "writethrough" and
controls how the host cache is used to access block data. format=format # 指定使用的磁盘格式
Specify which disk format will be used rather than detecting the format.
Can be used to specifiy format=raw to avoid interpreting an untrusted format
header.
... ...
cache 不同模式工作原理图:
对磁盘镜像文件进行一致性检查,查找镜像文件中的错误,目前仅支持对qcow2、qed、vdi格式文件的检查。其中,qcow2是 QEMU 0.8.3 版本引入的镜像文件格式,也是目前使用最广泛的格式。qed(QEMU enhanced disk)是从 QEMU 0.14 版开始加入的增强磁盘文件格式,为了避免 qcow2 格式的一些缺点,也为了提高性能,不过目前还不够成熟。而vdi(Virtual Disk Image)是 Oracle 的 VirtualBox 虚拟机中的存储格式。参数-f fmt是指定文件的格式,如果不指定格式qemu-img会自动检测,filename是磁盘镜像文件的名称(包括路径)。
$ qemu-img check CentOS6.4-x86_64.qcow2
No errors were found on the image.
qemu-kvm 通过-net参数配置网络选项
-net
nic[,vlan=n][,macaddr=mac][,model=type][,name=name][,addr=addr][,vectors=v] Create a new Network Interface Card and connect it to VLAN n (n = 0 is
the default). The NIC is an rtl8139 by default on the PC target. Optionally, the MAC address can be changed to mac, the device address set to addr (PCI cards only), and a name can be assigned for use in
monitor commands. Optionally, for PCI cards, you can specify the number v of MSI-X vectors that the card should have; this option
currently only affects virtio cards; set v = 0 to disable MSI-X. If no
-net option is specified, a single NIC is created. Qemu can emulate
several different models of network card. Valid values for type are
"virtio", "i82551", "i82557b", "i82559er", "ne2k_pci", "ne2k_isa", "pcnet", "rtl8139", "e1000", "smc91c111", "lance" and "mcf_fec". Not
all devices are supported on all targets. Use -net nic,model=? for a list of available devices for your target.
-net nic必需的参数,表明是一个网卡的配置
vlan=n表示将网卡放入到编号为 n 的 VLAN,默认为 0
macaddr=mac自定义 MAC 地址
model=type设置模拟的网卡类型,默认为 rtl8139
如果提供 VM 多个网卡,则需要多次使用-net参数。
桥接网络 手动桥接
qemu-kvm安装或者启动虚拟系统的时候如果需要和外界通信,那么就要设置网络桥接
/usr/libexec/qemu-kvm -m 1024 \-drive file=/data/images/CentOS6_4.qcow2,if=virtio \-net nic,model=virtio -net tap,script=no -nographic -vnc :0
使用-net tap,script=no方式启动之后,系统会生成 tapX 的虚拟网卡,默认是 DOWN 状态
# ip link show dev tap037: tap0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 500
link/ether d2:b0:af:7b:23:0f brd ff:ff:ff:ff:ff:ff
如果想和外界通信,可以手动执行生效,如下所示当前与 br0 桥接的设备,并没有 tap 相关的网卡
# brctl show br0bridge name bridge id STP enabled interfaces
br0 8000.b8975a626020 no eth0
vnet0
vnet1
我们需要把 tap0 也桥接到 br0 下以便和外界通信,方法如下:
# ip link set tap0 up # 使 tap0 状态变为 up# brctl addif br0 tap0 # 桥接 tap0 到 br0# brctl show br0 bridge name bridge id STP enabled interfaces
br0 8000.b8975a626020 no eth0
tap0
vnet0
vnet1
brctl delif br0 tap0删除桥接网络,qemu-kvm 工具在客户机关闭时会自动解除 TAP 设备的 bridge 绑定,所以这一步无需操作。 脚本实现
/usr/libexec/qemu-kvm -m 1024 \-drive file=/data/images/CentOS6_4.qcow2,if=virtio \-net nic,model=virtio -net tap,script=/tmp/qemu-ifup.sh -nographic -vnc :0
如上tap,script=/tmp/qemu-ifup.sh指定 script 网络配置启动前启动脚本,脚本内容如下
# cat /tmp/qemu-ifup.sh #!/bin/bash# 桥接网络设备switch=br0if [ -n $1 ]; then # $1为qemu-kvm传递值,这里是tap
ip link set $1 up
brctl addif ${switch} $1
exit 0else
echo "no interface!"
exit 1fi
三、KVM 高级功能
3.1 半虚拟化驱动
virtio
KVM 是必须使用硬件虚拟化辅助技术(如 Intel VT-x、AMD-V)的 hypervisor,在 CPU 运行效率方面有硬件支持,其效率是比较高的;在有 Intel EPT 特性支持的平台上,内存虚拟化的效率也较高。QEMU/KVM 提供了全虚拟化环境,可以让客户机不经过任何修改就能运行在 KVM 环境中。不过,KVM 在 I/O 虚拟化方面,传统的方式是使用 QEMU 纯软件的方式来模拟 I/O 设备(如模拟的网卡、磁盘、显卡等等),其效率并不非常高。在 KVM 中,可以在客户机中使用半虚拟化驱动(Paravirtualized Drivers,PV Drivers)来提高客户机的性能(特别是 I/O 性能)。目前,KVM 中实现半虚拟化驱动的方式是采用了virtio这个 Linux 上的设备驱动标准框架。 QEMU 模拟 I/O 设备的基本原理和优缺点
使用 QEMU 模拟 I/O 的情况下,当客户机中的设备驱动程序(device driver)发起 I/O 操作请求之时,KVM 模块中的 I/O 操作捕获代码会拦截这次 I/O 请求,然后经过处理后将本次 I/O 请求的信息存放到 I/O 共享页,并通知用户控件的 QEMU 程序。QEMU 模拟程序获得 I/O 操作的具体信息之后,交由硬件模拟代码来模拟出本次的 I/O 操作,完成之后,将结果放回到 I/O 共享页,并通知 KVM 模块中的 I/O 操作捕获代码。最后,由 KVM 模块中的捕获代码读取 I/O 共享页中的操作结果,并把结果返回到客户机中。当然,这个操作过程中客户机作为一个 QEMU 进程在等待I/O时也可能被阻塞。另外,当客户机通过 DMA(Direct Memory Access)访问大块 I/O 之时,QEMU 模拟程序将不会把操作结果放到 I/O 共享页中,而是通过内存映射的方式将结果直接写到客户机的内存中去,然后通过 KVM 模块告诉客户机 DMA 操作已经完成。
QEMU 模拟 I/O 设备的方式,其优点是可以通过软件模拟出各种各样的硬件设备,包括一些不常用的或者很老很经典的设备(如 RTL8139 网卡),而且它不用修改客户机操作系统,就可以实现模拟设备在客户机中正常工作。在 KVM 客户机中使用这种方式,对于解决手上没有足够设备的软件开发及调试有非常大的好处。而它的缺点是,每次 I/O 操作的路径比较长,有较多的 VMEntry、VMExit 发生,需要多次上下文切换(context switch),也需要多次数据复制,所以它的性能较差。 virtio 的基本原理和优缺点
virtio 是一个在 hypervisor 之上的抽象 API 接口,让客户机知道自己运行在虚拟化环境中,从而与 hypervisor 根据 virtio 标准协作,从而在客户机中达到更好的性能(特别是 I/O 性能)。其中前端驱动(frondend,如 virtio-blk、virtio-net 等)是在客户机中存在的驱动程序模块,而后端处理程序(backend)是在 QEMU 中实现的。在这前后端驱动之间,还定义了两层来支持客户机与 QEMU 之间的通信。其中,virtio 这一层是虚拟队列接口,它在概念上将前端驱动程序附加到后端处理程序。一个前端驱动程序可以使用 0 个或多个队列,具体数量取决于需求。例如,virtio-net 网络驱动程序使用两个虚拟队列(一个用于接收,另一个用于发送),而 virtio-blk 块驱动程序仅使用一个虚拟队列。虚拟队列实际上被实现为跨越客户机操作系统和 hypervisor 的衔接点,但它可以通过任意方式实现,前提是客户机操作系统和 virtio 后端程序都遵循一定的标准,以相互匹配的方式实现它。而 virtio-ring 实现了环形缓冲区(ring buffer),用于保存前端驱动和后端处理程序执行的信息,并且它可以一次性保存前端驱动的多次 I/O 请求,并且交由后端去动去批量处理,最后实际调用宿主机中设备驱动实现物理上的 I/O 操作,这样做就可以根据约定实现批量处理而不是客户机中每次 I/O 请求都需要处理一次,从而提高客户机与 hypervisor 信息交换的效率。
Virtio 半虚拟化驱动的方式,可以获得很好的 I/O 性能,其性能几乎可以达到和 native(即:非虚拟化环境中的原生系统)差不多的 I/O 性能。所以,在使用 KVM 之时,如果宿主机内核和客户机都支持 virtio 的情况下,一般推荐使用 virtio 达到更好的性能。当然,virtio 的也是有缺点的,它必须要客户机安装特定的Virtio驱动使其知道是运行在虚拟化环境中,且按照 Virtio 的规定格式进行数据传输,不过客户机中可能有一些老的 Linux 系统不支持 virtio 和主流的Windows系统需要安装特定的驱动才支持 Virtio。不过,较新的一些 Linux 发行版(如 RHEL 6.x、Fedora 17 等)默认都将 virtio 相关驱动编译为模块,可直接作为客户机使用 virtio。Windows 系统需要额外的安装相应的 virtio 区别,virtio-win。 virtio_net 和 vhost_net
使用 virtio_net 半虚拟化驱动,可以提高网络吞吐量和降低网络延迟。vhost_net 能够把网络 IO 请求的后端处理在内核空间完成,则效率更高,会提高网络吞吐量和减少网络延迟。 virtio_blk
virtio_blk 驱动使用 virtio API 为客户机提供了一个高效访问块设备 I/O 的方法。使用 virtio_blk 驱动的磁盘显示为/dev/vd*。 Device Assignment and SR-IOV
3.2 热插拔 (hot plugging)
热插拔可以提高服务器扩展性、灵活性以及对相关硬件问题的及时恢复能力。在服务器中,可以实现热插拔的部件主要是 SATA 硬盘、CPU、内存、USB、网卡、风扇等。在 KVM 虚拟化环境中,也支持客户机相应的设备热插拔。目前,KVM 对热插拔的支持还不是很完善,主要支持 PCI 设备和 CPU 的热插拔,内存的热插拔目前还不是很完善。
Red_Hat_Enterprise_Linux_6.5_Release_NotesRHEL 6.5 开始支持 CPU 的热插拔
CPU hot plugging and hot unplugging are supported with the help of the QEMU guest agent on Linux guests; CPUs can be enabled or disabled while the guest is running, thus mimicking the hot plug or hot unplug feature.