Linux内核虚拟化技术KVM总结以及Docker容器技术的浅析(以x86架构为例)_kvm 容器_风的妖怪的博客-程序员信息网

技术标签: 架构  linux  Linux OS封神之路  计算机体系结构  docker  


现代开源操作系统的基本组成:

功能 链接地址
文件管理和设备管理 虚拟文件系统(无持久存储的文件系统),以proc和sysfs为例
内存管理 现代操作系统的内存管理原理:以Linux2.6.x.x为例
系统启动原理 Linux 2.6.4.30 Arm Architecture源码深度剖析—基于《ARM Linux内核源码剖析》
虚拟地址空间 什么是虚拟地址空间?从架构视角来解释
动态链接 一篇长文带你深析Linux动态链接的全过程

从上篇:Intel 64 and IA-32 Architectures Software Developer’s Manual 读后感可以看到Intel对于虚拟化技术还是很看重的,因此此篇借助Linux平台来总结一下虚拟化技术。

虚拟化技术与容器技术的差别:KVM内核虚拟化技术以及Docker容器技术的原理浅谈?

下一篇:多核系统原理与解决方案

虚拟化主要分为以下几个部分:CPU虚拟化,内存虚拟化,中断虚拟化和设备虚拟化。其中内存虚拟化和中断虚拟化属于软件虚拟技术,而CPU虚拟化和设备虚拟化属于硬件虚拟化技术,特别是设备虚拟化,和真实存在的设备差别不是太大。

VT-d技术

CPU虚拟化

结合Intel手册中的虚拟化技术来看

