什么是虚拟化(virtualization)?
通俗地来说,操作系统将物理(physical)资源转换为更通用、更强大且更易于使用的虚拟形式。因此我们有时候将操作系统称为 虚拟机(virtual machine)
在书中有一段对话形象(或者不形象地)解释了这个概念:
**学生**:尊敬的教授,什么是虚拟化?
**教授**:想象我们有一个桃子。
**学生**:桃子?(不可思议)
**教授**:是的,一个桃子,我们称之为物理(physical)桃子。
但有很多想吃这个桃子的人,我们希望向每个想吃的人提供一个属于他的桃子,这样才能皆大欢喜。
我们把给每个人的桃子称为虚拟(virtual)桃子。
我们通过某种方式,从这个物理桃子创造出许多虚拟桃子。
重要的是,在这种假象中,每个人看起来都有一个物理桃子,但实际上不是。
**学生**:所以每个人都不知道他在和别人一起分享一个桃子吗?
**教授**:是的。
**学生**:但不管怎么样,实际情况就是只有一个桃子啊。
**教授**:是的,所以呢?
**学生**:所以,如果我和别人分享同一个桃子,我一定会发现这个问题。
**教授**:是的!你说得没错。但**吃的人多**才有这样的问题。
多数时间他们都在打盹或者做其他事情,
所以,你可以在他们打盹的时候把他手中的桃子拿过来分给其他人,
这样我们就创造了有许多虚拟桃子的假象,每人一个桃子!
**学生**:这听起来就像糟糕的竞选口号。教授,您是在跟我讲计算机知识吗?
**教授**:年轻人,看来需要给你一个更具体的例子。
以最基本的计算机资源 CPU 为例,
假设一个计算机只有一个 CPU(尽管现代计算机一般拥有 2 个、4 个或者更多 CPU),
虚拟化要做的就是将这个 CPU 虚拟成多个虚拟 CPU 并分给每一个进程使用,
因此,每个应用都以为自己在独占 CPU,
但实际上只有一个 CPU。
这样操作系统就创造了美丽的假象——它虚拟化了 CPU!
虚拟化的主要内容
虚拟化是指在物理硬件之上创建一个或多个抽象层,使得计算机资源可以被更灵活地使用。在操作系统中,虚拟化主要关注:
- 进程: 操作系统通过时间复用/时分共享(time sharing)技术,允许多个进程(程序的执行实例)看似同时运行在同一处理器上。进程是对正在运行的程序及其资源(如内存、设备等)的封装。
- 线程: 线程是进程内的执行单元,是处理器调度的基本单位。线程虚拟化允许同一个进程内的多个线程看似同时执行。
- 内存管理: 操作系统通过虚拟内存机制,使每个进程都认为它拥有连续的内存空间,而实际上物理内存可能是分散的,且多个进程的内存空间可能重叠。
时分共享(time sharing)
Q : 虽然只有少量的物理 CPU 可用,但是操作系统如何提供几乎有无数个 CPU 可用的假象?
A : 操作系统通过虚拟化(virtualization)CPU 来提供这种假象。通过让一个进程只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟 CPU 的假象。这就是时分共享(time sharing)CPU 技术,允许用户如愿运行多个并发进程。潜在的开销就是性能损失,因为如果 CPU 必须共享,每个进程的运行就会慢一点。
**时分共享(time sharing)**是操作系统共享资源所使用的最基本的技术之一。通过允许资源由一个实体使用一小段时间,然后由另一个实体使用一小段时间,如此下去,所谓的资源(例如,CPU 或网络链接)可以被许多人共享。
时分共享的自然对应技术是空分共享,资源在空间上被划分给希望使用它的人。例如,磁盘空间自然是一个空分共享资源,因为一旦将块分配给文件,在用户删除文件之前,不可能将它分配给其他文件。
抽象:进程
进程API
- 创建 create
- 销毁 destroy
- 等待 wait
- 其他控制 miscellaneous control
- 状态 statu (status? 书中确实这样所写):用于获得有关进程的状态信息。
操作系统如何将程序转化为进程的?
-
创建独立的虚拟地址空间:
操作系统为新进程分配一个虚拟地址空间,并对其进行初始化。这个地址空间被配置为包含所有程序执行所需的内存段,其中通常包括代码段、数据段、堆区和栈区等。 -
**装载可执行文件:**将代码静态数据(例如初始化变量)和程序(例如exe)加载(load)到内存中,加载到进程的地址空间中。
- 代码段和数据段加载:操作系统将可执行文件中包含程序指令的代码段和已初始化的静态数据加载到地址空间的相应部分。
- 设置页表或段表:为支持虚拟内存管理,操作系统会设置页表或段表,以实现虚拟地址到物理地址的映射。
- 动态链接处理:如果程序依赖于共享库或动态链接库,操作系统此时也会负责加载这些库,并解析程序中对库函数的调用,使它们指向正确的内存地址。
-
转移CPU控制权至进程并启动运行:
操作系统完成所有启动前的准备工作,包括但不限于对进程控制块(PCB)的初始化、设置程序计数器(PC)至程序入口点、初始化CPU寄存器集等。之后,进程被置于就绪状态并由操作系统的调度器进行调度,一旦被调度器选中,CPU开始执行进程的代码,即操作系统将执行上下文切换,把CPU的控制权交给该进程。
进程状态
简而言之,进程可以处于以下 3 种状态之一:
-
运行(running):在运行状态下,进程正在处理器上运行。这意味着它正在执行指令。
-
就绪(ready):在就绪状态下,进程已准备好运行,但由于某种原因,操作系统选择不在此时运行。
-
阻塞(blocked):在阻塞状态下,一个进程执行了某种操作,直到发生其他事件时才会准备运行。一个常见的例子是,当进程向磁盘发起 I/O 请求时,它会被阻塞,因此其他进程可以使用处理器。
机制:受限直接执行
构建时分共享CPU虚拟化机制的问题:第一个是性能:如何在不增加系统开销的情况下实现虚拟化?第二个是控制权:如何有效地运行进程,同时保留对 CPU 的控制?
目标:在保持控制权的同时获得高性能。
为了使程序尽可能快地运行,操作系统开发人员想出了一种技术——我们称之为受限的直接执行(limited direct execution)。
显然受限直接执行可以分为两个部分:1.受限,2.直接执行
首先来看直接执行是什么:
然后来看“受限”的部分:
- What:
- Why:构建一个操作系统的功能,例如授予文件访问权限前检查权限的文件系统,就不能让进程无限制,不然一个进程就可以读取或写入整个磁盘,所有的保护都会失效。
- How: 引入一种新的处理器模式,成为用户模式(user mode),