2019-05-21 21:45:05 qq_41112887 阅读数 907

KVM虚拟化

什么是虚拟化?

虚拟化,通过模拟计算机的硬件,来实现在同一台计算机上同时运行多个不同的操作系统的技术。

为什么要用虚拟化?

充分利用资源,软件运行环境的隔离,只有虚拟化才行实现。

场景1:同一台物理机运行多个php版本 php5.3(openssl,gd)  php5.5  php7.2
场景2:机房的迁移,解决了硬件和系统的依赖
场景3:openstacks环境,软件发布方式
场景4:开发环境和测试环境,使用虚拟化
场景5:业务的快速部署  

虚拟化:提高了资源的利用率,各个服务的安全性隔离,解决了系统和硬件之间的依赖

KVM虚拟化管理软件的安装

安装软件

yum install -y libvirt virt-install qemu-kvm
libvirt        # 虚拟机管理软件
virt-install   # 虚拟机的安装工具和克隆工具
qemu-kvm       # 管理虚拟机的虚拟磁盘

启动服务

systemctl start libvirtd.service
systemctl enable libvirtd.service

启动一个kvm虚拟机

virt-install --virt-type kvm --os-type=linux --os-variant rhel7 --name centos7 --memory 1024 --vcpus 1 --disk /opt/centos7.raw,format=raw,size=10 --cdrom /opt/CentOS-7-x86_64-DVD-1708.iso --network network=default --graphics vnc,listen=0.0.0.0 --noautoconsole
--virt-type               # 指定虚拟化类型
--os-type                 # 指定系统类型
--os-variant              # 指定系统版本
--name                    # 指定虚拟机的名称
--memory                  # 指定虚拟机内存
--vcpus                   # 指定虚拟机CPU
--disk                    # 指定虚拟机磁盘文件路径及名称、磁盘文件类型、磁盘空间
--cdrom                   # 指定虚拟机安装的镜像
--network                 # 指定网络
--graphics                # 
--noautocondole           # 不加改参数创建虚拟机的时候会夯住

raw和qcow2的区别

raw:裸格式,占用空间比较大,不支持快照功能,性能较好,不方便传输
qcow2:cow(copy on write)占用空间小,支持快照,性能比raw差一点,方便传输

KVM虚拟机的日常管理

查看虚拟机列表

virsh list
virsh list --all

开启虚拟机

virsh start centos7     # 最后跟的是KVM虚拟机的名称

关闭虚拟机

virsh shutdown centos7  # 正常关闭
virsh destroy centos7   # 拔电源

查看虚拟机配置

virsh dumpxml centos7
virsh dumpxml centos7 > centos7-off.xml

删除虚拟机

# 删除虚拟机之前先关闭虚拟机
virsh undefine centos7

导入虚拟机

virsh define centos7-off.xml

修改虚拟机配置

# 自带语法检查
virsh edit centos7

虚拟机重命名

virsh domrename centos7 centos-7

挂起虚拟机

virsh suspend centos-7

恢复虚拟机

virsh resume centos-7

查询vnc端口号

virsh vncdisplay centos-7

KVM虚拟机开机启动

# KVM虚拟机开机启动前提是libvirtd开机启动。
systemctl enable libvirtd
# KVM开启自启
virsh autostart centos-7

KVM虚拟机console登录

# 首先在虚拟机的内执行以下命令
grubby --update-kernel=ALL --args="console=ttyS0,115200n8"
# 重启KVM虚拟机,在KVM虚拟机内执行,重启后真正的虚拟机会关机
reboot
# 开启KVM虚拟机并使用console连接虚拟机
virsh start centos-7
virsh console centos-7

KVM虚拟机磁盘格式转换

qemu-img convert -f raw -O qcow2 /data/centos7.raw /data/centos7.qcow2

qemu-img命令管理

# 查看虚拟机磁盘文件信息
qemu-img info centos-7.raw
# 创建虚拟硬盘
qemu-img create -f qcow2 test.qcow2 10G
# 调整虚拟磁盘容量
qemu-img resize test.qcow2 +10G
# 转换磁盘格式
qemu-img convert -f raw -O qcow2 old.raw new.qcow2
                  原格式  新格式  原文件  新文件

创建KVM虚拟机快照

# 创建快照
virsh snapshot-create centos7
virsh snapshot-create-as centos7 --name install_ok
# 查看快照
virsh snapshot-list centos7
# 还原快照
virsh snapshot-revert centos7 --snapshowname centos7
# 删除快照
virsh snapshot-delete centos7  --snapshotname centos7

raw不支持做快照,qcow2支持快照,并且快照就保存在qcow2的磁盘文件中.

KVM虚拟机克隆

完整克隆

virt-clone --auto-clone -o web01 -n web02

链接克隆

# a.生成虚拟机磁盘文件
qemu-img create -f qcow2 -b web01.qcow2 web02.qcow2
# b.生成虚拟机配置文件
virsh dumpxml web01 > web02.xml
# 修改配置文件
## 修改名字
## 删除uuid
## 磁盘文件地址
## 删除mac地址
# c.导入虚拟机
virsh define web02.xml

KVM虚拟机的桥接模式

创建桥接网卡

virsh iface-bridge eth0 br0

