内存管理两部曲之虚拟内存管理

传统存储管理存在的问题

虚拟内存这个东西他为何会出现?他出现的背景是什么?git

前文 内存管理两部曲之物理内存管理 提到:随着用户程序功能的增长,进程所须要的内存空间愈来愈大,进程空间很容易就突破了物理内存的实际大小,致使进程没法运行。web

所以,为了解决内存不足的状况,缓和大程序与小内存之间的矛盾,扩充内存容量势在必行。算法

能够从物理和逻辑两方面来考虑扩充内存容量,物理扩容没啥技术含量,须要咱们研究的天然是如何从逻辑上扩充内存容量。缓存

所谓逻辑扩充,就是说实际上物理内存的容量没有发生改变,可是它能装的东西却变多了,使得用户看来彷佛有一个比实际内存大得多的内存。微信

对内存的逻辑扩充技术主要有三种:覆盖技术、交换技术、以及虚拟内存(Virtual Memory),也称为虚拟存储器。事实上,这些逻辑扩充技术的核心理念都是一致的,研究的都是将哪一个进程(或进程的某部分)暂时从内存移到外存(磁盘),以腾出内存空间供其余进程(或进程的某部分)占用。app

覆盖(Overlay)和交换(Swapping)这两种存在于早期操做系统中的逻辑扩充技术如今已经成为历史,这里就简单介绍下:编辑器

前文说过,早期操做系统仅将内存空间分红两块:系统区(用于存放操做系统相关数据)和用户区(用于存放用户进程相关数据,内存中只能有一道用户程序,用户程序独占整个用户区空间,显然,内存空间容不下某个用户程序的现象常会发生。学习

覆盖技术(Overlay)的基本思想就是:程序运行时并不是任什么时候候都要访问程序及数据的全部部分(尤为是大程序),所以能够把用户空间(内存)分红一个固定区和一个或多个覆盖区。flex

将程序常常活跃的部分放在固定区,其他部分按调用关系进行分段:首先将那些即将要用的段放在覆盖区,其余段放在外存(磁盘),在须要调用前由用户来安排特定的系统调用将这些放在外存中的段调入覆盖区,替换覆盖区中原有的段。url

覆盖技术的缺点显而易见而且能够说是让人没法接受的,那就是覆盖技术是把解决内存空间不足的问题交给了用户。操做系统仅仅为用户提供将覆盖段调入内存的系统调用,可是必须由用户本身来讲明覆盖哪一个段、调入哪一个段。

合着我用个电脑还得算着怎么才能让个人程序不崩溃?

OK,能够看出来,覆盖技术实际上是用在同一个做业/进程的不一样段之间的,那么不一样的做业/进程之间怎么办呢?

这就是交换技术的适用场景。

交换技术(Swapping)的基本思想是:空闲进程/做业主要存储在外存(磁盘)上,当其中某个进程/做业须要运行的时候,就将其从磁盘中完整地调入内存,使该进程运行一段时间,而后再把它返回磁盘。因此说当进程/做业不运行的时候它们是不会占用内存的。


事实上,覆盖和交换技术分别解决了传统存储管理(物理内存管理)中存在的某个问题:

  • 覆盖技术打破了 做业/进程必须一次性所有装入内存后才能开始运行(一次性)的限制
  • 交换技术打破了 一旦做业被装入内存,就会一直驻留在内存中,直至做业运行结束(驻留性)的限制

固然了,Anyway,这两种逻辑扩充技术已经成为历史,虚拟内存技术才是目前的主流,它综合了这两种古老技术的特色,单枪匹马解决了传统存储管理中存在的这两个问题。

什么是虚拟内存

有了上述交换技术的铺垫,理解起虚拟内存来也就不那么陌生了。

固然了,在此以前,我必定要着重声明的是,不要把虚拟内存看成一个实际存在的东西,它是一门技术!和交换技术覆盖技术同样是一门用来逻辑扩充内存空间的技术!

虚拟内存技术基于一个很是重要的原理,局部性原理

1)时间局部性:若是执行了程序中的某条指令,那么不久后这条指令颇有可能再次执行;若是某个数据被访问过,不久以后该数据极可能再次被访问。(由于程序中存在大量的循环)

2)空间局部性:一旦程序访问了某个存储单元,在不久以后,其附近的存储单元也颇有可能被访问(由于不少数据在内存中都是连续存放的,而且程序的指令也是顺序地在内存中存放的)

基于这个局部性原理,在一个程序装入内存的时候,能够只将这个程序中很快会用到的部分装入内存,暂时用不到的部分仍然留在外存(磁盘),而且程序能够正常执行;