陷入和模拟模型(Trap and Emulate:虚拟机的用户程序和内核都运行在用户模式(一般情况下是用户程序运行在用户模式,内核运行在系统模式),这称为特权级压缩(Ring Compression,在这种方式下,虚拟机的非特权级治疗直接运行在处理器上,当运行特权级指令时,将触发处理器异常,陷入VMM中,由VMM代理虚拟机完成系统资源的访问。

随后Intel开发了VT技术支持虚拟化,为CPU增加了VMX,即Virtual-Machine Extension。此时CPU提供了两种运行模式:VMX Root ModeVMX non-Root Mode,每一种都支持ring0-ring3VMM运行在VMX Root Mode,除了支持VMXVMX Root Mode和普通的模式并无本质区别。VM运行在VMX non-Root ModeGuest kernel运行在VMX non-Root Modering0中 。

VT技术上处理器上的作用,不必关心其细节。

在这里插入图片描述
处于VMX Root ModeVMM可以通过执行CPU提供的虚拟化指令VMLaunch切换到VMX non-Root Mode,因为这个过程相当于进入Guest,所以通常也被称为VM entry。当Guest内部执行力敏感指令,比如某些I/O操作后,将触发CPU发生陷入的动作,这个过程相当于退出VM,因此被称为VM exit。然后VMMGuest的操作进行模拟。

如同一个CPU可以分时运行多个任务一样,每个人物有自己的上下文,由调度器质调度时切换上下文,从而实现同一个CPU同时运行多个任务,在虚拟化场景下,同一个物理CPU分时运行着HostGuest,在不同的模式间按需切换,因此,不同的模式也需要保存自己的上下文。每个Guest都有自己的一个VMCS实例,当物理CPU加载了不同的VMCS时,将运行不同的Guest

当运行Guest模式时,Guest用户空间的系统调用直接陷入Guest模式的内核空间,而不是陷入Host模式的内核空间

对于外部中断,因为需要由VMM控制系统的资源,所以当处于Guest模式的CPU收到外部中断后。则触发CPUGuest模式退出到Host模式,由Host内核处理外部中断,处理完中断后,再重新切入Guest模式。(此时不必产生VM exit

对于VCPUVMM使用一个线程来代表这个VCPU实体

KVM是内核模块。

陷入和模拟:当Guest访问系统资源时,就需要退出到Host模式,由Host作为统一的管理者代为完成资源访问。当虚拟机进行I/O访问时,首先需要陷入HostVMM中的虚拟磁盘收到I/O请求后,如果虚拟机磁盘镜像存储在本地文件,那么就代为读写本地文件。在访问设备I/O内存映射的地址空间时,将触发页面异常,但是这些地址对应的不是内存,而是模拟设备的I/O空间,因此需要KVM介入,调用相应的模拟设备处理I/O

对于被动性触发的陷入,只需要GuestCPU资源让给Host即可。

常用外设访问方式包括PIOMMIO

内存虚拟化

实质是创建影子页表或者EPT等页表进行与物理内存的映射关系。

中断虚拟化

对于软件虚拟的中断芯片而言,中断信号只是一个变量,从软件模拟的角度就是设置变量的值了。如果KVM发现虚拟中断芯片有中断请求,则向VMCSVM entry control部分的VM-entry interruption-information filed字段写入中断信息,在切入Guest模式的一刻,CPU将检查这个字段,就如同检查CPU管脚 ,如果有中断,则进入中断执行过程。

其中中断的逻辑为

  • 虚拟设备向虚拟中断芯片PIC发送中断请求,虚拟PIC记录虚拟设备的中断信息,与物理的中断过程不同,此时并不会触发虚拟PIC芯片的中断评估逻辑,而是在VM entry时进行
  • 如果虚拟CPU处于睡眠状态,则唤醒虚拟CPU,即使虚拟CPU对应的线程进入了物理CPU的就绪任务队列
  • 当虚拟CPU开始运行时,在其切入Guest前一刻,KVM模块将检查虚拟PIC芯片,查看是否有 中断需要处理。此时,KVM将触发虚拟PIC芯片的中断评估逻辑
  • 一旦经过虚拟中断芯片计算得出一需要Guest处理的中断,则将中断信息注入VMCS中断字段VM-entry interruption-information
  • 进入Guest模式后,CPU检查VMCS中断中断信息
  • 如果有中断需要处理CPU将调用Guest IDT中相应的中断服务处理中断

在这里插入图片描述
一般对于中断的控制是由中断控制器8259A可编程中断控制器或者APIC可编程高级中断控制器来操作的,因此也要对这两个芯片进行虚拟化。

硬件虚拟化的支持

虚拟中断芯片是在用户空间实现的,但是中断芯片密集地参与了整个计算机系统的运转过程,因此,为了减少内核空间与用户空间之间的上下文切换带来的开销,后来,中断芯片的虚拟是在了内核空间。为了进一步提高效率,Intel从硬件层面对虚拟化的方方面面进行了支持。

  • virtual-APIC pageIntelCPUGuest模式下实现了一个用于存储中断寄存器的virtual-APIC page。在Guest模式下有了此状态,后面Guest模式下还有了中断逻辑,很多中断行为就无需VMM介入了,从而大大减少了VM exit的次数。
  • Guest模式下的中断评估逻辑。IntelGuest模式中实现了部分中断芯片的逻辑用于中断评估,当有中断发生时,CPU不必再推出到Host模式,而是直接在Guest模式下完成中断评估。
  • posted-interrupt processing:在虚拟中断芯片收到中断请求后,会将信息保存在虚拟中断芯片中,在VM entry时,触发虚拟中断芯片的中断评估逻辑,根据记录在虚拟中断芯片中断信息进行中断评估。但是当CPU支持者Guest模式下的中断评估逻辑后,虚拟中断芯片可以在收到中断请求后,将中断信息直接传递给处于Guest模式下的CPU,由Guest模式下的中断芯片的逻辑中Guest模式中进行中断评估,向Guest模式的CPU直接递交中断。

设备虚拟化

设备虚拟化:系统虚拟化软件使用软件的方式呈现给Guest操作系统硬件设备的逻辑。设备虚拟化先后经历了完全虚拟化,半虚拟化,以及后来出现的硬件辅助虚拟化,包括将硬件直接透传给虚拟机,以及将一个硬件从硬件层面虚拟成多个子硬件,每个子硬件分别透传给虚拟机等。

设备透传

设备虚拟化如果采用软件模拟的方式,则需要VMM参与进来。为了避免这个开销,Intel尝试从硬件层面对I/O虚拟化进行支持,即将设备直接透传给GuestGuest绕过VMM直接访问物理设备,无需VMM参与I/O过程,这种方式提供了最佳的I/O虚拟化性能。

Intel最初采用的方式是Direct Assignment,即将整个设备透传给某一台虚拟机,不支持多台VM共享同一设备。为例使不同设备制造商的设备可以互相兼容,PIC-SIG制定了一个标准:Single Root I/O Virtualzation and Sharing,简称SR-IOVSR-IOV引入了两个新的Function类型,一个是Physical Function,简称PF;另一个是Virtual Function,简称VF。一个SR-IOV可以支持多个VF,每一个VF可以分别透传给Guest,如此,将从硬件角度实现了多个Guest分享同一物理设备。

在这里插入图片描述
每个VF都有自己独立的用于数据传输的存储空间、队列、中断及命令处理单元等,但是这些VF的管理仍然在VMM的控制下,VMM通过PF管理这些VF。虚拟机可以直接访问这些VF,而无须再通过VMM中的模拟设备访问物理设备。PF除了提供管理VF的途径外,Host 以及其上的应用仍然可以通过PF无差别地访问物理设备。对于那些没有VF驱动的Guest,虚拟机依然可以通过SR-IOV设备的PF接口共享物理设备。

数据传输方式
(1)MMIOMemory-Mapped I/O:将外设的内存,寄存器映射到CPU的内存地址空间中,CPU访问外设如同访问自己的内存一样。
(2)PIOPort I/O:使用INOUT指令对指定端口进行读取数据。

我不喜欢网络,也没有探究网络虚拟化,跳过了

此处要注意,VT-d技术实现的是CPU的虚拟化和内存的虚拟化,中断和设备并无安全算上一种虚拟化。

QEMU/KVM技术

概念看多了,略显枯燥,来看实操

QEMU源码下载:qemu源码官网下载

Linux源码下载:Linux内核源码官网下载

概念和历史

虚拟机(Virtual Machine,VM)是进程,进程可以看作是一组资源的集合,有自己独立的进程地址空间以及独立的CPU和寄存器,执行程序员编写的指令,完成一定的任务。系统虚拟化是在云计算的支持下得到了快速的发展,IntelAMD都相继在CPU硬件层面增加了虚拟化的支持。

随后,Qumranet利用Intel的硬件虚拟化技术在Linux内核上开发了KVM Kernel Virtual MachineKVM架构精简,与Linux内核天然融合,得以很快进入内核。KVM现在已经是一个非常成功的虚拟化VMM,称为云计算的基石。

QEMU早期是通过把源架构指令通过TCG Tiny Code Generator引擎转换成目标架构指令。
IntelAMD都相继在CPU硬件层面增加了虚拟化的支持,即硬件虚拟化,Intelx86指令集的基础上增加了一套VMX扩展指令VT-x,为CPU增加了新的运行模式,完成了x86虚拟化漏洞的修补。
通过新的硬件虚拟化指令,可以非常方便的构造VMM,并且x86虚拟机中的代码能够原生地运行在物理CPU

KVM本身是一个内核模块,导出来一系列的接口到用户空间,用户空间可以使用这些接口创建虚拟机。最开始KVM只负责最核心的CPU虚拟化和内存虚拟化部分,使用QEMU作为其用户态组件,负责大量外设的模拟,当时的方案被称为QEMU-KVM

在这里插入图片描述
除此之外,还有VMwareVirtualbBox等虚拟化解决方案。

CPU虚拟化

虚拟机的创建:要创建一台KVM虚拟机,需要用户侧的QEMU发起请求。

QEMU侧虚拟机的创建和KVM虚拟机的创建:QEMU中使用KVMState结构体来表示KVM相关的数据结构,KVM_INIT函数首先打开/dev/kvm设备得到一个fd,调用kvm_ioctl(s, KVM_CREATE_VM)接口中KVM层面创建一个虚拟机,kvm_init也会调用kvm_arch_init完成一些架构相关的初始化。

// qemu
static int accel_init_machine(AccelClass *acc, MachineState *ms) 
{
    
	//
	ret = acc->init_machine(ms);
	if (ret < 0) 
	{
    
		...
	}
	return ret;
}

// kvm
static int kvm_init(MachineState *ms) 
{
    
	// 
	MachineClass *mc = MACHINE_GET_CLASS(ms);
	
	ms = KVM_STATE(ms->accelerator);
	ms->vmfd = -1;
	ms->fd = qemu_open("/dev/kvm", O_RDWR);

	do 
	{
    
		ret = kvm_ioctl(s, KVM_CREATE_VM, type);  // 创建一台KVM虚拟机
	} while (ret == -EINTR);
}

VCPU的创建

VCPU的类型

struct X86CPUDefinition {
    
    const char *name;
    uint32_t level;
    uint32_t xlevel;
    /* vendor is zero-terminated, 12 character ASCII string */
    char vendor[CPUID_VENDOR_SZ + 1];
    int family;
    int model;
    int stepping;
    FeatureWordArray features;
    char model_id[48];
};

static X86CPUDefinition builtin_x86_defs[] = {
    
	// 各种cpu型号和特性
    {
    
        .name = "pentium",
        .level = 1,
        .vendor = CPUID_VENDOR_INTEL,
        .family = 5,
        .model = 4,
        .stepping = 3,
        .features[FEAT_1_EDX] =
            PENTIUM_FEATURES,
        .xlevel = 0,
    },	
    // ......
}

在调用KVM的创建虚拟CPU会调用一个kvm_vcpu_ioctl函数,此处传入一个参数,用来表示创建cpu的状态,如下

int kvm_cpu_exec(CPUState *cpu) 
{
    
	do 
	{
    
		...
		run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);
	} while ()
}

Docker容器技术

传统的虚拟化技术可以通过硬件或者操作系统来实现,如KVM,为了让虚拟的应用程序达到和物理机相近的结果,使用了Hypervisor/VMM,它允许多个操作系统共享一个或多个CPU,但是却带来了很大的开销,由于虚拟机中包括全套的OS,调度和资源占用都非常重。

在这里插入图片描述

容器(Container)技术是一种更加轻量级的操作系统虚拟化技术,它将应用程序,依赖包,库文件等允许依赖环境打包到标准化的镜像中,通过容器引擎提供进程隔离,资源可限制的允许环境,实现应用与OS平台及底层硬件的解耦。

在这里插入图片描述

此处不得不提到一个命令:chroot,即change root,其作用是改变程序执行时所参考的根目录位置,可以增加系统的安全性,限制使用者能做的事。

容器管理引擎

主要包括守护进程,镜像,驱动和容器管理者几个模块。

Docker Daemon

该进程是一个常驻后台的守护进程,负责监听客户端请求,然后执行后续的对应逻辑,还能管理Docker对象,主要有三部分:

  • Server:负责接收客户端发来的请求,接收请求以后Server通过路由与分发调度找到相应的Handler执行请求,然后与容器镜像仓库交互镜像并将结果返回给Docker Client
  • Engine:运行引擎。该模块扮演了Docker container存储仓库的角色,Engine的每一项工作,都可以拆解成多个最小动作Job,这是其最基本的工作执行单元
  • JobDocker内部的每一步操作,都可以抽象为一个Job,会使用下层的Driver驱动来完成。

Docker Driver

Docker中的驱动,设计驱动这一层是解耦,将容器管理的镜像,网络和隔离执行逻辑从Docker Daemon的逻辑中剥离。

中期实现中,可以分为以下三类驱动:

  • graphdriver:负责容器镜像的管理,主要是镜像的存储和获取,当镜像下载时,会将镜像持久化存储到本地的制定目录。
  • networkdriver:负责Docker容器网络环境的配置,如Docker运行时进行IP分配端口映射以及启动时创建网桥和虚拟网卡。
  • execdriver:执行驱动,通过操作Lxc或者libcontainer实现资源隔离。它负责创建管理容器运行命名空间,管理分配资源和容器内部真实进程的运行。

libcontainer

提供了访问内核中和容器相关的API,负责对容器进行具体操作。

主要是利用内核的namespacecgroup两个技术。
其中namespace负责进程隔离,cgroup负责资源限制

可见虚拟文件系统(无持久存储的文件系统),以proc和sysfs为例中的Cgroup文件系统。


感兴趣的可以看看这个项目,用bash手写的一百多行简易dockerbocker


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_48322523/article/details/127688886

智能推荐

Linux pip错误分析 (Running pip as the ‘root‘ user can result in broken permissions and conflict)_干掉芹菜的博客-程序员信息网

liunx安装出经常出现的Running pip as the 'root' user can result in broken permissions and conflict问题,解决办法

Hdu 3364 Lanterns(高斯消元)_wang2147483647的博客-程序员信息网

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3364思路:灯的状态是由控制它的开关的异或值所决定的,每个开关值为0或1,所以设 (xi*k1)^(xi*k2)^.....^(xi*km) = xi 灯为x,开关为k。得到一方程组,高斯消元后等式左边系数全为0,若等式右边系数不为0,则无解。否则,答案为2^(自由元(不管开或关都行)个数)。

leet94. 中序遍历二叉树_yzpwslc的博客-程序员信息网

题目:给定一个二叉树,返回其中序遍历。例如:给定二叉树 [1,null,2,3], 1 \ 2 / 3返回 [1,3,2].分析:使用递归调用方式中序遍历代码:class Solution(object): def inorderTraversal(self, root): &quot;&quot;&quot; :type root: TreeNod...

win7下Python3的pip的使用_ Vic的博客-程序员信息网

初学Python,对模块的使用简直就是一脸懵逼的状态,时常用别人的源代码时,报各种模块的错,你比如说这个吧:no module named 'requests',然后百度,人说需要安装,具体做法是pip install requests.没了。其实在windows下使用pip,需要注意几点:1.安装python的时候,一定要勾选pip的选项,这样,python安装完成后,在scripts文件夹...

java 24点经典纸牌益智游戏_java24点_hgestll的博客-程序员信息网

java 24点经典纸牌益智游戏内容算法分析概要设计测试心得体会内容24点游戏是经典的纸牌益智游戏。常见游戏规则:从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。基本要求: 随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式,用擅长的语言(C/C++/Java或其他均可)实现程序解决问题。1.程序风格良好(使用自定义注释模板)2.列出表达式无重复。提高要求:用户初始生命值

基于Java实现的在线点餐、SSM+mysql餐厅点餐系统实现_ssm实现点餐订单结算_code306的博客-程序员信息网

基于Java实现的在线点餐、SSM+mysql餐厅点餐系统实现ssm实现在线点餐系统、分为普通用户点餐端(前台),后台管理端(后台)、另外后期会继续开发实现SpringBoot版本有什么问题大家可以指出来哦主要技术SpringBoot\SSM(两个版本都有)JSP、JSTL、jQuery、HTML、CSS、JSMysqlbootstrap开发工具和环境Eclipse\idea(都可以)兼容MavenTomcat 8JDK 1.8Mysql 5.6Win10 、Linux概况前

随便推点

Android 四大组件之 Service(一)_weixin_30480651的博客-程序员信息网

Service是Android中四大组件之一,在Android开发中起到非常重要的作用,它运行在后台,不与用户进行交互。1、Service的继承关系:java.lang.Object →android.content.Context →android.content.ContextWrapper→ android.app.Service2、Sevice定义:...

在Perl中使用Getopt::Long模块来接收用户命令行参数_use getopt::long_ygl892312492的博客-程序员信息网

在Perl中使用Getopt::Long模块来接收用户命令行参数我们在linux常常用到一个程序需要加入参数,现在了解一下perl中的有关控制参数的函数.getopt.在linux有的参数有二种形式.一种是–help,另一种是-h.也就是-和–的分别.–表示完整参数.-表示简化参数我们在linux常常用到一个程序需要加入参数,现在了解一下 p

自己做量化交易软件(24)小白量化增强仿通达信财务基本数据公式_荷蒲的博客-程序员信息网

自己做量化交易软件(24)小白量化增强仿通达信财务基本数据公式通达信公式指标中有很多关于系统或基本面的函数,小白量化公式系统只有关于行情方面的公式基本函数。为了便于移植更多的自编通达信公式指标,我们给大家介绍新一代小白量化系统框架中的增强仿通达信公式功能。这个系统这个要完全依赖通达信行情,来获取股票行情和财务基本数据。首先引入小白增强公式库HP_tdxgs模块。import HP_tdx as htdxfrom HP_formula import *import HP_tdxgs as hgs

iOS 开发学习资料整理(持续更新)_e62ces0iem的博客-程序员信息网

“如果说我看得比别人远些,那是因为我站在巨人们的肩膀上.” ---牛顿iOS及Mac开源项目和学习资料【超级全面】http://www.kancloud.cn/digest/ios-mac-study/84557iOS 学习资料整理https://segmentfault.com/a/1190000002473595#articleHeader16iOS、mac开源项目及库

java.sql.SQLException: Zero date value prohibited_jpa的日期为0报错_星空_灯塔的博客-程序员信息网

java.sql.SQLException: Zero date value prohibited:禁止零日期值。使用JPA检索数据库表中日期字段时报此错误,去数据库表中查询该日期字段,发现有 0000-00-00 00:00:00 的值,随机更新一个时间后不再报错。解决方法:1、修改日期为 0000-00-00 00:00:00 的字段值,根据实际情况设置为 null 或 其他日期2、在 jdbcUrl 中设置对应的对策driver-url=jdbc:mysql://localhost

推荐文章

热门文章

相关标签