虚拟化是云计算的基石,抛开虚拟化谈云计算无异于缘木求鱼,不得要领。

虚拟化简介

虚拟化是一种技术,它是对物理硬件资源的虚拟。通过虚拟化使得应用运行在虚拟化之后的虚拟机上,达到充分利用物理资源的目的。
 
根据虚拟化的类型可将虚拟化分为 I 型虚拟化和 II 型虚拟化。I 型虚拟化是直接作用于裸机上的,如 Xen 等虚拟化技术。II 型虚拟化是在操作系统之上的虚拟化,如 qemu-kvm,HyperV 等虚拟化技术。
 
根据虚拟化技术可将虚拟化分为:全虚拟化,半虚拟化和硬件辅助的虚拟化。
 

全虚拟化

 
如上图所示,全虚拟化是通过 QEMU 的模拟代码来模拟 Guest 请求的虚拟化。全虚拟化需要经过捕获/拦截/模拟等一系列流程。
 
物理上的 CPU 作用在不同的运行级别上,分别是 Ring3,Ring2,一直到 Ring0。对于 kernel 的代码,CPU 是运行在 Ring0 级别,可以执行特权指令,例如访问硬件资源等指令。而,作用在其它级别上时,CPU 是处于受限模式的。对于 QEMU 和 Guest 来说,CPU 在执行它们的指令时没有用 Ring0 级别去执行,当 Guest 执行 I/O 请求的时候,CPU 会报异常,这个异常会被内核模块的 KVM 捕获到,KVM 将该异常消息放入 I/O sharing page 中,然后通过 /dev/kvm 接口,告知 QEMU 去读取 sharing page 中的异常消息,QEMU 读取到该异常消息,通过自己的模拟代码来驱动内核中相应的设备模块实现 I/O 操作。当操作成功后,把结果放到 I/O sharing page 中,并通知 KVM 内核去读取该操作成功后,把结果,KVM 读取到该结果,将结果发给 Guest 的驱动程序实现整个 I/O 请求。
 
全虚拟化的指令都需要经过捕获/拦截/模拟的过程,很复杂,效率很低。
 

半虚拟化

 
如图所示,半虚拟化是对 Guest 的模块进行修改的虚拟化。现在的半虚拟化通常是基于 virtio 框架的虚拟化。在 Guest 中包含 virtio 的 Front-end,在 Host 上安装 virtio 的 Back-end。当有 I/O 请求时,Guest 上的前端 virtio driver 会直接和 Host 上的 virtio devcie 进行通信,实现对硬件资源的访问。
 
相比于 qemu 的全虚拟化,半虚拟化绕过了 QEMU ,从而少了指令捕获/拦截/模拟的过程,效率要更高。
 
完整的 Guest I/O 流程如下图所示: 
 

硬件辅助的全虚拟化

硬件辅助的全虚拟化最少需要硬件 CPU 支持。Intel VT 技术支持硬件辅助的全虚拟化方案,在使用时需要进入 BIOS 中打开 VT。
 
 
在硬件辅助的全虚拟化中,CPU 有两种执行模式,root 模式和 non-root 模式,对于 QEMU + kernel 代码的运行,CPU 是运行在 root 模式,而对于 Guest 代码的执行,CPU 运行在 non-root 模式。在 non-root 模式中,CPU 处于受限状态,只能执行限制的指令,无法执行特权指令。当 Guest 需要执行特权指令时,CPU 会从 non-root 模式切到 root 模式来执行该指令,此时 Guest 会被置于挂起状态。当执行完特权指令之后,退出到 non-root 模式,继续 Guest 的运行。
 
从 root 到 non-root 模式的切换叫 VM-Entry,从 non-root 到 root 模式的切换叫 VM-Exit。
 
相比于全虚拟化,硬件辅助的虚拟化不需要逐条翻译 Guest 指令,大部分指令都可以通过 CPU 执行,从而缩短了“流程”,提升了虚拟化的效率。
 

虚拟化原理

虚拟化从本质上来说是对 CPU / 内存 以及 I/O 资源的虚拟,使得 Guest 有自己的硬件资源,从 Guest 角度看,它就是一台独立的 server。
 
CPU 虚拟化
CPU 的虚拟化是通过 KVM 内核模块完成的,KVM 虚拟出 vCPU,Guest 上看到的 socket / core 实际上是绑定到 vCPU 上的。
 
vCPU 有三种模式,kernel / user 和 guest 模式,当 Guest 执行 I/O 相关的操作时,vCPU 是运行在 user 模式下的,当执行非 I/O 的用户空间操作,vCPU 是处在 guest 模式下的,当执行 kernel 代码时,vCPU 是处在 kernel 模式下的。
 