取消桥接网卡

virsh iface-unbridge br0

创建KVM桥接网络的虚拟机

virt-install --virt-type kvm --os-type=linux --os-variant rhel7 --name web04 --memory 1024 --vcpus 1 --disk /data/web04.qcow2 --boot hd --network bridge=br0 --graphics vnc,listen=0.0.0.0 --noautoconsole

热添加技术

KVM虚拟机在线热添加硬盘

# 临时添加
virsh attach-disk web04 --source /data/web04-add.qcow2 --target vdb --subdriver qcow2
# 永久添加
virsh attach-disk web04 --source /data/web04-add.qcow2 --target vdb --subdriver qcow2 --config

给KVM虚拟机添加新的硬盘

# 创建一个新的硬盘
qemu-img create -f qcow2 /data/new.qcow2 10G
# 附加新硬盘
virsh attach-disk web04 --source /data/new.qcow2 --target vdb --subdriver qcow2
# 虚拟机中
## 格式化硬盘
mkfs.xfs /dev/vdb
## 挂载硬盘
mount /dev/vdb /mnt

给KVM虚拟机硬盘扩容

# 虚拟机中
## 卸载硬盘
umount /mnt
# 物理机中
## 剥离硬盘
virsh detach-disk web04 --target vdb
## 扩容
qemu-img resize /data/new.qcow2 +10G
## 再插上硬盘
virsh attach-disk wbe04 --source /data/new.qcow2 --target vdb --subdriver qcow2
# 虚拟机中
## 挂载硬盘
mount /dev/vdb /mnt
## 重新加载
xfs_growfs /mnt
## 查看挂载
df -h

根磁盘扩容

# a.在宿主机上关闭虚拟机并调整虚拟机磁盘大小
qemu-img resize oldboy.qcow2 +10G
# b.虚拟机中fdisk重新调整分区
fdisk /dev/vda
# c.重启机器,执行xfs_growfs /dev/vda1
xfs_growfs /dev/vda1
## 注:ext4文件系统需要用:resize2fs /dev/vda1

KVM虚拟机在线热添加网卡

# 添加网卡
virsh attach-interface web04 --type bridge --source br0 --model virtio
virsh attach-interface web04 --type bridge --source br0 --model virtio --config
# 删除网卡
virsh detach-interface web04 --type bridge --mac 52:54:00:35:d3:71

KVM虚拟机在线热添加内存

# 创建新的虚拟机,指定最大的内存值
virt-install --virt-type kvm --os-type=linux --os-variant rhel7 --name web04 --memory 512,maxmemory=2048 --vcpus 1 --disk /data/web04.qcow2 --boot hd --network bridge=br0 --graphics vnc,listen=0.0.0.0 --noautoconsole
# 临时添加内存
virsh setmem web04 1024M 
# 永久添加内存
virsh setmem web04 1024M --config

KVM虚拟机在线热添加cpu

# 创建新的虚拟机,指定最大的cpu个数
virt-install --virt-type kvm --os-type=linux --os-variant rhel7 --name web04 --memory 512,maxmemory=2048 --vcpus 1,maxvcpus=10 --disk /data/web04.qcow2 --boot hd --network bridge=br0 --graphics vnc,listen=0.0.0.0 --noautoconsole
# 临时添加cpu
virsh setvcpus web04 4
# 永久添加cpu
virsh setvcpus web04 4 --config

KVM虚拟机热迁移

冷迁移kvm虚拟机:配置文件,磁盘文件
热迁移kvm虚拟机:配置文件,nfs共享

KVM虚拟机实现热迁移

1:两边的环境(桥接网卡)
2:实现共享存储(nfs)
3:虚拟机桥接网络
4:在线热迁移

# 1.修改宿主机的主机名和hosts文件
hostnamectl set-hostanme KVM01
hostnamectl set-hostanme KVM02
echo "10.0.0.11 KVM01" >>/etc/hosts
echo "10.0.0.12 KVM02" >>/etc/hosts
# 2.创建nfs服务器
## 具体部署nfs服务这里不详细解释
systemctl restart rpcbind
systemctl restart nfs
# 3.宿主机挂载nfs
mount -t nfs 10.0.0.13:/data/ /opt
# 4.创建一个KVM虚拟机
virt-install --virt-type kvm --os-type=linux --os-variant rhel7 --name oldbi --memory 1024,maxmemory=2048 --vcpus 1,maxvcpus=10  --disk /opt/oldbi.qcow2 --boot hd --network bridge=br0 --graphics vnc,listen=0.0.0.0 --noautoconsole
# 5.热迁移
virsh migrate --live --verbose oldbi qemu+ssh://10.0.0.12/system --unsafe
2013-11-12 09:23:17 lihang421 阅读数 971

kvm虚拟化技术:实战与原理解析


在具体内容上,本书不仅系统介绍了 KVM虚拟机的功能、特性和使用方 法,而且还深入地剖析了 KVM虚拟机的核心技术和工作原理,对 KVM做了全面而透彻的讲解。


对其内容详细阅读

2017-04-08 12:42:30 weixin_36908057 阅读数 1221

《kvm虚拟化技术 实战与原理解析》

