2023-2024-1 20232831《Linux内核原理与分析》第十周作业



第十章 KVM及虚拟机技术

  虚拟机技术是云计算最核心的技术,而 KVM(Kernel-based Virtual Machine)是当前最主流的虚拟机技术之一。虚拟机技术主要包括 CPU 的虚拟化、内存的虚拟化和 I/O 的虚拟化,典型的虚拟机实现有传统的 QEMU 虚拟机实现和轻量化的 StratoVirt 虚拟机实现。

10.1 虚拟技术概述

  主流虚拟化技术有VMware的ESXi、开源项目XenKVM等,主要差别在于CPU的虚拟化、内存的虚拟化和I/O的虚拟化,以及调度管理实现有所不同。ESXi中,所有虚拟化功能都在内核中实现;Xen中,内核只实现CPU和内存虚拟化,I/O虚拟化与调度管理由主机上启动的第一个负责管理的虚拟机实现;KVM中,Linux内核实现CPU与内存虚拟化,I/O虚拟化由QEMU实现,调度管理通过Linux进程调度器实现,如下图所示。

在这里插入图片描述
  除了上述主流虚拟化技术以外,微软的Hyper-V也是一种虚拟化技术解决方案,用于在Windows操作系统上创建和管理虚拟机。它是一种类型-1的虚拟化解决方案,可以在硬件上直接运行,而不需要主机操作系统的完全干预。

10.1.1 CPU的虚拟化

  CPU的虚拟化在实现上可以分为全虚拟化、半虚拟化和硬件辅助虚拟化。其中全虚拟化和半虚拟化都是软件实现,也就是虚拟机管理器(Virtual-Machine Monitor,VMM)是纯软件实现。全虚拟化实现方式在客户虚拟机执行特权指令时需要通过VMM进行异常捕获、二进制翻译(Binary Translation,BT)和模拟执行;半虚拟化实现方式需要将客户虚拟机操作系统的特权指令改为通过Hypercall调用VMM来处理特权指令,无需异常捕获与模拟执行。硬件辅助虚拟化的实现方式是VMM与Inter-VT、AMD-V等硬件辅助虚拟化技术相配合,提供在性能和运行环境上都更加逼真的虚拟机环境。

  下面以KVM为例介绍其原理和流程。

1、硬件支持: KVM要求主机硬件支持硬件虚拟化技术(如Intel的VT-x或AMD的AMD-V)。

2、内核模块: KVM通过内核模块的形式嵌入到Linux内核中。这个模块提供了虚拟化的基本设施,包括虚拟CPU和虚拟设备的管理。

3、客户机创建: 用户通过KVM工具或者Libvirt等管理工具创建虚拟机。这时KVM会创建一个虚拟CPU,并将虚拟机配置加载到内存中。

4、CPU虚拟化: 当虚拟机被启动时,KVM利用硬件虚拟化扩展来创建虚拟CPU。这个虚拟CPU通过硬件的辅助,可以直接执行虚拟机的代码。

5、运行虚拟机: 虚拟机的指令被翻译成对应的宿主机指令,然后由硬件执行。KVM负责将虚拟机的状态同步到内核中。

6、I/O虚拟化:KVM提供了对虚拟机的设备模拟,包括虚拟网络设备、磁盘等。这些虚拟设备的I/O请求会被映射到宿主机的实际设备上。

7、内存虚拟化:KVM使用页表虚拟化技术,将虚拟机的内存地址映射到宿主机的物理地址上。这使得虚拟机认为它有自己的地址空间,但实际上共享了宿主机的物理内存。

8、中断处理: KVM通过虚拟中断控制器模拟中断,将虚拟机中的中断映射到宿主机的中断处理机制中。

9、性能优化: KVM通过一些优化手段,如嵌套页表、直接内存访问(DMA)等,提高虚拟机的性能。

