虚拟化技术介绍

2024-07-14 发布 0条评论

虚拟化技术介绍

极简介绍

对于我们想要虚拟化的对象,提供实际资源之上的抽象。应用不同抽象级别将会呈现不同的虚拟化技术。

基于抽象级别的虚拟化技术有两类最为常用:

[toc]

Hypervisors

用于虚拟化的特殊软件通称,其本身有两个部分:

VMM

硬件在绝大多数情况下不能直接在虚拟机上使用,因此需要通过VMM捕获硬件(磁盘、网卡…)的特权指令,并代表虚拟机执行这些指令。其需满足三个属性:

功能

内存虚拟化

CPU虚拟化

这篇文章,对虚拟化特权级进行了全面解释。

代码定位见此

全虚拟化与半虚拟化

全虚拟化中,客户OS(guest OS)“不知道”自己运行在虚拟环境中,它的运行状态和在物理机中相同;而半虚拟化状态中,guest OS对虚拟环境做了特化,I/O的系统调用会被hypercalls替换掉。

它们的结构区别如下图所示:
20230803000233

半虚拟化状态中,guest os的驱动程序被称为前端驱动程序,主机的被称为后端驱动程序,这种前后端的实现方式很大的提高了交互效率,具体见virtio

对于I/O虚拟化,参见sriov

Hypervisors介绍

INTEL有两种虚拟化风格:Vt-xVt-i

本节将更为熟悉的Vt-x指令集

VT-x指令集

物理机中,执行顺序是程序指令->CPU指令;
而在虚拟机中,执行顺序是程序指令->虚拟CPU指令->CPU指令。
这将导致指令翻译两次,会造成巨大开销(详见这篇文章)

通过抽象CPU指令到虚拟指令,即对CPU指令扩充一些虚拟专用指令,可以在虚拟机完成:程序指令->CPU指令

对于Vt-x来说,它还做了一些权限分割上的工作,具体可见这篇关于ring -1的回答

细节了解和代码定位,参考这篇博客

KVM创建虚拟机

在用户态中创建虚拟机的过程如下:

具体信息和代码定位详见KVM官网相关文档

基于vhost的数据交互

这里以一个vhost-net设备的生命周期为例。

20230808001723

该设备初始化时,会为QEMU对应进程创建一个内核线程,专门处理guest I/O,该线程侦听主机端virtqueues上的事件。

事件为传输时,guest上的TX(传输)队列排出(virtio术语叫kick)数据包,线程将该包放到抽头设备(tap),后继续使用正常底层网络设备(路由、交换机)等进行常规网络包传输工作;接收(RX)同理。

事件注册采用eventfd机制,这是文件描述符,具体功能和机制参见这篇博客

其他虚拟化尝试

Unikernel

作为虚拟化技术的一种尝试,基础思想就是;仅将应用所需的环境包装到虚拟环境中,即一个虚拟机完成单一的应用功能。好处显然易见——轻量、安全、快速。但是单一的应用和复杂的打包机制也成为制约unikernel不利条件。

其基础概念可以查看官网
与容器的比较可以参考这篇博客

Project-Dune

斯坦福大学设计的一种机制,可以通过虚拟化技术让用户程序跑在ring 0中,即在非特权的上下文中执行特权指令。

原理可用一张图表示:
20230810235726

代码被分为受信任部分和非受信任部分,它们都运行在同一个物理地址上,但受信任的代码所在页被hypervisor bits位标记保护。受信任的代码通过Dune进程(由libdune库提供)运行在ring 0,非受信任的代码运行在ring 3。

详细信息可查阅project-dune官网

novm

在VM中提供了一个文件系统,而非块设备。是一种虚拟机的轻量化实现。

具体可见github项目

容器

Linux容器有三个组成基础:

Namespace

命名空间是Linux中的一种进程的逻辑隔离技术,即缩小进程的可见性。对于namespace的进程来说,它看不到除自己namespace外的其他资源。

namespace有很多类型:

其详情介绍可以参考wiki

原理分析和用法可以参考这篇博客,这大哥的博客写的是真好,从实践到原理到源码介绍的非常细致。如果有时间推荐两部分都看一下。