1、kvm原理简介

 x86平台虚拟化技术中,新引入的虚拟化层通常称为虚拟机监控器(Virtual Machine Monitor,VMM),也叫做Hypervisor.
 宿主机是真实的物理平台,客户机是虚拟出来的平台。
 虚拟化实现的技术方式有多种,比如软件虚拟化和硬件虚拟化,再比如准虚拟化和全虚拟化。准虚拟化需要修改客户机操作系统。
 Xen是开源准虚拟化技术的例子,是取代内核由自身管理系统资源的架构。
 KVM全称 kernel Virtual Machine,内核虚拟机。它作为一个模块,成为linux内核的一部分。它是全虚拟化的解决方案,部分准虚拟化。
 虚拟级监控器的主要职能是管理真实的物理平台,并为每个虚拟客户机提供对应的虚拟硬件平台。
 虚拟机的架构分为类型一和类型二。类型一是虚拟机在系统上电后首先加载运行虚拟机监控程序,而传统的操作系统运行在创建的虚拟机上。类型二是系统上电后运行一般意义上的操作系统,虚拟机监控程序作为特殊的应用程序,视作操作系统功能的扩展。
 虚拟机的创建和运行是一个用户空间的应用程序(QEMU)和KVM模块相互配合。大部分的输入和输出设备交给QEMU来负责。

2、kvm核心基础功能

  1. 内存管理
  2. 存储
  3. cpu
  4. 网络
  5. 显示

3、kvm管理功能

它们一般对qemu-kvm命令进行封装和功能增强,提供更友好的用户交互接口。基于libvirt API, openstack提供一个模块化的web用户界面。libvirt API为其他虚拟化管理工具提供一套通用的API管理包括qemu/kvm的多种Hypervisor.

4、kvm性能测试以及参考数据

  1. 内存性能测试
    1. 磁盘I/O性能测试
    2. cpu性能测试
    3. 网络性能测试
2016-10-18 21:39:25 nirendao 阅读数 3380

第一章和第二章

第一章 虚拟化和云计算

Saas(软件即服务):将已经部署好的软件作为一种服务来提供,比如:Google Docs, Google Apps
Paas(平台即服务):将开发环境作为一种服务来提供。
Iaas(基础设施即服务):将多台服务器组成的“云端”基础设施作为计量服务提供给客户。

软件虚拟化:

利用纯软件的方法在现有的物理平台上(往往并不支持硬件虚拟化)实现对物理平台访问的截获和模拟。
常见的软件虚拟机如QEMU,它是通过纯软件来仿真x86平台处理器的取指、解码和执行,客户机的指令并不在物理 平台上直接执行。
由于所有指令都是软件模拟的,因此性能往往比较差,但是可以在同一平台上模拟不同架构平台的虚拟机。

VMware的软件虚拟化使用了动态二进制翻译技术。虚拟机监控机(VMM)在可控制的范围内,允许客户机的指令在物理平台上直接运行。但是,客户机指令在运行前会被虚拟机监控机扫描,其中突破虚拟机监控机限制的指令会被动态替换为可以在物理平台上直接运行的安全指令,或者替换为对虚拟机监控机的软件调用。优点是比纯软件模拟性能有大幅提升,缺点是失去了跨平台虚拟化的能力。

硬件虚拟化:

物理平台本身提供对特殊指令的截获和重定向的支持;甚至新的硬件会提供额外的资源来帮助软件实现对关键硬件资源的虚拟化,从而提升性能。

以x86平台的虚拟化为例,支持虚拟技术的x86 CPU 带有特别优化过的指令集来控制虚拟过程,通过这些指令集,VMM很容易将客户机置于一种受限制的模式下运行,一旦客户机试图访问物理资源,硬件会暂停客户机的运行,将控制权交回给VMM处理。VMM可以将客户机在受限模式下对一些特殊资源的访问完全由硬件重定向到VMM指定的虚拟资源。整个过程不需要暂停客户机的运行和VMM软件的参与。

由于虚拟化硬件可提供全新的架构,支持操作系统直接在其上运行,无需进行二进制转码,减少了相关的性能开销,极大简化了VMM的设计,进而使得VMM能够按照通用标准进行编写,性能更加强大。

准虚拟化:

改动客户机操作系统,使它以为自己运行在虚拟环境下,能够同虚拟机监控机协同工作。所以,准虚拟化需要修改客户机的操作系统的源代码来实现主动通知。

Xen是准虚拟化的一个例子,它适用于Linux这样的开源操作系统,而不适合于Windows这样的闭源操作系统。

全虚拟化:

支持运行任何理论上可在真实物理平台上运行的操作系统。不需要对客户机操作系统进行任何修改。

KVM

KVM的开发人员并不是从底层开始新写一个Hypervisor,而是选择了基于Linux Kernel,通过加载新的模块而使Linux Kernel变身成为一个Hypervisor.
KVM目前设计为通过可加载的内核模块,支持广泛的客户机操作系统,包括Windows.

在KVM架构中,虚拟机实现为常规的Linux进程,由标准Linux调度程序进行调度。事实上,每个虚拟CPU实现为一个常规的Linux进程。这使得KVM可以享受Linux内核的所有功能。

