CPU虚拟化技术及QEMU/KVM虚拟机安装实践

虚拟化是云计算时代的基石,它的本质就是将原先的物理设备进行逻辑化,转化成一个文件夹或文件,实现软硬件的解耦。使用虚拟化后,物理服务器转变成一个文件夹或文件,这里面一般会包含两部分,一部分用来记录虚拟机的配置信息,另一部分用来保存用户数据的磁盘文件。使用虚拟化前,每台物理机上只能同时运行一个操作系统,如果在服务器上运行多个主应用程序,那么不同应用之间可能会产生冲突和性能问题。实际上,最佳做法是每个服务器仅运行一个应用程序以避免这些问题,但是这么做的结果是系统硬件资源长时间利用率较低,投入成本高。使用虚拟化后,每台物理机上可以同时运行多个虚拟机,每个虚拟机上又可以运行一个操作系统,硬件资源利用率得到了有效提高。并且由于虚拟化技术实现了软硬件的解耦合,虚拟化可以不受当前服务器的限制,在集群范围内实现业务的在线动态迁移,并且在迁移过程中可以做到业务无中断、用户无感知。按照虚拟化的程度不同,可以将虚拟化分成CPU虚拟化、内存虚拟化和IO虚拟化。

虚拟化架构分类

CPU虚拟化技术的通用实现方式是将软件和硬件相互分离,在操作系统与硬件之间加入一个虚拟化的软件层,通过空间上的分割(空分),时间上的分时(时分),以及指令的模拟,将服务器物理资源抽象成逻辑资源,向上层操作系统提供一个与它它原先期待一致的服务器硬件环境。使得上层操作系统可以直接运行在虚拟环境上。并允许具有不同操作系统的多个虚拟机相互隔离,组织故障传递,并发运行在同一台物理机上,从而提供更高的IT资源利用率和更好的分配灵活性。

计算机科学科学家巴特勒·兰普森有句名言,在计算机科学中的任何问题,都可以用加上另一层间接参照来解决。(Any problem in computer science can be solved with another level of indirection)。虚拟化也可以通过加层的方式来解决。CPU虚拟化的这个软件层,叫做VMM,也叫做Hypervisor,常见的Hypervisor软件架构方案分为两类,即Type I型和Type II型。

Type I型也叫裸金属型,指的是那红VMM直接运行在裸机上,使用和管理底层的硬件资源。guest OS对真实硬件资源的访问都要通过VMM来完成,VMM拥有硬件的驱动程序,因为他是底层硬件的直接操作者。

Type II型指VMM之下还有一层宿主操作系统,由于guest os对硬件的访问必须通过宿主操作系统,因而带来了额外的性能开销。好处是可以充分利用宿主操作系统上完备的软件服务来进行管理。

两种类型的Hypervisor示意图如下:

从指令集角度看虚拟化

虚拟化技术并非应用层面的技术,而是扎根于ICT产业的基石芯片之中,能力来源于芯片架构设计和指令集设计。从指令集的角度看,虚拟化需要将一些敏感的特权指令,例如修改虚拟机的运行模式或宿主状态的指令,剥夺特权后交给VMM执行,这就要求敏感指令集是特权指令级的一个子集。

框架设计的很好,但是实现起来并没有那样规整,不是所与的敏感指令都是特权指令,敏感指令集是特权指令级的一个子集这个要求可能不会满足。以X86为例,它的CPUID指令是一个非特权指令,可以在user space执行,以获取CPU相关的配置和能力信息。这显然是一个敏感指令,但却不是特权指令:

The CPUID Explorer Part 1

Feature and Interface Discovery | Microsoft Learn

x86 - Please explain the issue of sensitive instructions in Virtualization and how it is resolved - Stack Overflow