从 Host 上来看,Guest 就是一个 qemu-kvm 进程,而 Guest 上的 CPU 就是 qemu-kvm 上的一个线程,物理 CPU 会像调用普通进程一样调度它们。正因为 vCPU 是普通线程,所以它并不能真正代替 CPU 去执行 CPU 的指令,真正执行 Guest 上指令的还是 Host 上的物理 CPU。
 
从上图可以看到,vCPU 上的操作通过 CPU Scheduler 层层调度到指定的物理 CPU core 上执行,真正完成 Guest 操作的是物理 CPU(这也是为什么硬件辅助虚拟化最少需要物理 CPU 支持的原因)。
当然,可以通过绑核的机制实现 vCPU 和物理 CPU core 的绑定。
 

内存虚拟化

 
KVM 实现客户机内存的方式是,利用 mmap 系统调用,在 QEMU 主线程的虚拟地址空间中申明一段连续大小的空间用于客户机物理内存映射。
 
 
在 VM 中,进程的虚拟内存(VA)会映射到 VM 的物理内存(PA),而 PA 向下被映射到 Host 的机器内存(MA)。 但是 VM 操作系统不能直接实现 PA 到 MA 的映射,需要 VMM(KVM) 实现 PA 到 MA 的映射。
 
VMM 实现 PA 到 MA 的映射有两种方式:
1. 软件方式:通过影子页表技术实现内存地址的翻译;
2. 硬件方式:基于 CPU 的硬件辅助虚拟化,如 Intel 的 EPT 和 AMD 的 NPT 技术。
 

I/O 资源的虚拟化

I/O 资源的虚拟化,分为:
1. 设备模拟:在虚拟机监控器中模拟传统 I/O 设备的特性,比如在 QEMU 中模拟一个 Intel 的网卡和 IDE 硬盘驱动器,在客户机中就暴露为对应的硬件设备。客户机中的 I/O 请求都由虚拟机监控器捕获并模拟执行后返回给客户机(前面全虚拟化方式介绍的就是这种)。
2. 前后端驱动接口:在 VMM 和 VM 之间定义一种全新的适合于虚拟化环境的交互接口,常见的如 virtio 框架下,暴露给客户机的 virtio-net,virtio-blk 等网络和磁盘设备(前端设备),在 QEMU 中实现为相应的后端驱动。
3. 设备直接分配:将 Host 上的物理设备直接分配给 VM,如网卡或硬盘驱动器直接分配给 VM 等。
4. 设备共享分配:设备直接分配的 I/O 虚拟化分配的设备是极为有限的,设备共享分配将物理设备虚拟为多个虚拟机功能接口,该接口独立地分配给客户机使用,从而支持多个 VM 的设备分配。如 SRIOV 就是一种常见的设备共享分配的虚拟化 I/O 方式。
 

搭建 KVM 虚拟机

这里实现的是 qemu-kvm 硬件辅助的虚拟化方式。首先搭建虚拟化环境,打开 BIOS 中的 VT,编译 kvm 模块进内核,下载安装 qemu 软件,准备好 VMM 环境:
[root@localhost home]# lsmod | grep -i kvm
kvm_intel 170181 5
kvm 554609 1 kvm_intel
irqbypass 13503 15 kvm,vfio_pci
[root@localhost home]# rpm -qa | grep qemu
qemu-img-rhev-2.6.0-28.el7_3.6.x86_64
qemu-kvm-common-rhev-2.6.0-28.el7_3.6.x86_64
qemu-kvm-rhev-2.6.0-28.el7_3.6.x86_64
libvirt-daemon-driver-qemu-2.0.0-10.el7_3.4.x86_64
ipxe-roms-qemu-20160127-5.git6366fa7a.el7.noarch
 
环境准备好之后创建磁盘镜像,将操作系统加载到磁盘镜像中,通过 qemu-kvm 命令创建 VM:
[lianhua@localhost qemu-kvm]$ /usr/libexec/qemu-kvm -m 1G -smp 4 Virtualized.qcow2 -monitor stdio
QEMU 2.6.0 monitor - type 'help' for more information
(qemu) VNC server running on '::1;5900' (qemu) info status
VM status: running
(qemu) info cp
cpus cpustats
(qemu) info cpus
* CPU #0: pc=0xffffffff9141c0ee (halted) thread_id=825030
CPU #1: pc=0xffffffff9141c0ee (halted) thread_id=825031
CPU #2: pc=0xffffffff9141c0ee (halted) thread_id=825032
CPU #3: pc=0xffffffff9141c0ee (halted) thread_id=825033 [lianhua@localhost ~]$ ps -lef | grep -i qemu
2 S lianhua 825024 819281 32 80 0 - 537525 poll_s 16:56 pts/0 00:02:16 /usr/libexec/qemu-kvm -m 1G -smp 4 Virtualized.qcow2 -monitor stdio
 