Cgroup

cgroups(Control Group)是一项 Linux 内核功能,用于限制、说明和隔离进程集合的资源使用情况(CPU、内存、磁盘 I/O 等 )
有两个版本,v1(2.6.24)和v2(4.5)。

v2的版本介绍可以参考wiki百科kernel官方文档

v1的版本介绍可以参考kernel官方文档

分层文件系统

文件系统

Linux的设计理念是“万物均文件”,即系统中所有对象均使用文件进行抽象,访问这些对象和访问文件的过程是一致的(包括块文件和非块文件)。

Linux使用虚拟文件系统(VFS)层抽象所有文件系统。所有文件系统均在VFS中注册。其具有以下重要的数据结构:

详细内容可以参考这篇文章,和这个专栏系列中文件系统部分

伪文件系统

对于一些非块文件如procfs,它们可能通过文件接口公开内核的的一些资源,这些文件系统被称为伪文件系统

可参考论坛回答

分层文件系统简介

在上述Linux文件系统中,有两件事是容器需要但其无法满足的:

在分层文件系统中,文件系统被分成多层,每层都是只读文件系统

这些层在同意主机上的容器之间共享,且inode是相同的,它们将会引用相同操作系统页面进行缓存。

下面将介绍两种Linux中正在使用的分层文件系统。

unionfs(UFS)

unionfs允许管理员将文件在物理上分开,但在逻辑上合并到单个视图中。

合并目录的集合称为联合(黄色部分),每个物理目录称为分支(蓝色部分)。
20230815004314

每个分支都分配有一个优先级。具有较高优先级的分支会覆盖具有较低优先级的分支。 Unionfs 对目录进行操作。如果一个目录存在于两个底层分支中,则Unionfs目录的内容和属性是两个较低目录的组合。 Unionfs 自动删除任何重复的目录条目,因此用户不会因重复的文件名或目录而感到困惑。如果一个文件存在于两个分支中,则Unionfs文件的内容和属性与高优先级分支中的文件相同,而低优先级分支中的文件被忽略。

需要注意的是,当多个同名文件挂载到同联合中时,默认复制第一个文件中的内容。

测试:

$ ls /Fruits
Tomato hi/
$ ls /Veg
Tomato

$ cat /Fruits/Tomato
I am botanically a fruit.
$ cat /Veg/Tomato
I am horticulturally a veg.

# 联合目录挂载到 Eat
$ mount -t overlay -o dirs=/Fruits:/Veg overlay /Eat

# 查看Tomato的内容
$ cat /Eat/Tomato
I am botanically a fruit.

# 查看目录合并结果
$ ls /Eat

unionfs包括aufs均已过时,现在基本都用overlayoverlay2作为容器的文件系统。

参考如下链接:

祖宗:2004年的Linux期刊
原理剖析+源码解读
@knoldus/unionfs-a-file-system-of-a-container-2136cd11a779">直观图片+完整实验

Overlayfs

Linux 3.18内核版本之后,overlayfs被集成进内核。

其作用是将一个目录地内容覆盖到另一个目录上

当前有两个版本:v1和v2
v1有两层:

规则:

  1. 当进程“读取”文件时,overlayfs文件系统驱动将优先在上层目录upperdir中查找并从该目录中读取文件,找不到则在下层目录lowerdir中查找。
  2. 当进程”写入”文件时,overlayfs会将其写入上层目录upperdir。

v2有三层:

v2示例:

初始化创建
root@instance-1: mkdir base diff overlay workdir 
root@instance-1: echo "test data" > base/test1 
root@instance-1: sudo mount \ > -t overlay \ 
> -o lowerdir=base,upperdir=diff,workdir=workdir \ 
> overlay \ 
> overlay

过程和上一节一致,不做赘述。

修改overlay
root@instance-1:/overlay# touch test2 
root@instance-1:/overlay# ls test1 
test2 
root@instance-1:-/overlay# cd ../diff 
root@instance-1:-/diff# ls 
test2

可见修改的结果在diff中可见

可以动手试试修改文件内容,看在overlay和base中如何变化

参考这篇博客

发表评论