10.1.2 内存的虚拟化

  内存虚拟化是将Guest上的虚拟内存地址(Guest Virtual Address,GVA)转换为Guest上的物理内存地址(Guest Physical Address,GPA),进一步再转换为Host上的物理地址(Host Physical Address,HPA)。没有硬件辅助虚拟化之前,VMM 为每个 Guest 维护一份影子页表,通过软件维护 GVA、GPA 到 HPA 的映射,由于内存访问与更新频繁导致影子页表的维护复杂,运行开销大影响虚拟机的性能。内存虚拟化能够使得多个虚拟机能够共享宿主机上的物理内存,同时保持彼此隔离。以下是内存虚拟化的一些内容和原理介绍。

1、虚拟地址空间: 虚拟机操作系统认为它有自己的独立地址空间,从0开始的一系列虚拟地址。这些虚拟地址被映射到宿主机的物理地址上,但虚拟机并不直接访问物理地址。

2、页表虚拟化: 内存虚拟化依赖于硬件提供的页表虚拟化技术。当虚拟机尝试访问虚拟地址时,硬件将虚拟地址翻译为物理地址。KVM使用硬件的页表虚拟化功能,将虚拟机的页表映射到宿主机的页表上。

3、共享物理内存: 多个虚拟机可以共享相同的物理内存页。当这些虚拟机使用相同的数据时,它们可以共享相应的物理页,减少内存的浪费。

4、写时复制(Copy-On-Write): 当一个虚拟机试图修改共享的内存页时,KVM使用写时复制机制。它会为虚拟机创建一个新的物理页,使得该虚拟机拥有自己的独立拷贝,从而保持了虚拟机之间的隔离。

5、内存迁移: 内存虚拟化使得虚拟机的内存可以在宿主机之间迁移。这对于实现负载均衡、资源优化等方面非常有用。

6、嵌套页表: 有些处理器支持嵌套页表,这使得在虚拟机内部可以再创建一层虚拟页表,提高内存访问的效率。

7、透明大页: 透明大页是一种将物理页大小增加到几兆字节的技术,它减少了页表的数量,提高了内存访问的效率。

10.1.3 I/O的虚拟化

  在虚拟化环境中,Guest 的 I/O 操作需要经过特殊处理才能在底层 I/O 设备上执行。KVM 支持多种 I/O 虚拟化技术,比如全虚拟化的设备模拟与半虚拟化的 virtio 驱动都是通过软件实现的 I/O 虚拟化;比如设备直通 PCI、设备共享单根虚拟化(Single Root-I/O Virtualization,SR-IOV)、数据平面开发工具集(Data Plane Development Kit,DPDK)与存储性能开发工具集 (Storage Performance Development Kit,SPDK)等硬件辅助 I/O 虚拟化技术。

10.2 KVM API的使用方法

KVM(Kernel-based Virtual Machine)的API主要通过ioctl系统调用来实现。

1、打开KVM设备: 使用open系统调用打开/dev/kvm设备,获取KVM文件描述符。如果打开失败,表示KVM未启用或者没有权限。

int kvm_fd = open("/dev/kvm", O_RDWR);

2、检查KVM版本: 使用KVM_GET_API_VERSION ioctl检查KVM的API版本。

int api_version = ioctl(kvm_fd, KVM_GET_API_VERSION, NULL);

3、创建虚拟机: 使用KVM_CREATE_VM ioctl创建虚拟机。

int vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);

4、设置虚拟机内存: 使用KVM_SET_USER_MEMORY_REGION ioctl设置虚拟机的内存区域。

struct kvm_userspace_memory_region mem = {
    .slot = 0,
    .guest_phys_addr = 0x1000,
    .memory_size = 0x100000,
    .userspace_addr = (uintptr_t)guest_memory,
};

ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);

5、创建虚拟CPU: 使用KVM_CREATE_VCPU ioctl创建虚拟CPU。

int vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);

6、设置CPU状态: 使用KVM_SET_REGS ioctl设置虚拟CPU的寄存器状态。

struct kvm_regs regs = {
    .rip = 0x1000, // 设置虚拟CPU的指令指针
    // 其他寄存器设置
};

ioctl(vcpu_fd, KVM_SET_REGS, &regs);

7、运行虚拟机: 使用KVM_RUN ioctl运行虚拟机。

