首页 > 技术文章 > 虚拟化整理

TopStar 2017-07-23 07:32 原文

 

虚拟化整理

整理要点

  • 系统调用
  • 全虚拟化半虚拟化
  • 虚拟化、容器、云计算


虚拟化概述

虚拟化,是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。 —— 百度百科

虚拟化提供大量互相隔离的计算资源。为企业提供了基础环境

系统调用

用户态和内核态

首先说一个概念,linux 分为用户态和内核态。而现在的X86架构或X64架构的CPU 都会被分为ring0 ~ring3 四个等级。数字越小权限越大。linux和windows都只使用了ring0和ring3。应用程序一般运行在ring3层的用户态,操作系统运行在ring0层的内核态

内核态: 能够对cpu,内存,硬盘等做各种操作

用户态: 只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU资源可以被其他程序获取

系统调用

当运行在用户态的应用程序想要执行一些高级的操作的时候比如调用CPU 内存等资源。这些事情只能由在ring0的操作系统来做。操作系统提供给应用程序做一些只有在内核态才能做的操作的接口就叫做系统调用。

中断

当用户进程需要发生系统调用时,CPU 通过中断切换到内核态开始执行内核系统调用的函数。那么什么是中断呢?中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。比如,在x86机器上可以通过int指令进行软件中断,而在磁盘完成读写操作后会向CPU发起硬件中断。

用户栈与内核栈

内核在创建进程时,会为进程创建用户栈和内核栈。进程陷入内核态后,先把用户态堆栈的地址保存在内核栈之中,然后设置堆栈指针寄存器的内容为内核栈的地址,当进程从内核态恢复到用户态之行时,在内核态之行的最后将保存在内核栈里面的用户栈的地址恢复到堆栈指针寄存器即可。这就是用户栈和内核栈的切换

全虚拟化半虚拟化

半虚拟化

一台服务器上已经运行了一个操作系统,那么这个操作系统自然运行在ring0级别。虚拟机的操作系统这时就不能运行在ring0了。这时候如果要实现系统调用。就出现了半虚拟化。所谓半虚拟化就是对客户操作系统代码进行了修改,增加了一个专门的API,帮助客户操作系统陷入ring0级别,这时就能执行一些在内核态才能执行的一些特权指令了。

全虚拟化

全虚拟化是目前CPU产商生产的x86和x64的cpu均有支持全虚拟化的技术(Intel VT-x和AMD-V)。方式就是让CPU 运行2种模式。一种运行host OS(VMX Root Mode),一种运行guest OS(VMX non-Root Mode)。两种模式都拥有ring0~ring3的运行级别。

Docker

LXC

LXC(Linux Container)是一种轻量级虚拟化技术。他通过调用Cgroup和Namespace实现了Docker的资源管理和隔离。它为进程提供了一个虚拟的执行环境。是Docker的实现基础。 项目官网。可以把LXC看成是加强版的chroot。Docker最初的实现是在LXC上做了一层封装。但是0.9后的版本改用了Libcontainer 。

Cgroup

Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:cpu,memory,IO等等)的机制。最初由google的工程师提出,后来被整合进Linux内核。—— 百度百科

Libcontainer 调用Cgroup。对Docker做资源的管理。当然KVM 的资源管理底层实现也用到了Cgroup

Namespace

有了环境,实现了资源的管理。那么还需要将应用程序做一个隔离。让他们之间不会产生影响。Namespace就是用来做这件事的。Namespace是linux的系统调用。总共有以下6种Namespace:

Namespace系统调用参数隔离内容
UTS CLONE_NEWUTS 主机名与域名
IPC CLONE_NEWIPC 信号量、消息队列和共享内存
PID CLONE_NEWPID 进程编号
Network CLONE_NEWNET 网络设备、网络栈、端口等等
Mount CLONE_NEWNS 挂载点(文件系统)
User CLONE_NEWUSER 用户和用户组

Namespace API提供了三种系统调用接口:

接口用途
clone() 创建新进程的同时创建namespace
setns() 加入一个已经存在的namespace
unshare() 在原先进程上进行namespace隔离

查看Namespace号,[ ]内
可以通过mount命令将上面的命名空间保存起来,后面可以利用setns()系统调用去加入它。

# ls -al cd /proc/$pid/ns/

total 0
lrwxrwxrwx 1 root root 0 Aug 16 17:17 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Aug 16 17:17 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Aug 16 17:17 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Aug 16 17:17 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Aug 16 17:17 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 16 17:17 uts -> uts:[4026531838]

docker结构图

Libcontainer

首先Libcontainer 会将一些linux 的文件系统都挂载到rootfs上。它可以调用Namespace做隔离,调用Cgroup做资源管理。它是Docker中用于容器管理的包,它类似于LXC是一个执行环境。

Execdriver

execdriver是用来管理Libcontainer和LXC。用它来做容器的封装。

Networkdriver

docker默认的网络驱动有3种。bridge,host,null

  • bridge docker重新为容器分配一个网络namespace。将容器连接到一个虚拟网桥(docker0)。docker通过这个虚拟网桥来联通这个容器和宿主机
  • none 网络在一个特定于容器的网络堆栈上添加了一个容器。该容器缺少网络接口。
  • host docker将不会为使用host模式的容器创建新的namespace。而是使用宿主机的网络namespace,这样容器和宿主机就是共用同一个网卡,共同的IP

Graphdriver

层叠镜像模型(layered image model)允许一个或者多个容器进程共享文件系统内容。它是一个分层的缓存。要在运行的时候管理这些分层,需要一个驱动来将这些分层挂载到一个合并的rootfs里面。用来处理这些分层的驱动就就是graphdriver。例如vfs、aufs、overlay、overlay2。

推荐阅读