Hypervisor通过VM exit事件监控guest的一举一动,稍微“大”一点的动作(进程切换、读写msr、执行cpuid等)都会在guest触发exit,回到host的handle函数处理,host对guest有绝对的监控和处理的全力,在x86架构上,存在多个虚拟化敏感指令,这些指令在虚拟化环境中具有特殊的行为或被虚拟机监视器(Hypervisor)劫持和处理。以下是一些常见的虚拟化敏感指令:

  1. cpuid:用于查询处理器的特性信息,包括虚拟化支持和其他扩展特性。虚拟机监视器会拦截并返回虚拟化平台的特性信息。
  2. invlpg:用于刷新页表中的页表项缓存(Translation Lookaside Buffer,TLB)。在虚拟化环境中,invlpg指令可能被虚拟机监视器拦截,以处理虚拟机的内存管理。
  3. vmcall:用于在虚拟机和虚拟机监视器之间进行通信。通过执行vmcall指令,虚拟机可以向虚拟机监视器发起请求或执行特定的操作。
  4. vmclear、vmptrld、vmptrst:这些指令用于操作虚拟机控制结构(VMCS)和虚拟机指针。虚拟机监视器使用这些指令来管理虚拟机的状态和控制。
  5. vmlaunch、vmresume:这些指令用于启动或恢复执行虚拟机。通过执行这些指令,虚拟机监视器将控制权转移到虚拟机中。
  6. rdmsr、wrmsr:用于读取和写入模型特定寄存器(Model Specific Registers,MSR)。一些虚拟化相关的MSR由虚拟机监视器拦截和处理。
wget -c https://mirrors.edge.kernel.org/pub/linux/utils/cpu/msr-tools/msr-tools-1.1.2.tar.gz

这些指令在虚拟化环境中具有特殊的行为,虚拟机监视器会拦截和处理这些指令,以提供虚拟化功能、管理虚拟机和处理器状态。普通应用程序通常无法直接执行这些指令,而是通过与虚拟机监视器的交互来间接使用它们。

关于CPUID指令KVM模拟的验证,可以参考如下测试,KVMTOOL虚拟机中启动一个不断执行cpuid指令的应用,在HOST主机中拦截kvm_emulate_cpuid的调用情况,发现确实按照固定周期调用,说明CPUID指令会导致虚拟机退出,是模拟的:

虚拟化的实现

Xen:

KVM:

开始部署前了解下KVM-Qemu-Libvirt-Openstack之间的关系

Qemu

Qemu是一个模拟器,它向Guest OS模拟CPU和其他硬件,Guest OS认为自己和硬件直接打交道,其实是同Qemu模拟出来的硬件打交道,Qemu将这些指令转译给真正的硬件。

由于所有的指令都要从Qemu里面过一手,因而性能较差。

KVM

KVM是linux内核的模块,它需要CPU的支持,采用硬件辅助虚拟化技术Intel-VT,AMD-V,内存的相关如Intel的EPT和AMD的RVI技术,Guest OS的CPU指令不用再经过Qemu转译,直接运行,大大提高了速度,KVM通过/dev/kvm暴露接口,用户态程序可以通过ioctl函数来访问这个接口。

KVM内核模块本身只能提供CPU和内存的虚拟化,所以它必须结合QEMU才能构成一个完成的虚拟化技术,这就是下面要说的qemu-kvm。

qemu-kvm

Qemu将KVM整合进来,通过ioctl调用/dev/kvm接口,将有关CPU指令的部分交由内核模块来做。kvm负责cpu虚拟化+内存虚拟化,实现了cpu和内存的虚拟化,但kvm不能模拟其他设备。qemu模拟IO设备(网卡,磁盘等),kvm加上qemu之后就能实现真正意义上服务器虚拟化。因为用到了上面两个东西,所以称之为qemu-kvm。

Qemu模拟其他的硬件,如Network, Disk,同样会影响这些设备的性能,于是又产生了pass through半虚拟化设备virtio_blk, virtio_net,提高设备性能。

qemu-kvm曾经是kernel社区维护的专门用于KVM的QEMU分支。

在2012年,这个分支并入了主流的QEMU,从此不在需要QEMU-KVM,而是通过在qemu加上 -enable-kvm选项就可以创建kvm guest了。

libvirt

libvirt是目前使用最为广泛的对KVM虚拟机进行管理的工具和API。Libvirtd是一个daemon进程,可以被本地的virsh调用,也可以被远程的virsh调用,Libvirtd调用qemu-kvm操作虚拟机。

