【初学操作系统之内存管理】

内存管理


前言

内存管理是操作系统学习的核心要点,希望通过这些总结和手动实验push自己掌握这些知识点。若有错漏,欢迎指正~~


一、初识内存

1、什么是内存?有啥作用?

内存是存放数据的硬件,程序执行前需要先放到内存中才能被CPU处理。

问:多个程序并发执行,数据都要放到内存中,怎么区分哪个程序数据放哪呢?

答:给内存存储单元编地址。就是建很多个小房间(存储单元),然后给房间编号。编号可以按字节编址(1个存储单元1字节,即1B,8个二进制位),也可按字(1个存储单元一个字,16个二进制位)。
在这里插入图片描述

2、为啥要管理内存呢?

(1)方便存储分配
(2)有利于地址变换
(3)“扩充”主存容量
(4)存储保护:怎样进行分区的存储保护呢?可以用界限寄存器保护法:
在这里插入图片描述

用这种方法,每个内存地址在送到内存之前都会自动先加上基址寄存器的内容。

3、 把内存分层:分层存储体系

在这里插入图片描述
为了满足对存储器容量、速度等需求,人们提出了分层存储器体系的概念(如上图)。操作系统的工作是将这个存储体系抽象为一个有用的模型,并管理这个模型。操作系统管理这个模型的部分叫做存储管理器


二、如何管理内存

主要思想是:
建立一些内存模型;
优化管理内存的方案

建立内存模型+改进管理方案:

1、模型 :地址空间

地址空间:一种存储器抽象

通俗来讲,就是聪明的人类在硬件基础上 想象 出一块 有条理 的空间供计算机使用。

地址空间概念的提出是一种对之前的直接访问物理地址的改进。把物理地址直接暴露给进程会产生诸多问题(如可能破坏OS、很难实现同时运行多个程序等等)。而地址空间的提出解决了保护和重定位等问题。

地址空间是对内存的抽象,类比进程对CPU的抽象,是一个进程可用于寻址内存的一套地址集合。这个概念是相对于无存储器抽象,也即直接访问物理地址而言的。

提问:虚拟地址空间的范围有多大呢?是只有用户态?还是只有内核态?还是两者都有?
答:虚拟地址空间 包括 用户态+内核态
.


1)此种模型 如何实现 对内存的管理
基址寄存器和界限寄存器

这两个寄存器能帮助我们实现 动态重定位,给每个程序一个独有的地址空间。
每个内存地址在送到内存之前,都会自动加上基址寄存器中的内容。
.


2)此种模型下的 内存分配与回收

显然,在地址空间模型下,内存的分配是连续的,以下是几种分配方案:

(1) 单一连续存储分区存储管理

适用于单任务系统:在这里插入图片描述

优点:管理简单
缺点:系统只能同时运行一道程序,内存利用率低
.
.
.

(2)固定分区存储分区存储管理

如其名,事先把内存分区,确定好分区数量和大小,之后不能改变:
在这里插入图片描述

优点:系统可以同时运行多道程序
缺点:会产生 “内零头”,造成内存浪费

在这里插入图片描述

(3)可变分区存储分区存储管理

在固定分区上进一步,不搞专制,让内存 按需分配
但是呢,内零头的问题解决了,又冒出了 “外零头”
在这里插入图片描述
产生的问题及解决方案:

  • 外零头怎样产生的?

内存在多次被系统分配、回收后,就会形成已分配区和空闲区相间的状态,这些空闲区太小不足以分配给任何一个作业,造成内存浪费。

  • 怎样解决外零头问题呢?

1)离散式分配程序(把程序分成几部分装入不同的分区)
2)采用“拼接技术”,把零头们拼接成大的空闲区
.
.
.

  • 具体怎样按需分配的呢?

1)准备工作:要按需,肯定是动态分配,操作系统就必须要跟踪内存的使用情况,以判断哪里空闲可存。有两种方法:位图(bitmap)和链表区(Linked list)
在这里插入图片描述
位图:把内存划分成一系列分配单元,每个单元对应于位图中的一位,0表示空闲,1表示占用(或者相反)。分配时,就找位示图中对应位为0的存储块;回收时,就把待回收块的1改为0。

链表:它的一个节点或者包含一个进程,或者是两个进程间的空闲区。每个节点包含以下域:空闲区(H)或进程(P)的指示标志,起始地址,长度和指向下一节点的指针。按链表管理内存,结束进程X时可能产生节点合并的情况:
在这里插入图片描述
.
.
.
2)几个分配算法:按照链表管理内存时,有以下几种方法来分配内存:

首次适应(First fit):管理存储器沿段链表搜索,直到找到一个足够大的空闲区,如果刚好和需要的空间大小一样,那perfect~,如果有多余的,就让多余部分成为新的空闲区。