while (1) {
    int ret = ioctl(vcpu_fd, KVM_RUN, 0);
    if (ret == -1) {
        perror("KVM_RUN");
        break;
    }
    switch (run->exit_reason) {
        case KVM_EXIT_HLT:
            printf("KVM_EXIT_HLT\n");
            return 0;
        // 其他退出原因的处理
        default:
            fprintf(stderr, "Unhandled exit reason: %d\n", run->exit_reason);
            return -1;
    }
}

8、关闭虚拟机: 使用close系统调用关闭相关的文件描述符。

close(vcpu_fd);
close(vm_fd);
close(kvm_fd);

10.3 QEMU-KVM的实现原理

  QEMU原本和KVM并无关联,它是一个纯软件实现的虚拟化系统,是由Fabrice Bellard编写的模拟计算机的自由软件。因为是纯软件,所以性能较差。但是QEMU代码中包含整套的虚拟机实现,包括CPU虚拟化、内存虚拟化,以及KVM所欠缺的虚拟I/O设备,比如网卡、显卡、存储控制器和硬盘等的模拟实现,因此QEMU和KVM通常结合起来使用。下面是KVM创建过程的示意图:
在这里插入图片描述

10.4 StratoVirt

  StratoVirt是华为openEuler操作系统中引入的虚拟化技术,采用Rust语言编写实现。
  Qemu功能强大,为什么要还需要StratoVirt呢? 原因如下:

1.目前 QEMU已经有157万代码,而且其中又有很大一部分代码是用来支持legacy特性或者设备的,功能和设备严重耦合在一起,导致系统臃肿、维护困难。

2.QEMU的CVE(Common Vulnerabilities & Exposures,通用漏洞和风险)问题,其中有将近一半是因为内存问题导致的,StratoVirt社区系统通过Rust,来避免CVE。

3.容器轻量,但不够安全;虚拟机安全,但不够轻量;StratoVirt系统能够即构建出轻量虚拟机又能构建出标准虚拟机。

  StratoVirt 功能主要支持轻量虚拟机和标准虚拟机两种模式:

①轻量虚拟机模式下,单虚机内存底噪能够小于 4MB,启动时间小于 50ms,且支持毫秒级时延的设备极速伸缩能力;
②标准虚拟机模式下可支持完整的机器模型,启动标准内核,可以取代 Qemu 的作用,同时在代码规模和安全性上却有较大提升。

第十一章 Linux容器技术

  Linux容器技术是操作系统级别的虚拟化技术,可以在操作系统层次上为进程提供虚拟的执行环境,一个虚拟的执行环境被称为一个容器(container)。

11.1 容器技术概述

  容器是一种沙盒(sandbox)技术,主要目的是将应用打包起来提供一个与外界隔离的运行环境,以及方便这个沙盒转移到其他宿主机器。本质上,容器是一组特殊的进程,通过namespace、cgroup等技术把资源、文件、设备、状态和配置划分到一个独立的空间,形成一个虚拟的操作系统环境。
  通俗的理解就是一个装应用软件的箱子,箱子里有软件运行所需的依赖库和配置。开发人员可以把这个箱子搬到任何机器上,且不影响里面软件的运行。沙盒技术,顾名思义,就是在应用程序运行时为程序设计一个边界,使得不同应用之间可以独立运行而不互相干扰。其实这个沙盒技术并不是什么黑科技,其本质就是利用Linux的Namespace和Cgroups来实现应用隔离和资源限制,它并不是一个真正存在的边界。
在这里插入图片描述

以下是容器技术的概述:

1、轻量级隔离: 容器是一种轻量级的虚拟化技术,与传统虚拟机相比,它们共享操作系统内核,减少了资源占用和启动时间。

2、可移植性: 容器提供了一个封装应用及其所有依赖项的独立运行时环境,使应用能够在不同的环境中以相同的方式运行。

3、灵活性: 容器可以快速启动、停止和迁移,提供了更高的灵活性和敏捷性,适用于微服务架构。

4、隔离性: 容器使用命名空间和控制组等技术实现隔离,每个容器都有自己的文件系统、进程空间和网络空间,互不干扰。

容器具有便携性、隔离性、可配额和安全性四个特性。
在这里插入图片描述