KVM本身不执行任何模拟,需要用户空间程序通过/dev/kvm接口设置一个客户机虚拟服务器的地址空间,向它提供模拟的I/O,并将它的视频显示映射回宿主的显示屏。这个应用程序(注:上述的“用户空间程序”)就是大名鼎鼎的QEMU.

REHL6.x系统中的一个KVM客户机可以支持160个虚拟CPU和多达2TB的内存,KVM宿主机支持4096个CPU核心和多达64TB的内存。


第二章 KVM原理简介

操作系统内核设计分为:微内核和单内核。

单内核指的是整个内核从整体上作为一个单独的大过程来实现,并且同时运行在一个单独的地址空间内。所有的内核服务都在这样一个大的内核空间运行,内核之间的通信可以简单地实现为函数调用。

对于微内核来说,内核的功能被划分为多个独立的过程,每一个过程叫做一个服务器。多个服务器都运行在自己的地址空间,只有少量核心的服务器运行在特权模式下,服务器之间的通信采用了进程间通信机制。独立的服务器进程提高系统的健壮性,但是进程间通信由于涉及内核空间和用户空间的上下文切换,其开销比函数调用大得多。

Linux采用实用主义的设计:Linux内核被设计成单内核以满足性能要求;但同时,Linux内核还具有模块化设计和动态装载内核模块的能力。除了诸如进程切换、内存管理等核心内核功能,将大部分内核功能作为单独的内核模块设计并实现。这些内核模块编译好后以单独的二进制文件的形式存在,内核在运行过程中,按照需求,动态地加载并链接进入内核空间运行。不使用的模块还可以在运行过程中动态卸载。这样的设计,既保证了内核的性能,也改进了传统单内核设计的灵活性。
KVM就是以内核模块的形式存在,为Linux内核增加了虚拟化的功能。

从虚拟机的基本架构来区分,分为2种:类型一和类型二。
类型一:
系统上电之后首先加载运行虚拟机监控程序,而传统的操作系统则是运行在其创建的虚拟机中。
类型一的虚拟机监控程序,可以视为一个特别为虚拟机而优化裁剪的操作系统内核。
著名的开源虚拟化软件Xen、商业软件VMware ESX/ESXi 和微软的Hyper-V就是类型一的代表。

类型二:
在系统上电之后仍然运行一般意义的操作系统(也就是俗称的宿主机操作系统),虚拟机监控程序作为特殊的应用程序,可以是为操作系统功能的扩展。
对于类型二虚拟机来说,最大的优势在于可以充分利用现有的操作系统;但也会受到宿主机操作系统的一些限制。
KVM、VMware Workstation、VirtualBox就是属于类型二虚拟机。

KVM模块

P38-39

KVM模块的主要功能是初始化CPU硬件,打开虚拟化模式,然后将虚拟客户机运行在虚拟机模式下,并对虚拟客户机的运行提供一定的支持。
KVM仅支持硬件虚拟化。

以KVM在Intel公司的CPU上运行为例:
1. 在内核被加载的时候,KVM模块会先初始化内部的数据结构;
2. 做好准备之后,KVM模块检测系统当前的CPU,然后打开CPU控制寄存器CR4中的虚拟化模式开关;
3. 通过执行VMXON指令将宿主操作系统(包括KVM模块本身)置于虚拟化模式的根模式;
4. 最后,KVM模块创建特殊设备文件 /dev/kvm 并等待来自用户空间的命令;
5. 接下来,虚拟机的创建和运行将是一个用户空间应用程序(QEMU)和KVM模块相互配合的过程。

KVM模块和用户空间QEMU的通信接口主要是一系列针对特殊设备文件的IOCTL调用。
KVM模块加载之初,只存在 /dev/kvm 文件,而针对该文件的最重要的IOCTL调用就是“创建虚拟机”,其可以被理解为KVM为了某个特定的虚拟客户机(用户空间程序创建并初始化)创建对应的内核数据结构。同时,KVM还会返回一个文件句柄来代表所创建的虚拟机。

针对虚拟处理器最重要的IOCTL调用就是“执行虚拟处理器”。通过它,用户空间准备好的虚拟机在KVM模块的支持下,被置于虚拟化模式的非根模式下,开始执行二进制指令。在非根模式下,所有敏感的二进制指令都会被处理器捕捉到,处理器在保存现场之后,自动切换到根模式,由KVM决定如何进一步处理(要么由KVM模块直接处理,要么返回用户空间交由用户空间程序处理)。

除了处理器的虚拟化,内存虚拟化也是由KVM模块实现的。实际上,这一部分是一个虚拟机实现代码中代码量最大、实现最复杂的部分(至少在硬件支持二维地址翻译之前是这样的)。

处理器对设备的访问主要是通过IO指令和MMIO,其中IO指令会被处理器直接截获,MMIO会通过配置内存虚拟化来捕捉。但是,外设的模拟一般并不由KVM模块负责。一般来说,只有对性能要求比较高的虚拟设备才会由KVM内核模块来直接负责,比如虚拟中断控制器和虚拟时钟。大部分输入输出设备还是会交给用户态程序QEMU负责。

QEMU设备模型

QEMU本身并不是KVM的一部分,其自身就是一个著名的开源虚拟机软件。它是一个纯软件的实现,所以性能低下。