下次适配:和首次适应基本相同,增加了标记,每次找到合适的空闲区就记录其位置,下次就从上次结束的位置搜索。性能略低于首次适配。

最佳适配:搜索整个链表,找到能容纳进程的最小空闲区。(但是呢,它会产生更多的无用的小空闲区)

最差适配:每次找最大的空闲区。

.
.
.


2、进一步实现模型:虚拟内存

为了解决软件的膨胀、程序大于内存等问题,人们提出了 虚拟内存 的方法。

基本思想:把内存空间分割成很多块,每一块称作一页面。每一页都有连续的地址范围。但并不是所有的页都必须在内存中才能运行程序。每个进程都有自己的页表(因为它有自己的虚拟地址空间)

虚拟内存 的本质就是 进一步实现 地址空间 这个对物理内存的抽象。
.

.

如何进一步 对内存管理
宏观总览

在讲以下几种技术前,让我们先从总体上看看,有虚拟内存的计算机是如何读写地址的:
.
CPU把虚拟地址送到 内存管理单元MMU ,MMU把虚拟地址映射为物理地址。
在这里插入图片描述
.
再来看看 MMU的内部操作:(这里会提前涉及到 分页技术 的内容)

在下图所示的操作中,输入 MMU 的虚拟地址,被划分为:(这是其中一种最简单的实现)

页号 + 偏移量
在这里插入图片描述
MMU 拿到虚拟地址的前几位即 页号 后,通过 页号+页表寄存器里存放的页表始地址,查找页表的索引(下方图中页表的左侧数字,就是页号的十进制),如果找到该页号,则获取其 页框号(对应物理内存中的单元)。

物理地址=把页框号复制到输出寄存器中 + 再加上虚拟地址中的偏移量

“在不在 ”位:记录页面在内存中有没有,没有的话,会出现 缺页中断,文后会详细讲这点
在这里插入图片描述
.
整个过程:
在这里插入图片描述

.

具体实现:
(1)分页技术
总思想:把虚拟地址分解成 页,并把每一页映射到物理内存的某个 页框 或者暂时解除映射
-------结构实现: 页表

具体结构
在这里插入图片描述

组成元素

1)高速禁止缓存位(Caching Disabled)

可以实现 需要不断从设备中(比如I/O设备)读取新数据的硬件 避免 访问一个 旧的 被高速缓存的副本。具有 独立的I/O空间 而不使用 内存映射I/O 的机器不需要这一位。

2)访问位(Referenced)

用来帮助OS在发生 缺页中断 时,选择要淘汰的页面。具体效用,可从后面的 页面置换算法 中感知到。

3)修改位(Modification)

在写入一页时由硬件自动设置修改位。该位在OS重新分配 页框 时很有用。如果一个页面已经被修改过,则必须把它写回磁盘,没有的话,则直接丢弃即可。

4)保护位(Protection)

指示该页面允许什么样的访问,比如读、写、执行。

5)“在不在”位(Present/Absent)

6)页框号


-------管理分页

在任何分页系统中,都要解决两个主要问题:

虚拟地址到物理地址的映射速度必须要十分快,怎样提速?;如果虚拟地址空间很大,页表也会很大,而且每个进程都需要有自己的页表怎样节省内存空间

还有一个问题:在内存中找不到要的数据怎么办?

以下四点提出了解决之道:

1) 页表提速:TLB(转换检测缓冲区)

简单来说,就是把页表中经常访问的那几项拎出来,放在一个特定位置上(它通常在MMU中,是一个小型硬件),这样每次将一个虚拟地址放到 MMU 中进行转换时,都会进行以下步骤

硬件 首先 通过将该虚拟页号与 TLB 中所有表项 并行匹配,判断在不在。如果在,那么直接按规则(保护位)操作,不用再访问页表,否则,就去查 页表。这样快了不少。

提问:如果页表中也没找到呢?
说明它可能在硬盘中,需要去硬盘中找。(具体实现后文会讲到)

一个典型的TLB分页:
在这里插入图片描述

.

2)解决大内存页表:
  • 多级页表
    为何要引入它?可以避免把全部页表一直保存在内存中,从而节约空间。

实现
以32位的虚拟地址为例。虚拟地址被划分为三部分:10位的PT1域,10位的PT2域,12位的Offset域,即二级页表。

MMU首先用 PT1域 作为索引访问 顶级页表 ,得到 二级页表 的地址或 页框号 ,然后用 PT2 域 作为索引访问找到的 二级页表 ,找要找的页面,找到的话,就用得到的
页框号 + 偏移量 = 物理地址

.

  • 倒排页表
    在这里插入图片描述
    .