这里磁盘镜像已经事先制作好了,通过 qemu-kvm 启动 VM,其中 -m 表示 VM 的运行内存;-smp 表示 VM 的 CPU 架构, -smp 4 表示有 4 个 core;-monitor 表示直接将 VMM monitor 重定向到当前命令行所在的标准输入输出设备上。
 
进入 monitor 中使用 info status 和 info cpus 查询 VM 的状态和 CPU 的使用情况。可以看到 CPU 其实是 Host 上的线程,CPU 0 对应的线程 id 是 825030,CPU 1 对应的线程 id 是 825031,依次类推...,而 VM 则是 Host 上的一个 qemu 进程,其进程 id 为 825024。
 
值得注意的是,即时使用一个空的磁盘镜像,qemu-kvm 还是能让它运行,但是可以想见,由于空磁盘镜像并没有安装操作系统,实际上是没有任何意义的(能想到的用处是测试 OpenStack 上 cinder / swift 组件是否正常,通常会使用一个测试镜像测试该镜像是否可以上传/安装/被 VM 使用):
[lianhua@localhost qemu-kvm]$ ll -h lianhua.raw
-rw-r--r--. 1 lianhua lianhua 20G Jul 5 16:52 lianhua.raw
[lianhua@localhost qemu-kvm]$ du lianhua.raw
0 lianhua.raw
[lianhua@localhost qemu-kvm]$ /usr/libexec/qemu-kvm -m 1G -smp 5 lianhua.raw -monitor stdio
WARNING: Image format was not specified for 'lianhua.raw' and probing guessed raw.
Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
Specify the 'raw' format explicitly to remove the restrictions.
QEMU 2.6.0 monitor - type 'help' for more information
(qemu) VNC server running on '::1;5900' (qemu) info status
VM status: running
(qemu) info cpus
* CPU #0: pc=0x000000003fefa56a thread_id=888039
CPU #1: pc=0x00000000000fd374 (halted) thread_id=888041
CPU #2: pc=0x00000000000fd374 (halted) thread_id=888042
CPU #3: pc=0x00000000000fd374 (halted) thread_id=888043
CPU #4: pc=0x00000000000fd374 (halted) thread_id=888044
(qemu) info cpus
* CPU #0: pc=0x00000000000fc373 (halted) thread_id=888039
CPU #1: pc=0x00000000000fd374 (halted) thread_id=888041
CPU #2: pc=0x00000000000fd374 (halted) thread_id=888042
CPU #3: pc=0x00000000000fd374 (halted) thread_id=888043
CPU #4: pc=0x00000000000fd374 (halted) thread_id=888044

仔细看在创建 image 的时候, qemu 会聪明的不让镜像占用磁盘空间,而是等需要占用的时候再为它分配磁盘空间。使用 prealloction=full 选项可以在创建的时候为镜像分配磁盘空间。

 
 
 
 