Openstack

Openstack不会直接控制qemu-kvm,使用libvirt库去间接控制qemu-kvm。libvirt提供了跨VM平台的功能,它可以控制除了QEMU之外的模拟器,包括vmware, virtualbox, xen等等。

所以为了openstack的跨VM性,openstack只会用libvirt而不直接用qemu-kvm。libvirt还提供了一些高级的功能,例如pool/vol管理。

Intel虚拟化技术

  1. 虚拟化分软件虚拟化和硬件虚拟化,没有VT-x也能虚拟化,很老的机器都可以跑VMWare,因为使用了软件虚拟化的方式,不过运行效率肯定没有硬件虚拟化的高;
  2. 硬件虚拟化分两种:CPU层和IO层/芯片层,在Intel平台上,CPU层的虚拟化技术叫VT-x,IO层/芯片层叫VT-d;
  3. 一个CPU/一套平台可以同时支持VT-x和VTd,或者都不支持,或者只支持VT-x,决不可能只支持VT-d;
  4. 现阶段英特尔SNB/IVB平台都能保证支持VT-x;
  5. AMD的虚拟化技术叫AMD-v;
  6. 硬件虚拟化需软件支持,例如微软的Hyper-V;
  7. 现在主流的处理器基本都支持VT-x;

对VT-x和VT-d的简单介绍

VT-x:

原理:CPU运行有Ring0 ~ Ring3,一些底层操作必须Ring0。如果没有VT-x,虚拟机软件只能到Ring1,那么有些内核级别的东西就必须靠软件模拟,而效率降低。 有了VT-x,相当于多出来一套虚拟机的Ring0 ~ Ring3,这样在虚拟机内的内核请求和虚拟机外的就等于性质上/效率上没有差别了,从而提高效率。

用途:提高虚拟机效率,让虚拟机没有CPU性能的短板(当然还是受限于你CPU本身的能力)。另外,在32位系统上要跑64位虚拟机的话,也必须要VT-x支持。

VT-d:

原理:是一种基于North Bridge北桥芯片(或者按照较新的说法:MCH)的硬件辅助虚拟化技术,通过在北桥中内置提供DMA虚拟化和IRQ虚拟化硬件,实现了新型的I/O虚 拟化方式,Intel VT-d能够在虚拟环境中大大地提升 I/O 的可靠性、灵活性与性能。

用途:运用VT-d技术,虚拟机得以使用直接I/O设备分配方式或者I/O设备共享方式来代替传统的设备模拟/额外设备接口方式,从而大大提升了虚拟化的I/O性能,让虚拟机性能更接近于真实主机。

确认处理器是否支持虚拟化

验证cpu是否支持kvm,vmx是intel的 svm是AMD的,VMX和SVM指的是同一件事,都是虚拟化技术,VMX-virtual machine extension, AMD平台称为SVM-secure virtual machine extension.

$ grep -Eoc '(vmx|svm)' /proc/cpuinfo

如果CPU支持硬件虚拟化,则该命令将输出一个大于零的数字,即CPU核心的数量。否则,如果输出是,0则表示CPU不支持硬件虚拟化。

$ sudo apt update -y
$ sudo apt install cpu-checker -y
$ kvm-ok

kvm-ok会起一个线程去尝试打开/dev/kvm节点,根据返回值判断是否支持kvm.

kvm-ok是shell脚本程序

支持KVM的系统,内核中安装了kvm_intel和kvm两个内核模块。

configuration environment

$ sudo apt install virt-manager qemu qemu-kvm libvirt-bin bridge-utils libvirt-daemon-system libvirt-clients virtinst ssh-askpass-gnome --no-install-recommends
$ sudo addgroup libvirt
$ sudo usermod -aG kvm $USER
$ sudo usermod -aG libvirt $USER
$ systemctl start libvirtd.service
$ systemctl status libvirtd.service
$ systemctl is-active libvirtd

if the result of "systemctl is-active libvirtd" is active, the environment is fine.

install ubuntu 22.04

launch the virt-manager

$ sudo virt-manager