而在程序执行过程当中,当 CPU 所须要的信息不在内存中的时候,由操做系统负责将所需信息从外存(磁盘)调入内存,而后继续执行程序;

若是调入内存的时候内存空间不够,由操做系统负责将内存中暂时用不到的信息换出到外存。

以上,就是虚拟内存技术。

如何实现虚拟内存技术

能够看见,虚拟内存容许一个做业/进程分屡次调入内存,那若是采用连续分配方式,不方便实现,因此虚拟内存技术的实现是创建在不连续分配管理方式之上的。

传统的基本分页管理、基本分段管理、基本段页式管理和虚拟内存技术结合,分别称为请求分页管理(页式虚存系统)、请求分段管理(段式虚存系统)、请求段页式管理(段页式虚存系统)。

这几个概念很是容易混淆,其实很容易区分,记住这句话就 OK,摘自百度百科:

若是不具有请求调页、页面置换的功能,则称为基本分页管理(或称为纯分页管理),它不具备支持实现虚拟内存的功能,它要求把每一个做业(进程)所有装入内存后方能运行。

请求分段存储管理也差很少,它创建在分段存储管理之上,但增长了请求调段、段置换功能。

请求调页、页面置换 和 请求调段、段置换概念差很少,这里以请求调页和页面置换为例解释下。

  • 在程序执行过程当中,当所访问的信息不在内存时,由操做系统负责将所需信息从外存(磁盘)调入内存,而后继续执行程序(操做系统要提供 请求调页的功能, 将内存中缺失的页面从磁盘调入内存 );
  • 若内存空间不够,由操做系统负责将内存中暂时用不到的信息换出到磁盘(操做系统要提供 页面置换的功能, 将暂时用不到的页面换出磁盘)。

具体来讲,在页式虚存系统中,每当 CPU 要访问的页面不在内存时,就会产生一个缺页中断,而后由操做系统的缺页中断处理程序来处理中断。此时,缺页的这个进程/做业就会被阻塞住,放入阻塞队列,调页完成后再将其唤醒,放回就绪队列。

  • 若是内存中有空闲块,则为该进程分配一个空闲块,将所缺的页面装入这个块中,并修改页表中相应的页表项。
  • 若是内存中没有空闲块,则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回外存,未修改过的页面不用写回外存。

能够看出来,这并非一个简单的过程,基本分页管理中的简单页表已经没法胜任这样的工做。

咱们仍是先来回顾下基本分页管理的页表,它只有页号和块号两个字段:

请求分页管理的页表天然是会复杂很多的:

1)为了实现 “请求调页” 功能,操做系统须要知道每一个页面是否已经调入内存,若是还没调入,那么也须要知道该页面在磁盘中存放的位置。

2)而当内存空间不够时,要实现 “页面置换” 功能,操做系统须要经过某些指标来决定到底换出哪一个页面,有的页面没有被修改过,就不用浪费时间写回磁盘;有的页面修改过,就须要将磁盘中的旧数据覆盖。所以,操做系统也须要记录各个页面是否被修改的信息。

为此,请求分页管理的页表中添加了 4 个字段:

  • 状态位:该页面是否已调入内存
  • 访问字段:可记录该页面最近被访问过几回,或记录上次访问该页面的时间,供页面置换算法换出页面时参考
  • 修改位:该页面调入内存后是否被修改过
  • 外存地址:该页面在外存中的存放地址

页面置换算法也是一个很重要的内容,原本应该在这篇文章里一块儿写的,But 想到 “页面置换” 问题不只仅是在虚拟内存中存在,在计算机设计的其余领域也会一样发生(好比多数计算机都会把最近使用过的 32 字节或者 64 字节存储块保存在一个或多个高速缓存中,当这些高速缓存存满后就必须选取一些块丢弃掉,以此来存入最新的使用过的存储块),因此决定后续单开一篇文章。




  • 博主小硕在读,深耕 Java,目前在维护一个教程类仓库 CS-Wiki 「Gitee 官方推荐项目,现已 1.7k+ star,仓库地址:https://gitee.com/veal98/CS-Wiki」,公众号上的文章也会在此同步更新,欢迎各位前来交流学习。
  • 准备春招秋招的小伙伴能够参考个人这个论坛项目 Echo 「Gitee 官方推荐项目,现已 800+ star,仓库地址:https://gitee.com/veal98/Echo」。配套教程正在同步更新中,公众号后台回复 "Echo" 便可免费获取。
  • 另外,虽然如今本号仍然很小,不过我仍是建了一个交流群『 小牛肉和它的小伙伴们 』,感兴趣的各位能够下方扫码加我微信回复 "进群",我拉你进群:

本文分享自微信公众号 - 飞天小牛肉(CS-Wiki)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索