为了简化开发和代码重用,KVM在QEMU的基础上进行了修改。在虚拟机运行期间,QEMU会通过KVM模块提供的系统调用计入内核,由KVM模块负责将虚拟机置于处理器的特殊模式运行。遇到虚拟机进行输入输出操作,KVM模块会从上次的系统调用出口处返回QEMU,有QEMU来负责解析和模拟这些设备。

2013-07-25 14:59:54 u010670757 阅读数 1029

====================================
目录
1 硬件虚拟化技术背景

2 KVM的内部实现概述
2.1 KVM的抽象对象
2.2 KVM的vcpu
2.3 KVM的IO虚拟化
2.3.1 IO的虚拟化
2.3.2 VirtIO

3 KVM-IO可能优化地方
3.1 Virt-IO的硬盘优化
3.2 普通设备的直接分配(Direct Assign)
3.3 普通设备的复用

===================================

1 硬件虚拟化技术背景

硬件虚拟化技术通过虚拟化指令集、MMU(Memory Map Unit)以及IO来运行不加修改的操作系统。

传统的处理器通过选择不同的运行(Ring 特权)模式,来选择指令集的范围,内存的寻址方式,中断发生方式等操作。在原有的Ring特权等级的基础上,处理器的硬件虚拟化技术带来了一个新的运行模式:Guest模式[1],来实现指令集的虚拟化。当切换到Guest模式时,处理器提供了先前完整的特权等级,让Guest操作系统可以不加修改的运行在物理的处理器上。Guest与Host模式的处理器上下文完全由硬件进行保存与切换。此时,虚拟机监视器(Virtual Machine Monitor)通过一个位于内存的数据结构(Intel称为VMCS, AMD称为VMCB)来控制Guest系统同Host系统的交互,以完成整个平台的虚拟化。

传统的操作系统通过硬件MMU完成虚拟地址到物理地址的映射。在虚拟化环境中,Guest的虚拟地址需要更多一层的转换,才能放到地址总线上:

    Guest虚拟地址 -> Guest物理地址 -> Host物理地址
                 ^               ^
                 |               |
                MMU1            MMU2

其中MMU1可以由软件模拟(Shadow paging中的vTLB)或者硬件实现(Intel EPT、AMD NPT)。MMU2由硬件提供。

系统的IO虚拟化技术,通常是VMM捕捉Guest的IO请求,通过软件模拟的传统设备将其请求传递给物理设备。一些新的支持虚拟化技术的设备,通过硬件技术(如Intel VT-d),可以将其直接分配给Guest操作系统,避免软件开销。

[1]X86处理器的生产厂商有自己的称谓,比如英特尔将Guest模式称为non-root operation,与之相对的是root operation,本文称为host模式。

2 KVM的内部实现概述

KVM是Linux内核的一个模块,基于硬件虚拟化技术实现VMM的功能。该模块的工作主要是通过操作与处理器共享的数据结构来实现指令集以及MMU的虚拟化,捕捉Guest的IO指令(包括Port IO和mmap IO)以及实现中断虚拟化。至于IO设备的软件模拟,是通过用户程序QEMU来实现的。QEMU负责解释IO指令流,并将其请求换成系统调用或者库函数传给Host操作系统,让Host上的驱动去完成真正的IO操作。她们之间的关系如下图所示:

    +--------------+               +--------+    
    | Qemu         |               |        |   
    |              |               |        |               
    | +---+  +----+|               | Guest  |               
    | |vHD|  |vNIC||<-----+        |        |               
    | +---+  +----+|      |        |        |               
    +--------------+      |        +--------+               
         ^                |            ^                    
         | syscall        |IO stream   |                    
         | via FDs        |            |                      
    +----|----------------|------------|--------+             
    |    |                |            v        |             
    |    v                |       +----------+  |   
    |  +--------+         +------>|          |  |
    |  |drivers |<--+             |  kvm.ko  |  |
    |  +--------+   |             +----------+  |
    |    ^          |   Host kernel             |   
    +----|----------|---------------------------+
         v          v                           
    +--------+    +---+                    
    | HDD    |    |NIC|                    
    +--------+    +---+    
                           图 1

从Host操作系统的角度来看,KVM Guest操作系统相当于一个进程运行在系统上,普通的命令如kill、top、taskset等可以作用于该Guest。该进程的用户虚拟空间就是Guest的物理空间,该进程的线程对应着Guest的处理器。

从Qemu的角度来看,KVM模块抽象出了三个对象,她们分别代表KVM自己,Guest的虚拟空间以(VM)及运行虚拟处理器(VCPU)。这三个对象分别对应着三个文件描述符,Qemu通过文件描述符用系统调用IOCTL来操作这三个对象,同KVM交互。此时,Qemu主要只模拟设备,她以前的CPU和MMU的模拟逻辑都被kvm.ko取代了。

2.1 KVM的抽象对象

KVM同应用程序(Qemu)的交互接口为/dev/kvm,通过open以及ioctl系统调用可以获取并操作KVM抽象出来的三个对象,Guest的虚拟处理器(fd_vcpu[N]), Guest的地址空间(fd_vm), KVM本身(fd_kvm)。其中每一个Guest可以含有多个vcpu,每一个vcpu对应着Host系统上的一个线程。