press the new vm buttons 

创建后,将会在/var/lib/libvirt/images目录生成虚拟机磁盘镜像:

 go on and then install:

install fine:

 after install

and the virt-manger console

查看虚拟机列表

below command used to view the vm status.

$ sudo virsh -c qemu:///system list

relationship with qemu

the virtual manager based on qemu, the proofs are below.

czl@czl-Vostro-3268:~$ sudo fuser -uv /dev/kvm
                     用户     进程号 权限   命令
/dev/kvm:            libvirt-qemu   3641 F.... (libvirt-qemu)qemu-system-x86
czl@czl-Vostro-3268:~$ 
czl@czl-Vostro-3268:~$ cat /proc/3641/comm 
qemu-system-x86
czl@czl-Vostro-3268:~$ cat /proc/3641/cmdline 
qemu-system-x86_64-enable-kvm-nameguest=ubuntu22.4,debug-threads=on-S-objectsecret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-1-ubuntu22.4/master-key.aes-machinepc-i440fx-bionic,accel=kvm,usb=off,vmport=off,dump-guest-core=off-cpuBroadwell-noTSX-IBRS-m1024-realtimemlock=off-smp2,sockets=2,cores=1,threads=1-uuid08e5f591-a8df-4e1f-8e9f-7c454fb4bd83-no-user-config-nodefaults-chardevsocket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-1-ubuntu22.4/monitor.sock,server,nowait-monchardev=charmonitor,id=monitor,mode=control-rtcbase=utc,driftfix=slew-globalkvm-pit.lost_tick_policy=delay-no-hpet-no-shutdown-globalPIIX4_PM.disable_s3=1-globalPIIX4_PM.disable_s4=1-bootstrict=on-deviceich9-usb-ehci1,id=usb,bus=pci.0,addr=0x5.0x7-deviceich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x5-deviceich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,addr=0x5.0x1-deviceich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,addr=0x5.0x2-devicevirtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x6-drivefile=/var/lib/libvirt/images/ubuntu22.4.qcow2,format=qcow2,if=none,id=drive-ide0-0-0-deviceide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1-driveif=none,id=drive-ide0-0-1,readonly=on-deviceide-cd,bus=ide.0,unit=1,drive=drive-ide0-0-1,id=ide0-0-1-netdevtap,fd=26,id=hostnet0-devicertl8139,netdev=hostnet0,id=net0,mac=52:54:00:76:da:e5,bus=pci.0,addr=0x3-chardevpty,id=charserial0-deviceisa-serial,chardev=charserial0,id=serial0-chardevspicevmc,id=charchannel0,name=vdagent-devicevirtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=com.redhat.spice.0-spiceport=5900,addr=127.0.0.1,disable-ticketing,image-compression=off,seamless-migration=on-deviceqxl-vga,id=video0,ram_size=67108864,vram_size=67108864,vram64_size_mb=0,vgamem_mb=16,max_outputs=1,bus=pci.0,addr=0x2-deviceintel-hda,id=sound0,bus=pci.0,addr=0x4-devicehda-duplex,id=sound0-codec0,bus=sound0.0,cad=0-chardevspicevmc,id=charredir0,name=usbredir-deviceusb-redir,chardev=charredir0,id=redir0,bus=usb.0,port=1-chardevspicevmc,id=charredir1,name=usbredir-deviceusb-redir,chardev=charredir1,id=redir1,bus=usb.0,port=2-devicevirtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x7-msgtimestamp=on

其实核心的启动指令只有有限几个,经过测试,可以通过如下的指令启动虚拟机,不需要virt manager.

$ sudo qemu-system-x86_64 -m 4096 -smp 4 --enable-kvm -drive file=/var/lib/libvirt/images/ubuntu18.04.qcow2

HOST PCI 以太网卡 pass through给虚拟机的指令:

$ sudo qemu-system-x86_64 -m 4096 -smp 4 --enable-kvm -drive file=/var/lib/libvirt/images/ubuntu18.04.qcow2 -device vfio-pci,host=0000:02:00.0

安装RHEL9也可以按照上面的方式按部就班进行。<

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值