写点什么

『内存中的操作系统』内存虚拟化又是什么

作者:soolaugust
  • 2022 年 1 月 19 日
  • 本文字数:1971 字

    阅读完需:约 6 分钟

『内存中的操作系统』内存虚拟化又是什么

思维模型

  1. 如何虚拟化内存

  2. 虚拟化内存有哪些准则或者目标么

  3. 内存虚拟化带来的影响

1. 如何虚拟化内存


在早期的系统, 机器并没有抽象出虚拟内存给用户. 基本上, 物理内存像下图所示:



操作系统是一组函数, 在内存中从物理地址 0 开始, 然后有一个正在运行的程序(如之前的文章所说, 早期机器是串行运行程序的), 在途中是从 64KB 的物理地址开始的, 使用剩余的内存.


但是那时候机器非常昂贵, 从上面来看当前程序并不一定能充分使用剩余的内存. 因为正因为之前的文章所说, 出现了多道程序和分时,.


多个进程在给定时间准备运行,比如当有一个进程在等待 I/O 操作的时候,操作系统会切换这些进程,这样增加了 CPU 的有效利用率(utilization)。


但很快,人们开始对机器要求更多,分时系统的时代诞生了。具体来说,许多人意识到批量计算的局限性,尤其是程序员本身[CV65],他们厌倦了长时间的(因此也是低效率的)编程—调试循环。交互性(interactivity)变得很重要,因为许多用户可能同时在使用机器,每个人都在等待(或希望)他们执行的任务得到及时响应。


一种实现时分共享的方法,是让一个进程单独占用全部内存运行一小段时间(见图 13.1),然后停止它,并将它所有的状态信息保存在磁盘上(包含所有的物理内存),加载其他进程的状态信息,再运行一段时间,这就实现了某种比较粗糙的机器共享。

于是产生了下面的共享内存结构:



这时候由于多个程序同时驻留在内存中, 内存保护又成为了急需解决的问题, 人们不希望一个进程可以读取其他进程的内存,更别说修改了。


所以这个时候出现了第一个物理内存抽象: 地址空间(address space).这个是运行中的程序能够看到的系统内存.而这个就是内存虚拟化的关键.


一个进程的地址空间包含运行的程序的所有内存状态。比如:程序的代码(code,指令)必须在内存中,因此它们在地址空间里。当程序在运行的时候,利用栈(stack)来保存当前的函数调用信息,分配空间给局部变量,传递参数和函数返回值。最后,堆(heap)用于管理动态分配的、用户管理的内存,就像你从 C 语言中调用 malloc()或面向对象语言(如 C ++或 Java)中调用 new 获得内存。当然,还有其他的东西(例如,静态初始化的变量),但现在假设只有这 3 个部分:代码、栈和堆。



如上图, 我们给每个程序分配了 16KB 的地址空间. 因为程序运行时, 堆和栈都会增长,所以放到地址空间的两端(这只是一种示例). 而这时就提供了一种物理地址的抽象, 因为对于程序来说, 他说看到的物理地址其实并不是真正的物理地址, 他的 0KB 的地址可能是物理地址中的任何一个地址. 而这就叫做虚拟化内存.


2. 虚拟化内存有哪些准则或者目标么


我们上面既然说到了虚拟化内存, 那么在虚拟化内存的详细设计前, 我们应当首先指定一些准则或者目标来规划我们的设计(这个应该是每一个想做架构师或者程序设计的工程师要铭记的).


首先按照上面的说法, 第一个目标应该是透明. 也就是对于上层进程来说无感知. 我们虽然是做了虚拟内存, 但是不希望上层进程再去做适配. 相反, 我们应该给进程一个假象, 那就是自己拥有了自己的私有内存.


第二个目标就是效率, 操作系统应该追求虚拟化尽可能高效(efficient),包括时间上(即不会使程序运行得更慢)和空间上(即不需要太多额外的内存来支持虚拟化)。如果说我们做虚拟化结果比原来的效果还差, 那这个做的就没有任何意义.


第三个目标是保护, 操作系统应确保进程受到保护(protect),不会受其他进程影响,操作系统本身也不会受进程影响。当一个进程执行加载、存储或指令提取时,它不应该以任何方式访问或影响任何其他进程或操作系统本身的内存内容(即在它的地址空间之外的任何内容)。因此,保护让我们能够在进程之间提供隔离(isolation)的特性,每个进程都应该在自己的独立环境中运行,避免其他出错或恶意进程的影响。


3. 内存虚拟化带来的影响

那么通过我们上面对内存虚拟化的思考, 会给我们接下来的应用行为带来什么影响呢? 虽然我们上面说的内存虚拟化第一个目标就是对上层进程无感知, 但是还是产生了一个影响, 那就是进程中看到的所有地址都不是实际的物理地址.


即使我们写一个 C 程序去获取地址, 也是虚拟地址, 而不是实际的物理地址.比如下面的代码:


#include <studio.h>#include <stdlib.h>
int main(int argc, char *argv[]) { printf("location of code : %p\n", (void *) main); // 程序地址 printf("location of heap : %p\n", (void *) malloc(1)); // 堆地址 int x=3; printf("location of stack : %p\n", (void *) &x); // 栈地址 return x; }
复制代码

我们可能会得到下面的地址(每个机器上面可能不一样):


location of code : 0x1095afe50location of heap : 0x1096008c0location of stack : 0x7fff691aea64
复制代码

而这些地址其实都是虚拟地址, 实际的物理地址将由操作系统和硬件进行翻译得到.从而在真实的物理内存位置中获取到值.


参考文献:

[1] 操作系统导论

发布于: 刚刚阅读数: 4
用户头像

soolaugust

关注

公众号:雨夜随笔 2018.09.21 加入

公众号:雨夜随笔

评论

发布
暂无评论
『内存中的操作系统』内存虚拟化又是什么