Qemu启动Guest系统时,通过/dev/kvm获取fd_kvm和fd_vm,然后通过fd_vm将Guest的“物理空间”mmap到Qemu进程的虚拟空间,并根据配置信息创建vcpu[N]线程,返回fd_vcpu[N]。然后Qemu将操作fd_vcpu在其自己的进程空间mmap一块KVM的数据结构区域。该数据结构(下图中的shared)用于同kvm.ko交互,包含Guest的IO信息,如端口号,读写方向,内存地址等。Qemu通过这些信息,调用虚拟设备注册的回调函数来模拟设备的行为,并将Guest IO请求换成系统请求发送给Host系统。由于Guest的地址空间已经映射到Qemu的进程空间里面,Qemu的虚拟设备逻辑可以很方便的存取Guest地址空间里面的数据。三个对象之间的关系如下图所示:

                            
    +----------+            |         +--------+
    | Qemu     | Host user  |         |        |
    |          |            |         |        |
    |          |            |         | Guest  |
    |  +------+|            |         | user   |
    |  |shared||            |         |        |
    |  +------+|            |         |        |
    |       ^  |            |         |        |
    +-------|--+            |         |        |
        |   |               |         |        |
     fds|   |               |         |        |
  ------|---|---------------|         |--------|
        |   |               |         |        |
        v   v   Host kernel |         | Guest  |
     +---------+            |         | kernel |
     |         |            |         |        |
     |  kvm.ko |----+       |         |        |
     |         |    |fd_kvm |         |        |
     +---------+    |       |         +--------+
                    v                     ^
                  +----+       fd_vm      |
                  |vmcs|----+--------------
      +------+    +----+    |          +------+
      | host |              |          | Guest|
      | mode |              |fd_vcpu   | mode |       
      +------+              |          +------+
          ^                 v             ^
          |             +-------+         |
          | vm exit     |  Phy  | vm entry|
          +-------------|  CPU  |---------+
                        +-------+
                           图 2

图中vm-exit代表处理器进入host模式,执行kvm和Qemu的逻辑。vm-entry代表处理器进入Guest模式,执行整个Guest系统的逻辑。如图所示,Qemu通过三个文件描述符同kvm.ko交互,然后kvm.ko通过vmcs这个数据结构同处理器交互,最终达到控制Guest系统的效果。其中fd_kvm主要用于Qemu同KVM本身的交互,比如获取KVM的版本号,创建地址空间、vcpu等。fd_vcpu主要用于控制处理器的模式切换,设置进入Guest mode前的处理器状态等等(内存寻址模式,段寄存器、控制寄存器、指令指针等),同时Qemu需要通过fd_vcpu来mmap一块KVM的数据结构区域。fd_vm主要用于Qemu控制Guest的地址空间,向Guest注入虚拟中断等。

2.2 KVM的vcpu

如前文所述,KVM的vcpu对应着host系统上的一个线程。从Qemu的角度来看,她运行在一个loop中:

	for (;;) {
		kvm_run(vcpu);
		switch (shared_data->exit_reason) {
		...
		case KVM_IO: 
			handle_io(vcpu);
			break;
		case KVM_MMIO:
			handle_mmio(vcpu);
			break;
		...
		}
	}

该线程同Guest的vcpu紧密相连。如果我们把线程的执行看作Guest vcpu的一部分,那么从Host的角度来看,该vcpu在三种不同的上下文中运行:Host user/Host kernel/Guest,将运行于一个更大的循环当中。该vcpu的运行逻辑如下图:

      Host user   |  Host kernel  | Guest mode   |
                  |               |              |
                  |               |              |
 +->kvm_run(vcpu)-------+         |              | 
 |                |     v         |              |
 |                | +->vm entry----------+       |     
 |                | |             |      v       |
 |                | |             |   Execute    |
 |                | |             |   Natively   |
 |                | |             |      |       |
 |                | |  vm exit<----------+       |  
 |                | |    |        |              |  
 |                | |    |        |              |
 |            Yes | |    v        |              |   
 |     +----------------I/O ?     |              |    
 |     |          | |    | No     |              |
 |     |          | |    |        |              |      
 |     |          | |    v        |              |
 |     v      Yes | |  Signal     |              |
 +--Handle IO<---------Pending?   |              |
                  | |    | No     |              |
                  | +----+        |              |

                           图 3

实际上,在host上通过ps命令看到的关于vcpu这个线程的运行时间正是这三种上下文的总和。

2.3 KVM的IO虚拟化

2.3.1 IO的虚拟化

传统系统中,设备都直接或间接的挂在PCI总线上。PCI设备通过PCI配置空间以及设备地址空间接收操作系统的驱动请求和命令,通过中断机制通知反馈操作系统。配置空间和设备地址空间都将映射到处理器Port空间或者操作系统内存空间中,所以设备的软件模拟需要VMM将相关的Guest PIO和MMIO请求截获,通过硬件虚拟化提供的机制将其传送给软件。模拟软件处理完后再通过VMM提供的虚拟中断机制反馈Guest。如下图所示:

        +-----------------------------------+
        | +--------------+                  |   
        | | PCI config   |     +----------+ |
        | +--------------+<--->| driver   | | 
        | +--------------+<--->|          | | 
        | | Device memory|     +----------+ |
        | +--------------+           ^      |   
        |       ^                    |      |   
        +-------|--------------------|------+  
                |                    | vINTR via VMM    