3)请求分页

回到之前的第三个提问:内存中也找不到所需要的的数据怎么办?

由于分页系统模式下,只需要调入进程的一部分页面就可以运行,而其他的页面是在需要时通过请求OS从外存(磁盘)中调入,称为 请求分页系统

以下以流程图的形式详细的展示其过程:

在这里插入图片描述
.

提问:在请求分页时,如果原来的内存空间已经满了,那么必须替换掉原有的某页,也即图中的 换入 ,那如何决定换哪一页更有利呢?

下面的 页面置换算法 就是为了解决它而提出的。

.
.
.

4)页面置换算法
  • OPT:最优算法

置换 以后 不再访问 的页面,或者离下次访问最远的页面

评价:不是一个实用的算法。是其他实用算法的一个衡量标准。

  • FIFO: 先进先出

以页面调入内存的顺序维护一个页面的链表,让链首的页面被置换。

评价:在内存时间 最短 的页面有可能经常被访问。

  • LRU:最近最久未使用(使用软件模拟)

保持一个页面链表,最近访问的页面在链表的最前端,最近没有访问的在链表的最后端。置换最近没有被访问的页面,即链尾页面。

硬件实现:使用一个64位计数器(硬件寄存器)。
每执行一条指令后计数器的值增1,故计数器的值为程序从开始运行以来已经执行的指令数。当一个页面被引用时,当前计数器的值被保存在页面的页表项中。置换页表项中的计数值最小的页面

软件实现:
1)**NFU:**最不常用页面置换算法

每个页面有一个关联的软件计数器。每个时钟中断时,操作系统扫描每个页面,将页面的R位的值累加于对应的计数器,所以计数器值最大的页面表示访问次数最多的页面。
置换计数器值最小的页面

评价:主要问题是它从不忘记任何事情。如果在第一次扫描中频繁使用的程序到第二次扫描时,它的 计数器值 可能依然很大,而此程序这次 未必频繁使用。

2**)老化算法**

对NFU 的改进,核心思想就是 让计数器的值 定期 清理。

具体做法是,以时钟滴答0 到 时钟滴答1期间为例,访问了的页面,其页面的 R位(访问位)设为1,否则为0,对应页面的计数器的值右移一位,让其R位位于最左端空出来的位上。每个时钟周期都如此。置换计数器值最小的页面

下图蓝色部分为一个页面的R位,1表示访问,黄色方块表示页面0的计数器,其最左端那位就是插入的R位。
在这里插入图片描述

  • 第二次机会,时钟算法,数据集(LRU、CLOCK等)
    第二次机会:把页面按先进先出的方法排列链表,并在页面上记录其装入内存的时间。检查最老页面的R位,若为0,则说明其又老又不常使用,则置换。若为1,则将R清0并把页面放到链表尾端,修改其装入时间使它就像刚装入一样,然后继续搜素。

    时钟算法
    在这里插入图片描述

(2)分段技术

基本思想:按程序自然逻辑结构分段,每个分段作为一个独立的分配单元,可以离散的分配到内存中。
两张图解释其原理:
在这里插入图片描述
在这里插入图片描述

为何要提出 分段?
因为它有一些分页系统没有的优点:

  • 能简化对长度经常变动的数据结构的管理
  • 有助于进程之间的共享。(把要共享的内容放在单独的段里)
  • 有利于段的保护
    总的来说,为了使程序和数据可以被划分为 逻辑上 独立的地址空间并且有助于共享和保护
-------结构实现: 段

在这里插入图片描述
地址映射:
在这里插入图片描述
.

------管理方案

和分页系统一样,也要解决内存中找不到需要数据的问题,即 缺段中断
在这里插入图片描述
.
.

(3)分段+分页技术

基本思想:段内分页
在这里插入图片描述
具体结构:
在这里插入图片描述
如何地址映射:
在这里插入图片描述
如何地址变换:
在这里插入图片描述
分段+分页 结合了分段(易于编程、模块化、保护和共享)和分页(统一的页面大小和只使用段的一部分时不用把它全部调进内存)的优点。

虽然分段技术看起来如此优秀,然而现实是目前其使用范围已经很小,原因很多,简单来说就是,无论是分段还是分页,都是为了使用户体验更好,但是这种技术是有代价的,它会降低计算机的性能(具体怎样可以自行查阅),所以,孰轻孰重,可见分晓。

.
.
.

总结

内存管理技术位于操作系统的重要地位,其中运用的思想、技术都十分Amazing!虽然本篇很长,但总的思路就一句话:要管理内存,就建立抽象模型,并寻求对应的管理方案。
方案多种多样,有些看起来在此处不实用,但是在彼处可能有意想不到的奇效!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值