KVM 学习笔记:再谈虚拟化的更多相关文章

  1. [ kvm ] 学习笔记 2:虚拟化基础

    1. 虚拟化概念 什么是虚拟化 虚拟化是使用所谓虚拟机管理程序从一台物理机上创建若干个虚拟机的过程.虚拟机的行为和运转方式与物理机一样,但它们会使用物理机的计算资源,如 CPU .内存和存储.虚拟机管 ...

  2. Java学习笔记——浅谈数据结构与Java集合框架(第一篇、List)

    横看成岭侧成峰,远近高低各不同.不识庐山真面目,只缘身在此山中. --苏轼 这一块儿学的是云里雾里,咱们先从简单的入手.逐渐的拨开迷雾见太阳.本次先做List集合的三个实现类的学习笔记 List特点: ...

  3. [ kvm ] 学习笔记 4:KVM 高级功能详解

    1. 半虚拟化驱动 1.1 virtio 概述 KVM 是必须使用硬件虚拟化辅助技术(如 Intel VT-x .AMD-V)的 Hypervisor,在CPU 运行效率方面有硬件支持,其效率是比较高 ...

  4. Python学习6——再谈抽象(面对对象编程)

    1.对象魔法 在面对对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法. 使用对象而非全局变量以及函数的原因有多个,而最重要的好处不过以下几点: 多态:可对不同类型的对象 ...

  5. java设计模式学习笔记--浅谈设计模式

    设计模式的目的 编写软件的过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战.设计模式为了让程序具有更好的 1.代码重用性(即:相同功能的代码,不用多次编写) ...

  6. [ kvm ] 学习笔记 1:Linux 操作系统及虚拟化

    1. 前言 一台计算机是由一堆硬件设备组合而成,在硬件之上是操作系统,操作系统与计算机硬件密不可分,操作系统用来管理所有的硬件资源提供服务,各个硬件设备是通过 总线 进行连接起来的: 在操作系统之上, ...

  7. [ kvm ] 学习笔记 8:Ovirt 基础及使用

    目录- 1. oVirt 功能介绍- 2. oVirt 安装部署    - 2.1 基础准备    - 2.2 安装 ovirt-engine    - 2.3 配置 kvm 主机    - 2.4 ...

  8. OpenStack学习笔记(一)----安装虚拟化工具

    下面的命令运行在操作系统Ubuntu 14.04上. 在开源软件里面,主要是采用KVM和Xen.尽管OpenStack对KVM和Xen都支持,但是OpenStack对KVM的支持明显要比Xen做得好, ...

  9. KVM 学习笔记

    查看虚拟化环境 (1)查看虚拟机环境 (2)查看kvm模块支持 (3)查看虚拟工具版本 (4)查看网桥

  10. [ kvm ] 学习笔记 7:KVM 虚拟机创建的几种方式

    通过对 qemu-kvm.libvirt 的学习,总结三种创建虚拟机的方式: (1)通过 qemu-kvm 创建 (2)通过 virt-install 创建 (3)通过 virt-manager 创建 ...

随机推荐

  1. MybatisPlus查询时过滤不需要的字段~

    解释一下:乍一看标题可能有点懵~,其实就是想查询的时候过滤掉某些字段 例如: select name,email,password from user; --改为-> select name,e ...

  2. 【Python】【OpenCV】边缘检测和创建自定义核

    对于使用OpenCV已有的算子,我们还可以自定义卷积核以达到不同的效果. filters.py 1 import cv2 2 import numpy 3 4 """ 定 ...

  3. 23年底,我出齐了Spring boot,Spring cloud和案例方面的书,正在写一本面试书(代年终总结)

    年末了,再来总结一下吧,希望本人明年的年终总结文还能在博客园发. 这次总结的主题是本人出的java书.这几年本人出了不少书,其中有python.redis和Java方面的. 姑且不说其它,java方面 ...

  4. 【玩转腾讯混元大模型】怎么说?我用混元AI大模型开发了个IDEA插件

    前言 halo 我是杨不易呀,在混元大模型内测阶段就已经体验了一番当时打开页面的时候灵感模块让我大吃一惊这么多角色模型真的太屌了,随后我立马进行了代码处理水平和上下文的效果结果一般般但是到如今混元大模 ...

  5. Python——第二章:字符的编码encode和解码decode

    相关阅读:字符集(Character Set)和编码(Encoding)的历史演化 字符集和编码的总结: 1. ASCII编码: 8bit, 1byte => 256(最大可表示)2. GBK编 ...

  6. 部署堡垒机4——CentOS7 编译安装 Python 3.8.12

    1.去python3的官方网站下载源代码 https://www.python.org/downloads/ 下载安装Python 3.8.12到/opt/python3 cd /opt wget h ...

  7. 【推荐】Helix的编程语言配置

    目录 编程语言 languages.toml 语言配置 探测文件类型 编程语言服务 为一个编程语言配置语言服务 Tree-sitter 语法配置 选择语法 编程语言 编程语言设置以及语言服务器设置位于 ...

  8. electron入门之创建新窗口remote(一)

    electron入门到入土,从渲染线程中创建新窗口.2022-03-21入门版本17.1.2 electron重要概念,只有一个主线程,其他都是渲染进程或者叫子线程,他们不能直接相互操作,可以通过ip ...

  9. MySQL 基础(二)日志

    在操作系统和数据库管理系统中,为了提高数据的容灾性,一般都会通过写入相关日志的方式来记录数据的修改,使得系统受到灾难时能够从之前的数据中恢复过来.MySQL 也提供了日志的机制来提高数据的容灾性,主要 ...

  10. 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 08.创建主HUD & 自定义作弊指令

    斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论 概述 本篇文章对应课程Lecture 14 ,56-58节.本篇文章将会教你将之前创建的各种UMG控件统合到一个主控件 ...