PIO/MMIO via VMM|         +----------+           
                v         |                       
         +------------------------+    
         | +--------+  +--------+ |    
         | |  PCI   |  | Device | |    
         | | config |  | memory | |  Virtual Device    
         | +--------+  +--------+ |              
         +------------------------+    
                      |   
                      v   
                +------------+
                |host driver |
                +------------+

                             图 4

虚拟设备的软件逻辑放在用户层也可以放在内核中。完全的虚拟设备模拟,可以处理在Guest中不加修改的驱动请求。通常这将消耗大量的处理器cycle去模拟设备。如果可以修改或者重写Guest的驱动代码,那么虚拟设备和驱动之间的IO接口可以根据虚拟化的特性重新定义为更高层更加高效的接口,如下图所示:

        +----------------+
        |                |
        | +-----------+  |
        | |para-driver|  |
        | +-----------+  |
        +-------^--------+
                |         
                | new I/O interface via VMM
                v
            +---------+                      
            |Virtual  |                       
            |device   |                       
            +---------+                       
                |                             
                v
           +------------+
           |host driver |
           +------------+
                          图 5

KVM的virtio正是通过这种方式提供了高速IO通道。

除了软件模拟,现有的硬件虚拟化技术还可以将一些支持虚拟化技术的新兴硬件直接分配给Guest。除了需要支持虚拟化技术的硬件(可以发起remmappable的MSI中断请求),设备的直接分配一般还需要主板上的芯片以及CPU支持,比如英特尔的VT-d技术。支持虚拟化技术的硬件平台主要做两件事,一个是DMA Remapping,将DMA请求中的Guest的物理地址映射到Host的物理地址,另一个是中断Remapping,将能remappable的中断请求根据由VMM设置,位于内存的IRT(Interrupt Remapping Table)发送到指定的vcpu上。

PC平台上,通常北桥(或者类似结构的root-complex)连接着CPU、内存以及外设。用于DMA Remapping和中断Remapping的硬件逻辑位于北桥中。如下所示:

      +-------------+
      |cpu0, cpu1...|              
      +-------------+    
            ^    
            |        <-- System Bus  
            |                |   
            v                v   
   +---------------------+  
   |  North Bridge       |   
   |                     |       +--------+
   |    +--------+       |<----->| Memory |
   |    |  vt-d  |       |       +--------+
   |    +--------+       |   
   +---------------------+   
         ^            ^          
         |            |   
         v            v      
    +--------+    +--------+
    | PCI-e  |    | South  |<-----> PCI legacy devices...
    | device |    | Bridge |
    +--------+    +--------+
                            图 6

目前,只有支持MSI的PCI/PCI-e设备才能直接分配给Guest。其中PCI-e设备可以直接与北桥相连或者桥连,然后单独分配给一个Guest。在一个桥后的所有的桥连PCI设备只能作为一个整体分配给一个Guest。KVM在硬件虚拟化的平台上支持PCI-e/PCI设备的直接分配。

2.3.2 VirtIO

VirtIO为Guest和Qemu提供了高速的IO通道。Guest的磁盘和网络都是通过VirtIO来实现数据传输的。由于Guest的地址空间mmap到Qemu的进程空间中,VirtIO以共享内存的数据传输方式以及半虚拟化(para-virtualized)接口为Guest提供了高效的硬盘以及网络IO性能。其中,KVM为VirtIO设备与Guest的VirtIO驱动提供消息通知机制,如下图所示:

     +---------------+
     |  Qemu         |
     |    +--------+ |        +-------------------+
     |    | VirtIO | |        | +---------+       |
     |    | Device | |        | | VirtIO  | Guest |
     |    +--------+ |        | | Driver  |       |  
     +------|--^-----+        | +---------+       |  
            |  |              +---|---^-----------+  
      irqfd |  |              PIO |   |               
      fd_vm |  |ioeventfd         |   |vInterrupt             
   ---------|--|------------------|---|------------
            v  |                  v   |
        +----------+         +--------------+ Host
        | eventfd  |<------->|  KVM.ko      | kernel
        | core     |         |              |
        +----------+         +--------------+
                           图 7

如图所示,Guest VirtIO驱动通过访问port空间向Qemu的VirtIO设备发送IO发起消息。而设备通过读写irqfd或者IOCTL fd_vm通知Guest驱动IO完成情况。irqfd和ioeventfd是KVM为用户程序基于内核eventfd机制提供的通知机制,以实现异步的IO处理(这样发起IO请求的vcpu将不会阻塞)。之所以使用PIO而不是MMIO,是因为
KVM处理PIO的速度快于MMIO。

3 KVM-IO可能优化地方

3.1 Virt-IO的硬盘优化

