前言
抛开众多令人花眼的虚拟化技术不谈,一个实实在在计算机系统都包含什么?处理器(CPU)、内存(Memory)、存储(Storage)、网络(Network)、显示(Display)等。对于虚拟机也是如此。本文介绍就是针对CPU的虚拟化技术。在正式开始之前,先想像下面这个问题? 虚拟化技术中最重要的一个环节在哪,为什么 ?
虚拟化技术的核心——CPU虚拟化
CPU是虚拟化技术的核心? 鬼扯,早期QEMU使用纯代码模拟硬件不是也是先虚拟化了吗?不仅如此,还做到跨平台呢(因为QEMU是C写的)。这个一点没错,但是QEMU技术下的虚拟机运行速度太慢,所有针对硬件的访问都要QEMU这Hypervisor进行转译,尤其是针对CPU也要转译。对于现今云计算时代需要的虚拟化技术而言,在性能上实在达不到标准。影响QEMU虚拟化性能最关键的一块就在CPU虚拟化上(其他还包括内存、对性能要求高硬件虚拟化上),可以说这是QEMU的瓶颈所在。
现在正式开始回答,为啥CPU是虚拟化的核心。如下图
实现虚拟化的重要一步就在于,虚拟化层必须能够截获计算元件对物理资源的直接访问,并将其重新定向到虚拟资源池中。区分有无CPU虚拟化的关键是,这个“截获并重定向”的动作是纯软件实现,还是有硬件协助。对于传统QEMU,属于纯软件实现,对于KVM则是有硬件协助。
需要说明的是,硬件协助虚拟化技术是一套解决方案。完整的情况需要CPU、主板芯片组、BIOS和软件的支持。例如Hypervisor软件本身 或者 某些OS本身。即使只是CPU支持虚拟化技术,在配合Hypervisor软件的情况下,也会比完全不支持虚拟化技术的系统有更好的性能。
Intel虚拟化技术
鉴于虚拟化的巨大需求和硬件虚拟化产品的广阔前景,Intel自2005年末,便开始在其CPU产品线中推广应用Intel Virtualization Technology(IntelVT)虚拟化技术。下图是Intel虚拟化技术进化图
主要分为3类:
第一类是处理器相关的,称为VT-x,是实现处理器虚拟化的硬件扩展,这也是硬件虚拟化的基础;
第二类是芯片组相关的,成为VT-d,是从芯片组的层面为虚拟化提供必要支持,通过它,可以实现诸如直接分配物理设备给客户机的功能;
第三类是输入输出设备相关的,主要目的是通过定义新的输入输出协议,使新一代的输入输出设备可以更好地支持虚拟化环境下的工作,比如Intel网卡自有的VMDq技术和PCI组织定义的单根设备虚拟化协议(SR-IOV)。
QEMU和QEMU-KVM
上面说过QEMU是纯软件的虚拟化,由于没有CPU协助虚拟化,因此性能不高。KVM诞生伊始就需要硬件虚拟化支持,但是KVM模块本身仅仅对CPU、内存、部分对性能要求高的硬件做了虚拟化,其他的外设并没有进行虚拟化。而这部分工作恰恰是QEMU的强项。于是两者结合,有了QEMU-KVM分支。但是在2012年底,QEMU的1.3.0版本发布时,qemu-kvm中针对KVM的修改已经完全加入到普通的QEMU代码库中了,从此之后可以完全使用纯QEMU来与KVM配合使用(命令行添加-enable-kvm参数),而不是需要专门使用qemu-kvm代码库了。QEMU与KVM的结合可以说是各取所需,QEMU利用KVM实现硬件加速,KVM利用QEMU实现虚拟机运行时其他依赖的虚拟设备。不仅如此QEMU还负责虚拟机的配置和创建,虚拟机运行时的用户操作环境和交互,以及一些针对虚拟机的特殊技术(诸如动态迁移)。
SMP技术
在处理器技术中,多处理器、多核、超线程等技术得到了广泛的应用。无论是在企业级和科研应用的服务器领域中,还是个人消费者使用的台式机、笔记本甚至智能手机上,随处可见SMP(Symmetric Multi-Processor,对称多处理器)系统。在SMP系统中,多个程序(进程)可以做到真正的并行执行,而且单个进程的多个线程也可以得到并行执行,这极大地提高了计算机系统并行处理能力和整体性能。
在硬件方面,早期的计算机系统更多的是在一个主板上拥有多个物理的CPU插槽来实现SMP系统,后来随着多核技术、超线程(Hyper-Threading)技术的出现,SMP系统就会使用多处理器、多核、超线程等技术中的一个或多个。多数的现代CPU都支持多核 或 超线程技术。要想CPU支持了超线程(HT)技术,还需要在BIOS中打开它的设置开关。在BIOS中,超线程的设置可能会在“Advanced→CPU Configuration”下设置,通常标识为“Hyper-Threading”。另外,由于AMD走mult-core的路线,可能没有Hyper-Threading的产品或者支持不完善。而Intel支持mult-core和hyper-threading一起使用。
在操作系统软件方面,主流OS都提供了对SMP系统的支持。
如下脚本可用于检查当前系统中CPU数量,多核及超线程使用情况
#!/bin/bash #this script only works in a Linux system which has one or more identical physical CPU(s). #author: Jay http://smilejay.com/ echo -n "logical CPU number in total: " #逻辑CPU个数 cat /proc/cpuinfo | grep "processor" | wc -l echo -n "physical CPU number in total: " #物理CPU个数 cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l echo -n "core number in a physical CPU: " #每个物理CPU上Core的个数(未计入超线程) core_per_phy_cpu=$(cat /proc/cpuinfo | grep "core id" | sort | uniq | wc -l) echo $core_per_phy_cpu echo -n "logical CPU number in a physical CPU: " #每个物理CPU中逻辑CPU(可能是core, threads或both)的个数 logical_cpu_per_phy_cpu=$(cat /proc/cpuinfo | grep "siblings" | sort | uniq | awk -F: '{print $2}') echo $logical_cpu_per_phy_cpu #是否打开有超线程? #如果在同一个物理CPU上两个逻辑CPU具有相同的”core id”,那么超线程是打开的。 #此处根据前面计算的core_per_phy_cpu和logical_core_per_phy_cpu的比较来查看超线程。 #当然,cat /proc/cpuinfo | grep ht 也可以检查超线程。 if [ $logical_cpu_per_phy_cpu -gt $core_per_phy_cpu ]; then echo "Hyper threading is enabled." elif [ $logical_cpu_per_phy_cpu -eq $core_per_phy_cpu ]; then echo "Hyper threading is NOT enabled." else echo "Error. There's something wrong." fi
qemu-kvm在SMP下的体现形式
多个Guest就是Host中的多个QEMU进程,而一个Guest的多个vCPU就是一个QEMU进程中的多个线程。和普通操作系统一样,在客户机系统中,同样分别运行着客户机的内核和客户机的用户空间应用程序。对其vCPU这个线程,在传统执行模式(Kernel Mode、User Mode)基础上又增加了一种模式——Guest Mode。三种模式关系图如下
配置SMP的参数
我的实验环境上qemu-kvm时使用yum从远处软件仓库拉去rpm包在线安装得,在线安装的版本默认开启了对KVM支持。如果没有开启KVM支持,在qemu-kvm启动时加上-enable-kvm参数。
-smp n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]
- n用于设置Guest中使用的逻辑CPU数量(默认值是1)。
- maxcpus用于设置Guest中最大可能被使用的CPU数量,包括启动时处于离线(offline)状态的CPU数量(可用于热插拔hot-plug加入CPU,但不能超过maxcpus这个上限)。
- cores用于设置每个CPU socket上的core数量(默认值是1)。
- threads用于设置每个CPU core上的线程数(默认值是1)。
- sockets用于设置Guest中看到的总的CPU socket数量。
Guest中查看vCPU信息
启动Guest,在Guest中查看cpu情况
由于我已事先安装好Guest(如何安装参考:基于WMWare Workstation 搭建 KVM 环境)。启动Guest
/usr/libexec/qemu-kvm -vnc 0.0.0.0:1 centos1708.img
VNC viewer远程连接到Guest
Ctrl+Alt+2切换到qemu monitor查看cpu信息,以及qemu对kvm支持情况
可以看到有1个vCPU,线程IP是2884。
在Host中查看这个线程
[root@localhost ~]# ps -efL | grep qemu UID PID PPID LWP C NLWP STIME TTY TIME CMD root 2880 1240 2880 1 3 22:30 pts/0 00:00:04 /usr/libexec/qemu-kvm -vnc 0.0.0.0:1 centos1708.img root 2880 1240 2884 11 3 22:30 pts/0 00:00:30 /usr/libexec/qemu-kvm -vnc 0.0.0.0:1 centos1708.img root 2880 1240 2886 0 3 22:30 pts/0 00:00:00 /usr/libexec/qemu-kvm -vnc 0.0.0.0:1 centos1708.img root 2974 2948 2974 0 1 22:34 pts/1 00:00:00 grep --color=auto qemu
PID 2880这个进程是客户机进程,它产生线程LWP 2884 作为Guest 的vCPU运行在Host中
线程关系树如下
给Guest启动8个vCPU
由于我的Host最多只有4个逻辑CPU,因此无法启动Guest(会一直卡在Guest启动界面)
这相当于2个sockets,每个socket 有2个cores,每个cores有1个thread。
我物理CPU使用的是i7 6700K,4核心2线程,相当于有8个CPU。更改Host设置
再次启动Guest
/usr/libexec/qemu-kvm -smp 8,sockets=2,cores=2,threads=2 -vnc 0.0.0.0:1 centos1708.img
启动成功后查看cpu信息
Host查看vCPU对性的线程(LWP 1323~1330)
设置Guest CPU支持热插拔
如果要对Guest进行CPU的热插拔(hot-plug),则需要在启动Guest的qemu-kvm命令行参数中加上“maxcpus=num”这个选项。以前qemu-kvm中CPU的hot-plug功能有一些bug,处于不可用状态,现在不清楚啥情况。
CPU过载使用
KVM允许客户机过载使用(over-commit)物理资源,即允许为客户机分配的CPU和内存数量多于物理上实际存在的资源。
物理资源的过载使用能带来资源充分利用方面的好处。试想在一台强大的硬件服务器中运行Web服务器、图片存储服务器、后台数据统计服务器等作为虚拟客户机,但是它们不会在同一时刻都负载很高,如Web服务器和图片服务器在白天工作时间负载较重,而后台数据统计服务器主要在晚上工作,所以对物理资源进行合理的过载使用,向这几个客户机分配的系统资源总数多于实际拥有的物理资源,就可能在白天和夜晚都充分利用物理硬件资源,而且由于几个客户机不会同时对物理资源造成很大的压力,它们各自的服务质量(QoS)也能得到保障。
CPU的过载使用,是让一个或多个客户机使用vCPU的总数量超过实际拥有的物理CPU数量。QEMU会启动更多的线程来为客户机提供服务,这些线程也是被Linux内核调度运行在物理CPU硬件上。
关于CPU的过载使用,推荐的做法是对多个单CPU的客户机使用over-commit,比如,在拥有4个逻辑CPU的宿主机中,同时运行多于4个(如8个、16个)客户机,其中每个客户机都分配一个vCPU。这时,如果每个宿主机的负载不是很大,宿主机Linux对每个客户机的调度是非常有效的,这样的过载使用并不会带来客户机的性能损失。
关于CPU的过载使用,最不推荐的做法是让某一个客户机的vCPU数量超过物理系统上存在的CPU数量。比如,在拥有4个逻辑CPU的宿主机中,同时运行一个或多个客户机,其中每个客户机的VCPU数量多于4个(如16个)。这样的使用方法会带来比较明显的性能下降,其性能反而不如为客户机分配2个(或4个)VCPU的情况,而且如果客户机中负载过重,可能会让整个系统运行不稳定。不过,在并非100%满负载的情况下,一个(或多个)有4个vCPU的客户机运行在拥有4个逻辑CPU的宿主机中并不会带来明显的性能损失。
总的来说,KVM允许CPU的过载使用,但是并不推荐在实际的生产环境(特别是负载较重的环境)中过载使用CPU。在生产环境中过载使用CPU,有必要在部署前进行严格的性能和稳定性测试。