11.2 Linux容器技术的基本原理

  一个容器大致有3个底层关键技术支持其运行:第一个是通过chroot对文件系统进行隔离,虚拟出容器系统环境的根目录;第二个是通过namespace对pid、user id、网络等进行隔离,虚拟出容器的运行环境;第三个是通过cgroup对内存、CPU、I/O等资源进行隔离,用来管控容器消耗的系统资源。

  1. 命名空间(Namespaces):
    命名空间是 Linux 内核提供的一种机制,用于隔离系统资源的可见性。不同的命名空间允许在同一主机上创建独立的资源实例,如进程、网络、文件系统等。常见的命名空间包括:

PID 命名空间: 隔离进程编号,使每个容器拥有独立的进程树。
Network 命名空间: 隔离网络接口、IP 地址、路由表等网络资源。
Mount 命名空间: 隔离文件系统挂载点,使容器有独立的文件系统。
UTS 命名空间: 隔离主机名和域名。

  1. 控制组(Cgroups):
    控制组是一种用于限制、账户和隔离进程组的机制。Cgroups 允许在不同层次上分配资源,并限制容器对 CPU、内存、磁盘等资源的使用。每个容器都可以有自己的资源限制和使用配额。

  2. 文件系统隔离:
    容器使用文件系统隔离来实现对文件和目录的隔离。每个容器都有自己的根文件系统,可以与主机和其他容器隔离。这种隔离通过 Mount 命名空间实现,允许容器拥有独立的文件系统视图。

  3. 容器镜像:
    容器镜像是一个轻量级、独立、可执行的软件包,包含运行应用所需的一切,如代码、运行时、系统工具、系统库等。镜像通过分层存储(UnionFS)的方式构建,允许多个层叠加在一起,形成一个完整的镜像。

  4. 工作流程:

镜像构建: 制作一个包含应用程序及其依赖项的容器镜像。
容器创建: 使用容器运行时创建一个容器实例,从镜像中启动应用程序。
运行应用:应用程序在容器中运行,利用隔离机制独立于主机和其他容器。
资源隔离: Cgroups 和命名空间确保容器在资源上是独立隔离的。

11.3 如何创建一个容器

  课本上内容较为晦涩,故询问了ChatGpt,它给了一个创建Docker容器的例子。下面也将详细介绍Docker。
在这里插入图片描述

11.4 Docker

  Docker是一个开源的应用容器引擎,基于Go语言编写,并采用了Apache 2.0协议。Docker可以将我们的应用程序打包封装到一个容器中,该容器包含了应用程序的代码、运行环境、依赖库、配置文件等必需的资源,通过容器就可以实现方便快速并且与平台解耦的自动化部署方式,无论你部署时的环境如何,容器中的应用程序都会运行在同一种环境下。
  一个完整的Docker有以下几个部分组成:

DockerClient客户端
Docker Daemon守护进程
Docker Image镜像
DockerContainer容器

Docker为什么比VM快?
1、Docker有着比虚拟机更少的抽象层

2、docker利用的是宿主机的内核, vm需要Guest OS

  因此,新建一个容器,docker不需要向虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest OS,是分钟级别。而docker是利用宿主机的操作系统,省略了这个复杂的过程,是秒级别的。
在这里插入图片描述

11.5 iSula

  这里的iSula是华为开源的容器软件栈,包含引擎、网络、存储、工机集等;iSulad作为其中轻量化的容器引擎,可以为多种场景提供灵活、稳定、安全的底层支持。相比Docker,iSulad是一种新的容器解决方案,提供统一的架构设计来满足CT和IT领域的不同需求。相比Golang编写的Docker,轻量级容器使用C/C++实现,具有轻、灵、巧、快的特点,不受硬件规格和架构的限制,底噪开销更小,可应用领域更为广泛。

  iSula在亚马逊丛林巴西原住民眼里,它是一种非常强大的蚂蚁,学术上称为“子弹蚁”,因为被它咬一口,犹如被子弹打到那般疼痛,它是世界上最强大的昆虫之一。而iSulad 作为轻量化的容器引擎,可以为多种场景提供最灵活、最稳定、最安全的底层支撑,与子弹蚂蚁"小个头、大能量"的形象不谋而合。

其容器统一架构如图所示:
在这里插入图片描述

Chatgpt帮助

在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值