从图1中可以看到,Guest的IO请求需要经过Qemu处理后通过系统调用才会转换成Host的IO请求发送给Host的驱动。虽然共享内存以及半虚拟化接口的通信协议减轻了IO虚拟化的开销,但是Qemu与内核之间的系统模式切换带来的开销是避免不了的。
目前Linux内核社区中的vhost就是将用户态的Virt-IO网络设备放在了内核中,避免系统模式切换以及简化算法逻辑最终达到IO减少延迟以及增大吞吐量的目的。如下图所示:

                             +-------------------+
                             | +---------+       |
                             | | VirtIO  | Guest |
                             | | Driver  |       |  
                             | +-----+---+       |  
                             +---|---^-----------+  
                             PIO |   |               
                                 |   | vInterrupt             
   ------------------------------|---|--------------
                                 v   |
        +----------+         +--------------+  Host
        | Vhost    |<------->|  KVM.ko      |  kernel
        | net      |         |              |
        +----^-----+         +--------------+
             |               
             |               
         +---v----+          
         | NIC    |          
         | Driver |          
         +--------+      
                             图 8

目前KVM的磁盘虚拟化还是在用户层通过Qemu模拟设备。我们可以通过vhost框架将磁盘的设备模拟放到内核中达到优化的效果。

3.2 普通设备的直接分配(Direct Assign)

如前文所述,目前只有特殊的PCI设备才能直接分配给相应的Guest,即VMM-bypass,避免额外的软件开销。我们可以在KVM中软实现DMA以及中断的remapping功能,然后将现有的普通设备直接分配给Guest。如下图所示:

               +----------------+
               |  Guest         |
               |  +---------+   |
     +-------->|  | Driver  |   |
     |         |  +---------+   |
     |         +------------^---+
   D |              |       |    
   M |      DMA Req.|       | vINTR
   A |              |       |
     |      +-------|-------|----------+   
   O |      |       v KVM   |          |  
   p |      |   +------------------+   |  
   e |      |   | DMA remmapping   |   |              
   r |      |   |                  |   |  
   a |      |   | INTR remmapping  |   |              
   t |      |   +-----------^------+   |  
   i |      +-------|-------|----------+  
   o |              |       | INTR        
   n |              v       |             
     |              +---------+           
     +------------->| Deivce  |           
                    +---------+
                             图 9

这将大大减少Guest驱动同物理设备之间的路径(省去了KVM的涉入),去掉了虚拟设备的模拟逻辑,不过IO性能的提高是以增加KVM的逻辑复杂度的代价换来的。此时,IO的性能瓶颈从Qemu/KVM转移到物理设备,但是IO的稳定性、安全性将会更加依赖于KVM的remapping逻辑实现。

3.3 普通设备的复用

在普通设备的直接分配的基础上,我们甚至可以在多个Guest之间复用设备,好比m个进程跑在n个处理器上一样(n < m)。比如将一个硬盘分成多个区,每一个分区作为一个块设备直接分配给Guest;或者直接将n个网卡分配给m个Guest(n < m)。其中磁盘的复用,只需在KVM中添加分区管理的逻辑,而网卡的复用则要复杂一些:KVM需要为设备提供多个设备上下文(每一个设备上下文对应着一个Guest),同时还需要提供算法逻辑对设备上下文进行切换和调度。如下图所示:

                                                 
                        |                  KVM   |
                        |  Device context        |
                        |  queue                 |
           +------+     |     +-+                |
           |Guest |---------->| |                |
           -------+     |     +-+                |
                        |      |                 |
           +------+     |     +-+                |
           |Guest |---------->| |   +----------+ |
           +------+     |     +-+   | Device   | |
                        |      |    | Scheduler| |
           +------+     |     +-+   +----------+ |
           |Guest |---------->| |-----+          |
           +------+     |     +-+     |          |
                        |          +--v--------+ |
                        | Current--->+--+  DM  | |     +-----+
                        | Context  | +--+------------->| NIC |
                        |          +-----------+ |     +-----+
                        |                        |

                               图 10

其中,Device Modle(DM)实现前文提到的remapping逻辑,Device Scheduler用于选择和切换设备上下文实现物理设备的复用。在普通设备直接分配的基础上,通过对现有普通设备的复用,将会带来廉价、灵活、高效的IO性能。与之相对的是,目前已经有支持SR-IOV的网卡,从硬件上实现复用功能,支持多个(静态,即最大数目是内置的)虚拟的PCI网卡设备,价格昂贵,且受到一个网口总带宽有限的限制(软件复用技术,可以复用多个网卡,进而提高系统总的带宽)。
参考:
1[代码] qemu-kvm. git://git.kernel.org/pub/scm/virt/kvm/qemu-kvm.git
2[代码] Linux-2.6{/virt/kvm/*, arch/x86/kvm/*, drivers/virtio/*, drivers/block/virtio_blk.c, drivers/vhost/*}
3[手册] Intel? Virtualization Technology for Directed I/O
4[手册] Intel? 64 and IA-32 Architectures Software Developer’s Manual 3B.
5[论文] Towards Virtual Passthrough I/O on Commodity Devices. 2008.
6[论文] kvm: the Linux Virtual Machine Monitor. 2007.
7[论文] virtio: Towards a De-Facto Standard For Virtual I/O Devices. 2008
8[论文] High Performance Network Virtualization with SR-IOV. 2010.
9[论文] QEMU, a Fast and Portable Dynamic Translator. 2005.


没有更多推荐了,返回首页