内存管理
考纲内容
- 内存管理基础
内存管理概念:逻辑地址与物理空间,地址变换,内存共享,内存保护,内存分配与回收
连续分配管理方式:页式管理;段式管理;段页式管理 - 虚拟内存管理
虚拟内存基本概念;请求页式管理;页框分配;页置换算法
内存映射文件;虚拟存储器性能的影响因素及改进方式
内存管理和进程管理是操作系统的核心内容,需要重点复习
本章围绕分页机制展开:通过分页管理方式在物理内存大小的基础上提高内存的利用率,再进一步引入请求分页管理方式,实现虚拟内存,使内存脱离物理大小的限制,从而提高处理器的利用率
内存管理概念
内存管理的基本原理和要求
原因:不可能将所有用户进程和系统所需要的全部程序与数据放入主存。因此操作系统必须**对内存空间进行合理的划分和有效的动态分配,即内存管理
**
有效的内存管理在多道程序设计中非常重要,它不仅可以**方便用户使用存储器、提高内存利用率
,还可以通过虚拟技术从逻辑上扩充存储器
**
主要功能:
- 内存空间的分配与回收。由操作系统完成主存储器空间的分配和管理,使程序员摆脱存储分配的麻烦,
提高编程效率
- 地址转换。在多道程序环境下,程序中的逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供地址变换功能,
把逻辑地址转换成相应的物理地址
- 内存空间的扩充。利用**
虚拟存储技术或自动覆盖技术
,从逻辑上扩充内存
** - 内存共享。指**
允许多个进程访问内存的同一部分
**。例如,多个合作进程可能需要访问同一块数据,因此必须支持对内存共享区域进行受控访问 - 存储保护。
保证各道作业在各自的存储空间内运行,互不干扰
1、程序的链接与装入
创建进程首先要将程序和数据装入内存
。将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤:
- 编译。由**
编译程序将用户源代码编译成若干目标模块
** - 链接。由**
链接程序
将编译后形成的一组目标模块及它们所需的库函数链接在一起,形成一个完整的装入模块
** - 装入。由**
装入程序
将装入模块装入内存运行
**
程序的链接有以下三种方式:
- 静态链接
在程序运行之前,先**将各目标模块及它们所需的库函数链接成一个完整的装配模块,以后不再拆开
**
将几个目标模块装配成一个装入模块时,需要解决两个问题:
1、修改相对地址,编译后的所有目标模块都是从0开始的相对地址,当链接成一个装入模块时要修改相对地址
2、变换外部调用符号,将每个模块中所用的外部调用符号也都变换为相对地址
- 装入时动态链接
将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的方式
。其优点是**便于修改和更新,便于实现对目标模块的共享
** - 运行时动态链接
对某些目标模块的链接,是在程序执行中需要该目标模块时才进行的。凡**在执行过程中未被用到的目标模块,都不会被调入内存和被链接到装入模块上
。其优点是能加快程序的装入过程
,还可节省大量的内存空间
**
内存的装入有以下三种方式:
- 绝对装入
绝对装入方式**只适用于单道程序环境
。在编译时,若知道程序将驻留在内存的某个位置,则编译程序将产生绝对地址的目标代码。绝对装入程序按照装入模块中的地址,将程序和数据装入内存
。由于程序中的逻辑地址与实际内存地址完全相同
,因此不需对程序和数据的地址进行修改
**
另外,程序中所用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。而通常情况下在程序中采用的是符号地址,编译或汇编时再转换为绝对地址 - 可重定位装入
在多道程序环境下,多个目标模块的起始地址通常都从0开始,程序中的其他地址都是相对于起始地址的
,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入内存的适当位置
。在装入时对目标程序中指令和数据地址的修
改过程称为重定位,又因为**地址变换通常是在进程装入时一次完成的
,故称为静态重定位
当一个作业装入内存时,必须给它分配要求的全部内存空间
,若没有足够的内存,则无法装入
。此外,作业一旦进入内存,整个运行期间就不能在内存中移动,也不能再申请内存空间
** - 动态运行时装入
也称动态重定位
。程序在内存中若发生移动
,则需要采用动态的装入方式。装入程序把装入模块装入内存后,并**不立即把装入模块中的相对地址转换为绝对地址
,而是把这种地址转换推迟到程序真正要执行时才进行
。因此,装入内存后的所有地址均为相对地址
。这种方式要一个重定位寄存器的支持
**
动态重定位的优点:可以将程序分配到不连续的存储区;在程序运行之前可以只装入部分代码即可投入运行,然后在程序运行期间,根据需要动态申请分配内存:便于程序段的共享
2、逻辑地址与物理地址
编译后,每个目标模块都从0号单元开始编址
,这称为该目标模块的相对地址(或逻辑地址)
当链接程序将各个模块链接成一个完整的可执行目标程序时,链接程序顺序依次按各个模块的相对地址构成统一的从0号单元开始编址
的逻地址空间(或虚拟地址空间)
进程在运行时,看到和使用的地址都是逻辑地址
不同进程可以有相同的逻辑地址
,因为**这些相同的逻辑地址可以映射到主存的不同位置
**
物理地址空间是指**内存中物理单元的集合
,它是地址转换的最终地址
**
进程在运行时执行指令和访问数据,最后都要通过物理地址从主存中存取
当装入程序将可执行代码装入内存时,必须**通过地址转换将逻辑地址转换成物理地址
,这个过程称为地址重定位**
操作系统通过内存管理部件(MIMU)将进程使用的逻辑地址转换为物理地址。进程使用虚拟内存空间中的地址,操作系统在相关硬件的协助下,将它“转换”成真正的物理地址。逻辑地址通过⻚表映射到物理内存,⻚表由操作系统维护并被处理器引用
3、进程的内存映像
不同于存放在硬盘上的可执行程序文件,当一个程序调入内存运行时,就构成了进程的内存映像
一个进程的内存映像一般有几个要素:
- 代码段:即程序的二进制代码,代码段是**
只读
的,可以被多个进程共享
** - 数据段:即**
程序运行时加工处理的对象
,包括全局变量和静态变量
** - 进程控制块(PCB):存放在系统区。操作系统**
通过PCB来控制和管理进程
** - 堆:用来**
存放动态分配的变量
**。通过调用malloc
函数动态地向高地址分配空间 - 栈:用来**
实现函数调用
**。从用户空间的最大地址往低地址方向增⻓
代码段和数据段在程序调入内存时就指定了大小,而堆和栈不一样
当调用像malloc
和free
这样的C标准库函数时,堆可以在运行时动态地扩展和收缩
用户栈在程序运行期间也可以动态地扩展和收缩,每次调用一个函数,栈就会增⻓;从一个函数返回时,栈就会收缩
一个进程在内存中的映像:
共享库用来存放进程用到的共享函数库代码
,如printf()
函数等
在只读代码段中,.init
是程序初始化时调用的_init
函数;.text
是用户程序的机器代码;.rodata
是只读数据
在读/写数据段中,.data
是已初始化的全局变量和静态变量;.bss
是未初始化及所有初始化为0的全局变量和静态变量
4、内存保护
确保每个进程都有一个单独的内存空间
。内存分配前,需要保护操作系统不受用户进程的影响,同时保护用户进程不受其他用户进程的影响
内存保护可采取两种方法:
- 在CPU中**
设置一对上、下限寄存器
,存放用户作业在主存中的下限和上限地址
**,每当CPU要访问一个地址时,分别和两个寄存器的值相比,判断有无越界 - 采用**
重定位寄存器
(又称基地址寄存器
)和界地址寄存器
(又称限⻓寄存器
)来实现这种保护。重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址的最大值
**。内存管理机构动态地将逻辑地址与界地址寄存器进行比较,若未发生地址越界,则加上重定位寄存器的值后映射成物理地址,再送交内存单元
重定位寄存器是用来**加
的,逻辑地址 + 重定位寄存器中的值 = 物理地址
**
界地址寄存器是用来**比
的,通过比较界地址寄存器中的值与逻辑地址的值来判断是否越界
**
加载重定位寄存器和界地址寄存器时**必须使用特权指令
,只有操作系统内核才可以加载这两个存储器
**
5、内存共享
并不是所有的进程内存空间都适合共享,只有那些**只读的区域才可以共享
**
可重入代码又称纯代码
,是一种**允许多个进程同时访问但不允许被任何进程修改的代码
**
在实际执行时,可以为每个进程配以**局部数据区
**,把在执行中可能改变的部分复制到该数据区,这样,程序在执行时只需对该私有数据区中的内存进行修改,并不去改变共享的代码
6、内存分配与回收
在操作系统由单道向多道发展时,存储管理方式便由单一连续分配发展为固定分区分配;为了能更好地适应不同大小的程序要求,又从固定分区分配发展到动态分区分配;为了更好地提高内存的利用率,进而从连续分配方式发展到离散分配方式一一**⻚式存储管理
**
覆盖与交换
注:考纲里面已经删除
覆盖与交换技术是**在多道程序环境下用来扩充内存
**的两种方法
- 覆盖
覆盖的基本思想如下:由于程序运行时并非任何时候都要访问程序及数据的各个部分,因此可**把用户空间分成一个固定区和若干覆盖区
。将经常活跃的部分放在固定区,其余部分按调用关系分段
首先将那些即将要访问的段放入覆盖区,其他段放在外存中,在需要调用前,系统再将其调入覆盖区,替换覆盖区中原有的段
**
特点:打破了必须将一个进程的全部信息装入主存后才能运行的限制,但**当同时运行程序的代码量大于主存时仍不能运行
,此外,内存中能够更新的地方只有覆盖区的段,不在覆盖区中的段会常驻内存
**
覆盖技术对用户和程序员不透明
- 交换
交换(对换)的基本思想如下:把处于等待状态
(或在CPU调度原则下被剥夺运行权利)的程序从内存移到辅存,把内存空间腾出来
,这一过程又称换出
;把准备好竞争CPU运行的程序从辅存移到内存
,这一过程又称换入
例如:有一个CPU采用时间片轮转调度算法的多道程序环境。时间片到,内存管理器将刚刚执行过的进程换出,将另一进程换入刚刚释放的内存空间。同时,CPU调度器可以将时间片分配给其他已在内存中的进程。每个进程用完时间片都与另一进程交换。在理想情况下,内存管理器的交换过程速度足够快,总有进程在内存中可以执行
有关交换,需要注意以下几个问题:
- 交换**
需要备份存储
**,通常是磁盘。它必须足够大,并提供对这些内存映像的直接访问 - 为了有效使用CPU,需要使**
每个进程的执行时间比交换时间⻓
** - 若**
换出进程
,则必须确保该进程完全处于空闲状态
** - 交换空间**
通常作为磁盘的一整块
,且独立于文件系统
**,因此使用起来可能很快 - 交换**
通常在有许多进程运行且内存空间吃紧时开始启动
,而在系统负荷降低时就暂停
** - 普通的交换使用不多,但交换策略的某些变体在许多系统(如UNIX)中仍发挥作用
交换技术主要在不同进程(或作业)之间进行,而覆盖则用于同一个程序或进程中
连续分配管理方式
连续分配方式是指**为一个用户程序分配一个连续的内存空间
。连续分配方式主要包括单一连续分配、固定分区分配和动态分区分配
**
1、单一连续分配
内存在此方式下分为**系统区和用户区
**
系统区**仅供操作系统使用
,通常在低地址部分
用户区内存中,仅有一道用户程序
,即整个内存的用户空间由该程序独占
**
优点是**简单、无外部碎片,无须进行内存保护
,因为内存中永远只有一道程序
缺点是只能用于单用户、单任务的操作系统中,有内部碎片,存储器的利用率极低
**
2、固定分区分配
固定分区分配是最简单的一种**多道程序存储管理方式
,它将用户内存空间划分为若干固定大小的区域,每个分区只装入一道作业
。当有空闲分区时
,便可再从外存的后备作业队列中选择适当大小的作业装入该分区
**,如此循环
在划分分区时有两种不同的方法:
- 分区大小**
相等
。程序太小
会造成浪费
,程序太大
又无法装入
,缺乏灵活性
** - 分区大小**
不等
**。划分为多个较小的分区、适量的中等分区和少量大分区
为了便于分配,建立一张分区使用表,通常按分区大小排队,各表项包括每个分区的起始地址、大小及状态(是否已分配)。分配内存时,便检索该表,以找到一个能满足要求且尚未分配的分区分配给装入程序,并将对应表项的状态置为“已分配”;若找不到这样的分区,则拒绝分配。回收内存时,只需将对应表项的状态置为“未分配”即可
这种方式存在两个问题:
- 程序可能太大而放不进任何一个分区,这时就需要采用覆盖技术来使用内存空间
- 当程序小于固定分区大小时,也要占用一个完整的内存分区,这样**
分区内部就存在空间浪费
**,这种现象称为内部碎片
固定分区是可用于多道程序设计的最简单的存储分配,无外部碎片
,但**不能实现多进程共享一个主存区
,所以存储空间利用率低
**
3、动态分区分配
又称可变分区分配,它是在进程装入内存时,根据进程的实际需要,动态地为之分配内存,并使分区的大小正好适合进程的需要
。因此,系统中分区的大小和数目是可变的
如上所示,系统有64MB内存空间,其中低8MB固定分配给操作系统,其余为用户可用内存
开始时装入前三个进程,它们分别分配到所需的空间后,内存仅剩4MB,进程4无法装入
在某个时刻,内存中没有一个就绪进程,CPU出现空闲,操作系统就换出进程2,换入进程4,由于进程4比进程2小,这样在主存中就产生了一个6MB的内存块
之后CPU又出现空闲,要换入进程2,而主存无法容纳进程2,操作系统就换出进程1,换入进程2
动态分区在开始时是很好的,但**随着时间的推移,内存中会产生越来越多小的内存块,内存的利用率也随之下降
。这些小的内存块称为外部碎片
,它存在于所有分区的外部
**,这与固定分区中的内部碎片正好相对
克服外部碎片可以通过**紧凑技术
来解决,即操作系统不时地对进程进行移动和整理
。但这需要动态重定位寄存器的支持
,且相对费时
**
在进程装入或换入主存时,若内存中有多个足够大的空闲块,则操作系统必须确定分配哪个内存块给进程使用
,这就是动态分区的分配策略:
- 首次适应算法。空闲分区**
以地址递增的次序链接
。分配内存时,从链首开始顺序查找,找到大小能满足要求的第一个空闲分区分配给作业
**
首次适应算法最简单,通常也是最好和最快的。不过,首次适应算法**会使得内存的低地址部分出现很多小的空闲分区,而每次分配查找时都要经过这些分区,因此增加了开销
** - 邻近适应算法。又称
循环首次适应算法
,由首次适应算法演变而成。不同之处是,分配内存时从上次查找结束的位置开始继续查找
邻近适应算法试图解决这个问题。但它常常导致**在内存空间的尾部
(因为在一遍扫描中,内存前面部分使用后再释放时,不会参与分配)分裂成小碎片
**。通常比首次适应算法要差 - 最佳适应算法。空闲分区**
按容量递增的次序形成空闲分区链
,找到第一个能满足要求且最小的空闲分区分配给作业
,避免“大材小用”
最佳适应算法虽然称为“最佳”,但是性能通常很差
,因为每次最佳的分配会留下很小的难以利用的内存块,会产生最多的外部碎片
** - 最坏适应算法。空闲分区**
以容量递减的次序链接
,找到第一个能满足要求的,即最大的分区,从中分割一部分存储空间给作业
**
量坏适应算法与量佳适应算法相反,它选择最大的可用块,这看起来最不容易产生碎片,但是却**把最大的连续内存划分开,会很快导致没有可用的大内存块,因此性能也非常差
**
在动态分区分配中,与固定分区分配类似,设置一张空闲分区链(表),并按始址排序
分配内存时,检索空闲分区链,找到所需的分区,若其大小大于请求大小,便从该分区中按请求大小分割一块空间分配给装入进程(若剩余部分小到不足以划分,则无须分割),余下部分仍留在空闲分区链中
回收内存时,系统根据回收分区的始址,从空闲分区链中找到相应的插入点,此时可能出现四种情况:
- 回收区与**
插入点的前
一空闲分区相邻,将这两个分区合并
,并修改前一分区表项的大小为两者之和
** - 回收区与**
插入点的后
一空闲分区相邻,将这两个分区合并
,并修改后一分区表项的始址和大小
** - 回收区同时与**
插入点的前、后
两个分区相邻,此时将这三个分区合并
,修改前一分区表项的大小为三者之和,取消后一
** - 回收区**
没有相邻
的空闲分区,此时应为回收区新建一个表项,填写始址和大小,并插入空闲分区链
**
以上三种内存分区管理方法有一个共同特点,即**用户程序在主存中都是连续存放的
**
连续分配方式中,即使内存有超过1GB的空闲空间,但若没有连续的1GB空间,则需要1GB空间的作业仍然是无法运行的
若采用非连续分配方式,则作业所要求的1GB内存空间可以分散地分配在内存的各个区域,这也需要额外的空间去存储它们(分散区域)的索引,使得非连续分配方式的存储密度低于连续分配方式
非连续分配方式**根据分区的大小是否固定
,分为分⻚存储管理和分段存储管理
**
在分⻚存储管理中,又**根据运行作业时是否要把作业的所有⻚面都装入内存才能运行
,分为基本分⻚存储管理和请求分⻚存储管理
**
基本分页存储管理
固定分区会产生内部碎片,动态分区会产生外部碎片
,这两种技术**对内存的利用率都比较低
**
分⻚的思想:把主存空间划分为**大小相等且固定的块,块相对较小,作主存的基本单位
。每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间
**
分⻚的方法从形式上看,像分区相等的固定分区技术,分⻚管理不会产生外部碎片
。但它又有本质的不同点:块的大小相对分区要小很多,而且进程也接照块进行划分,进程运行时按块申请主存可用空间并执行
。这样,进程**只会在为最后一个不完整的块申请一个主存块空间时,才产生主存碎片
,所以尽管会产生内部碎片,但这种碎片相对于进程来说是很小的
**,每个进程平均只产生半个块大小的内部碎片
(也称⻚内碎片
)
1、分页存储的几个基本概念
- 页面和页面大小
进程中的块称为页
或页面
,内存中的块称为⻚框
或页帧
,外存也以同样的单位进行划分,直接称为块
或盘块
进程在执行时需要申请主存空间,即要**为每个⻚面分配主存中的可用⻚框
,这就产生了⻚和⻚框的一一对应
**
为方便地址转换,⻚面大小应是2的整数幂
。同时⻚面大小应该适中,⻚面**太小
会使进程的⻚面数过多
,这样⻚表就会过⻓,占用大量内存
,而且也会增加硬件地址转换的开销,降低⻚面换入/换出的效率
;⻚面过大
又会使⻚内碎片增多,降低内存的利用率
**
- 地址结构
分⻚存储管理的逻辑地址结构如下所示:
地址结构包含两部分:前一部分为⻚号P
,后一部分为⻚内偏移量W
地址⻓度为32位,其中0 ~ 11位为⻚内地址,即每⻚大小为4KB;12 ~ 31位为⻚号,即量多允许 220 ⻚
注意:地址结构决定了虚拟内存的寻址空间有多大
- ⻚表
为了**便于在内存中找到进程的每个⻚面所对应的物理块
**,系统为每个进程建立一张⻚表,它记录⻚面在内存中对应的物理块号,⻚表一般存放在内存中
在配置⻚表后,进程执行时,通过查找该表,即可找到每⻚在内存中的物理块号
。可⻅,⻚表的作用是**实现从⻚号到物理块号的地址映射
**
⻚表是由⻚表项组成的
,下面介绍一下页表项与地址结构的异同:
⻚表项与地址都由两部分构成。而且第一部分都是⻚号
但⻚表项的第二部分是物理内存中的块号,而地址的第二部分是⻚内偏移
⻚表项的第二部分与地址的第二部分共同组成物理地址
2、基本地址变換机构地址
变换机构的任务是**将逻辑地址转换为内存中的物理地址
。地址变换是借助于⻚表实现的
**
在系统中通常设置一个**⻚表寄存器(PTR)
**,存放⻚表在内存的起始地址F
和⻚表⻓度M
进程**未执行时,⻚表的始址和⻚表⻓度存放在本进程的PCB中
,当进程被调度执行时
,才将⻚表始址和⻚表⻓度装入⻚表寄存器中
**。设⻚面大小为L
,逻辑地址A
到物理地址E
的变换过程如下:
- 计算
⻚号P
(P = A / L
)和⻚内偏移量W
(W = A % L
) - 比较
⻚号P
和⻚表⻓度M
,若**P ≥ M
,则产生越界中断
,否则继续执行
** - ⻚表中**
⻚号P对应的⻚表项地址 = ⻚表始址F + ⻚号P x ⻚表项⻓度
,取出该⻚表项内容b
,即为物理块号
**
注意区分⻚表⻓度和⻚表项⻓度。⻚表⻓度
:一共有多少⻚;⻚表项⻓度
:⻚地址占多大的存储空间 - 计算**
E = b x L + W
**,用得到的物理地址E去访问内存
例如,若⻚面大小L
为1KB
,⻚号2
对应的物理块为b = 8
,计算逻辑地址A = 2500
的物理地址E
的过程如下:
页号P = 2500 / 1K = 2
;页内偏移量W = 2500 % 1K = 452
,(注意1K = 1024B
)
查找得到⻚号2对应的物理块的块号为8
,物理地址E = 8 × 1024 + 452 = 8644
⻚式管理只需给出一个整数就能确定对应的物理地址,因为⻚面大小是固定的
。因此,⻚式管理中地址空间是一维的
⻚表项的大小不是随意规定的
,而是有所约束的
⻚表项的作用是**找到该⻚在内存中的位置
**
确定页表项的大小。以32位逻辑地址空间
、字节编址单位
、一⻚4KB
为例:
地址空间内一共有232B / 4KB = 1M⻚,因此需要log21M = 20位才能保证表示范围能容纳所有⻚面,又因为以字节作为编址单位,即⻚表项的大小 ≥「20| = 3B
所以在这个条件下,为了保证⻚表项能够指向所有⻚面,⻚表项的大小应该大于或等于3B
,当然,也可选择更大的⻚表项让一个⻚面能够正好容下整数个⻚表项,进而方便存储(如取成4B,这样一⻚正好可以装下1K个⻚表项),或增加一些其他信息
分⻚管理方式存在的两个主要问题:
- 每次访存操作都需要进行逻辑地址到物理地址的转换,
地址转换过程必须足够快,否则访存速度会降低
- 每个进程引入⻚表,用于存储映射机制,
⻚表不能太大,否则内存利用率会降低
3、具有快表的地址变换机构
若**⻚表全部放在内存中
,则存取一个数据或一条指令至少要访问两次内存
:第一次是访问⻚表,确定所存取的数据或指令的物理地址
;第二次是根据该地址存取数据或指令
**。显然,这种方法比通常执行指令的速度慢了一半
为此,在地址变换机构中增设一个具有并行查找能力的高速缓冲存储器
——快表
,又称相联存储器(TLB)
,用来**存放当前访问的若干⻚表项,以加速地址变换的过程
**。与此对应,主存中的⻚表常称为慢表
在具有快表的分⻚机制中,地址的变换过程如下:
- CPU给出逻辑地址后,由硬件进行地址转换,
将⻚号送入高速缓存寄存器,并将此⻚号与快表中的所有⻚号进行比较
- 若**
找到匹配的⻚号
,说明所要访问的⻚表项在快表中
,则直接从中取出该⻚对应的⻚框号,与⻚内偏移量拼接形成物理地址
**。这样,存取数据仅一次访存便可实现 - 若**
未找到匹配的⻚号
,则需要访问主存中的⻚表,读出⻚表项后,应同时将其存入快表,以便后面可能的再次访问
。若快表已满,则须按特定的算法淘汰一个旧⻚表项
**
注:有些处理机设计为快表和慢表同时查找,若在快表中查找成功則终止慢表的查找
快表的有效性基于著名的局部性原理
4、两级⻚表
由于引入了分⻚管理,进程在执行时不需要将所有⻚调入内存⻚框,而**只需将保存有映射关系的⻚表调入内存
**。但仍然需要考虑⻚表的大小
以32位逻辑地址空间
、⻚面大小4KB
、⻚表项大小4B
为例:
若要实现进程对全部逻辑地址空间的映射,则每个进程需要 220 即约100万个⻚表项。也就是说,每个进程仅⻚表这一项就需要4MB主存空间
,而且还要求是连续
的,显然这是不切实际的。即便不考虑对全部逻辑地址空间进行映射的情况,一个逻辑地址空间稍大的进程,其⻚表大小也可能是过大的
以一个40MB
的进程为例,⻚表项共40KB
(40MB / 4KB × 4B
)。若将所有⻚表项内容保存在内存中,则需要10个内存⻚框
来保存整个⻚表。整个进程大小约为1万个⻚面,而实际执行时只需要几十个⻚面进入内存⻚框就可运行,但若要求10个⻚面大小的⻚表必须全部进入内存,则相对实际执行时的几十个进程⻚面的大小来说,肯定降低了内存利用率;从另一方面来说,这10⻚的⻚表项也并不需要同时保存在内存中,因为在大多数情况下,映射所要的⻚表项都在⻚表的同一个⻚面中
为了压缩页表,进一步延伸⻚表映射的思想,就可得到二级分⻚,即使用层次结构的⻚表
:将⻚表的10⻚空间也进行地址映射,建立上一级⻚表,用于存储页表的映射关系
对⻚表的10个⻚面进行映射只需要10个⻚表项,所以上一级⻚表只需要1⻚就已足够(可以存储 210 = 1024个⻚表项)。在进程执行时,只需要将这一⻚的上一级⻚表调入内存即可,进程的⻚表和进程本身的贡面可在后面的执行中再调入内存
根据上面提到的条件(32位逻辑地址空间、⻚面大小4KB、⻚表项大小4B,以字节为编址单位),页内偏移地址为log24KB = 12位
不把⻚表放在连续的空间里,则需要一张索引表来告诉我们第几张⻚表该上哪里去找,这能解决⻚表的查询问题,且不用把所有的⻚表都调入内存,只在需要它时才调入,因此能解决占用内存空间过大的问题
为查询方便,顶级⻚表最多只能有1个⻚面
,因此**顶级⻚表总共可以容纳4KB / 4B = 1K个⻚表项
**,它占用的地址位数为log~2~1K = 10位
,由于⻚内偏移地址占用了12位,因此一个32位的逻辑地址空间就剩下了10位,正好使得二级⻚表的大小在一⻚之内,这样就得到了逻辑地址空间的格式
二级⻚表实际上是**在原有⻚表结构上再加上一层⻚表
**
建立多级⻚表的**目的在于建立索引,以便不用浪费主存空间去存储无用的⻚表项,也不用盲目地顺序式查找⻚表项
**
基本分段存储管理
1、分段
段式管理方式**按照用户进程中的自然段划分逻辑空间
**
例如,用户进程由主程序段、两个子程序段、栈段和数据段组成,于是可以把这个用户进程划分为5段,每段从0开始编址,并分配一段连续的地址空间(段内要求连续,段间不要求连续
,因此**整个作业的地址空间是二维的
**),其逻辑地址由段号S
与段内偏移量W
两部分组成
段号为16位
,段内偏移量为16位
,因此一个作业最多有216 = 65536段,最大段⻓为64KB
在⻚式系统中,逻辑地址的⻚号和⻚内偏移量对用户是透明的
在段式系统中,段号和段内偏移量必须由用户显式提供
,在高级程序设计语言中,这个工作由编译程序完成
2、段表
每个进程都有一张逻辑空间与内存空间映射的段表
,其中每个段表项对应进程的一段
,段表项记录该段在内存中的始址和⻓度
配置段表后,执行中的进程可**通过查找段表,找到每段所对应的内存区
。可⻅,段表用于实现从逻辑段到物理内存区的映射
**
3、地址变换机构
分段系统的地址变换过程如下所示:
为了实现进程从逻辑址到物理地址的变换功能,在系统中设置了**段表寄存器
**,用于存放段表始址F
和段表⻓度M
从逻辑地址A
到物理地址E
之间的地址变换过程如下:
- 从
逻辑地址A
中取出**前几位为段号S
,后几位为段内偏移量W
** - 比较
段号S
和段表⻓度M
,若**S ≥ M
,则产生越界中断,否则继续执行
** - 段表中**
段号S对应的段表项地址 = 段表始址F + 段号S x 段表项⻓度
,段表项的前几位为段⻓C
,段表项的后几位为始址b
。若段内偏移量 ≥ C
,则产生越界中断,否则继续执行
** - 取出段表项中该段的始址b,计算**
E = b + W
,用得到的物理地址E去访问内存
**
4、段的共享与保护
在分段系统中,段的共享是通过两个作业的段表中相应表项指向被共享的段的同一个物理副本来实现的
。当一个作业正从共享段中读取数据时,必须防止另一个作业修改此共享段中的数据
不能修改的代码称为纯代码
或可重入代码
(它**不属于临界资源
),这样的代码和不能修改的数据可以共享,而可修改的代码和数据不能共享
**
分段管理的保护方法主要有两种:一种是**存取控制保护
,另一种是地址越界保护
**
地址越界保护将段表寄存器中的段表⻓度与逻辑地址中的段号比较,若**段号大于段表⻓度,则产生越界中断
;再将段表项中的段⻓和逻辑地址中的段内偏移进行比较,若段内偏移大于段⻓,也会产生越界中断
**
⻚式管理与段式管理的区别:
分⻚管理只需要判断⻚号是否越界,⻚内偏移是不可能越界的
与⻚式管理不同,段式管理不能通过给出一个整数便确定对应的物理地址,因为每段的⻓度是不固定的,无法通过整数除法得出段号,无法通过求余得出段内偏移,所以段号和段内偏移一定要显式给出(段号,段内偏移),因此分段管理的地址空间是二维的
段⻚式管理
分⻚存储管理能有效地提高内存利用率
,而**分段存储管理能反映程序的逻辑结构并有利于段的共享和保护
**。将这两种存储管理方法结合起来,便形成了段⻚式存储管理方式
在段⻚式系统中,作业的地址空间首先被分成若干逻辑段,每段都有自己的段号,然后将每段分成若干大小固定的⻚
对内存空间的管理仍然和分⻚存储管理一样,将其**分成若干和⻚面大小相同的存储块,对内存的分配以存储块为单位
**
在段⻚式系统中,作业的逻辑地址分为三部分:段号、⻚号和⻚内偏移量
为了实现地址变换,系统为每个进程建立一张段表,每个分段有一张⻚表
。段表表项
中至少包括段号、⻚表⻓度和⻚表始址
,⻚表表项
中至少包括⻚号和块号
。此外,系统中还应有一个**段表寄存器
,指出作业的段表始址和段表⻓度
(段表寄存器和⻚表寄存器的作用都有两个,一是在段表或⻚表中寻址
,二是判断是否越界
**)
注意:在一个进程中,段表只有一个,而⻚表可能有多个
在进行地址变换时,首先通过段表查到⻚表始址,然后通过⻚表找到⻚帧号,最后形成物理地址
进行一次访问实际需要三次访问主存
,这里同样可以使用快表来加快查找速度,其关键字由段号、⻚号组成,值是对应的⻚帧号和保护码
段⻚式管理的地址空间是二维的
本节小结
为什么要进行内存管理?
在单道系统阶段,一个系统在一个时间段内只执行一个程序,内存的分配极其简单,即仅分配给当前运行的进程
引入多道程序后,进程之间共享的不仅仅是处理机,还有主存储器。然而,共享主存会形成一些特殊的挑战。若不对内存进行管理,则容易导致内存数据的混乱,以至于影响进程的并发执行
因此,为了更好地支持多道程序并发执行,必须进行内存管理
多级⻚表解决了什么问题?又会带来什么问题?
多级⻚表解决了当逻辑地址空间过大时,⻚表的⻓度会大大增加的问题
但采用多级⻚表时,一次访盘需要多次访问内存甚至磁盘,会大大增加一次访存的时间
无论是段式管理、⻚式管理还是段⻚式管理,只需要掌握下面三个关键问题:
1、逻辑地址结构
2、表项结构
3、寻址过程
提醒:
存储保护的目的是保证在各自的内存中运行,防止干扰
虚拟内存的管理需要由相关的硬件和软件支持,如请求分页页表机制、却也中断机构、地址变换机构等
段页式存储管理中,进程先划分成段,后每段划分成页,因此地址映射表是每个进程一张段表、每个分段一张页表
内存保护是内存管理的一部分,是操作系统的任务,但出于安全性和效率考虑,必须由硬件实现,因此内存保护需要由操作系统和硬件机构合作完成
空闲区合并机制:有上或有下,空闲不变;有上且有下,空闲减1
静态装入是指在编程阶段就把物理地址计算好
可重定位是指在执行时吧逻辑地址转换成物理地址,但装入后不能改变
动态重定位是指在执行时再决定装入的地址并装入,装入后有可能会换出,所以同一个模块在内存中的物理地址是可能改变的
动态重定位是指在作业运行过程中执行到一条访存指令时,再把逻辑地址转换为主存中的物理地址,实际中是通过硬件地址转换机制实现的
只有分段式不会产生内部碎片,其只产生外部碎片,只要有分页或者分区都会产生外部碎片
采用分页或分段管理后,提供给用户的物理地址空间为总空间大小减去页表或段表的长度,由于页表或段表长度不能确定,即物理空间地址也是不能确定的
分页系统是由硬件和操作系统层面实现的,因此页面只能被其二者所感知
分段是指在用户编程时,将程序的逻辑按照逻辑划分为几个逻辑段,分段是在用户编程时决定的
程序的动态链接与程序的逻辑结构相关,分段存储管理将程序按照逻辑段进行划分,因此有利于其动态链接
动态分区时系统运行过程中在作业装入时动态建立的
内存管理提高了内存利用率,但牺牲了换入换出的速度,因此目标是为了提高换入换出速度
非虚拟存储器中作业必须全部装入内存且运行过程中也一直驻留在内存中;而虚拟存储器在作业运行前,不必全部装入内存,且在运行过程中也不必一直驻留在内存中
页面大,用于管理的页表就少,但页内碎片会相对较大;页面小,用于管理的页表较多,但页内碎片会相对较小
页式存储管理方法中,所划分的页面大小必须相同,以便于系统管理
引入段式存储管理方式,主要是为了满足用户的下列要求:方便编程;分段共享;分段保护;动态链接和增大
存储管理的目的:方便用户;提高内存利用率
对主存储器的访问一字节或字为单位
重定位:把作业空间中使用的逻辑地址变为内存中的物理地址
单用户连续分配只适合于单通道程序设计
分页存储管理中,逻辑地址分配是按页为单位进行分配的;主存的分配即物理地址分配是以内存块为单位分配的
分页存储管理方式中,多个进程并发执行时,所有进程的页表大多数驻留在内存中,在系统中只设置一个页表寄存器(PTR),存放页表在内存中的始址和长度
段页式存储管理用分段方法来分配和管理用户地址空间,用分页方法来管理物理存储空间
静态重定位:在程序运行之前由装配程序完成的,必须分配其要求的全部连续内存空间。而页式是将程序离散的分成若干页
分区分配内存管理方式的主要保护措施是:界地址保护
段内位移的最大值就是最大段长
在虚拟内存管理中,地址变换机构将逻辑地址变换为物理地址,形成该逻辑地址的阶段是链接阶段
在动态分区分配算法中,最容易产生内部碎片的是最佳适应算法,其每次分配都会产生很小的内部碎片
在多级页表的分页系统中,CPU页表基址寄存器中存放了顶级页表的起始物理地址
虚拟内存管理
虚拟内存的基本概念
1、传统存储管理方式的特征
上一节所讨论的各种内存管理策略都是为了同时将多个进程保存在内存中,以便允许进行多道程序设计,它们具有两个共同的特征:
- 一次性。作业必须一次性全部装入内存后,才能开始运行
这会导致两种情况:
1、当作业很大而不能全部被装入内存时,将使该作业无法运行
2、当大量作业要求运行时,由于内存不足以容纳所有作业,只能使少数作业先运行,导致多道程序度的下降 - 驻留性。作业被装入内存后,就一直驻留在内存中,其任何部分都不会被换出,直至作业运行结束
运行中的进程会因等待I/O而被阻塞,可能处于⻓期等待状态
2、局部性原理
局部性原理表现在以下两个方面:
- 时间局部性。程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生的原因是程序中存在着大量的循环操作
- 空间局部性。一旦程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的
时间局部性通过将近来使用的指令和数据保存到高速缓存中,并使用高速缓存的层次结构实现
空间局部性通常使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现
虚拟内存技术实际上建立了“内存-外存”的两级存储器结构,利用局部性原理实现高速缓存
3、虚拟存储器的定义和特征
基于局部性原理,在程序装入时,仅须将程序当前要运行的少数⻚面或段先装入内存,而将其余部分暂留在外存,便可启动程序执行
。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。这样,系统好像为用户提供了一个比实际内存容量大得多的存储器,称为虚拟存储器
虚拟存储器有以下三个主要特征:
- 多次性。是指**
无须在作业运行时一次性地全部装入内存,而允许被分成多次调入内存运行
**,即只需将当前要运行的那部分程序和数据装入内存即可开始运行。以后每当要运行到尚未调入的那部分程序时,再将它调入。多次性是虚拟存储器最重要的特征 - 对换性。是指**
无须在作业运行时一直常驻内存,在进程运行期间,允许将那些暂不使用的程序和数据从内存调至外存的对换区(换出),待以后需要时再将它们从外存调至内存(换进)
**。正是由于对换性,才使得虚拟存储器得以正常运行 - 虚拟性。是指**
从逻辑上扩充内存的容量,使用户所看到的内存容量远大于实际的内存容量
**。这是虚拟存储器所表现出的最重要特征,也是实现虚拟存储器的量重要目标
4、虚拟内存技术的实现
虚拟内存技术允许将一个作业分多次调入内存,实现需要**建立在离散分配的内存管理方式的基础上
**
若采用连续分配方式,会使相当一部分内存空间都处于暂时或“永久”的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量
虚拟内存的实现有以下三种方式:
1、请求分⻚存储管理;2、请求分段存储管理;3、请求段⻚式存储管理
不管哪种方式,都需要有一定的硬件支持。一般需要的支持有以下几个方面:
- 一定容量的内存和外存
- ⻚表机制(或段表机制),作为主要的数据结构
- 中断机构,当用户程序要访问的部分尚未调入内存时,则产生中断
- 地址变换机构,逻辑地址到物理地址的变换
请求分⻚管理方式
请求分⻚系统建立在基本分⻚系统基础之上,为了支持虚拟存储器功能而增加了请求调⻚功能和⻚面置换功能
在请求分⻚系统中,只要求将当前需要的一部分⻚面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的⻚面不在内存中时,再通过调⻚功能将其调入,同时还可通过置换功能将暂时不用的⻚面换出到外存上,以便腾出内存空间
除了需要一定容量的内存及外存的计算机系统,还需要有⻚表机制、缺⻚中断机构和地址变换机构
1、页表机制
请求⻚表项中增加了4个字段
- 状态位P。用于**
指示该⻚是否已调入内存
**,供程序访问时参考 - 访问宇段A。用于**
记录本⻚在一段时间内被访问的次数,或记录本⻚最近已有多⻓时间未被访问
**,供置换算法换出⻚面时参考 - 修改位M。
标识该⻚在调入内存后是否被修改过
,以确定⻚面置换时是否写回外存
- 外存地址。用于**
指出该⻚在外存上的地址
**,通常是物理块号,供调入该⻚时参考
2、缺⻚中断机构
在请求分⻚系统中,每当所要访问的⻚面不在内存中时,便产生一个缺⻚中断,请求操作系统将所缺的⻚调入内存
此时应将缺⻚的进程阻塞(调⻚完成唤醒),若内存中有空闲块,则分配一个块,将要调入的⻚装入该块,并修改⻚表中的相应⻚表项,若此时内存中没有空闲块,则要淘汰某⻚(若被淘汰⻚在内存期间被修改过,则要将其写回外存)
缺⻚中断作为中断,同样要经历诸如保护.CPU.环境、分析中断原因、转入缺⻚中断处理程序、恢复CPU环境等几个步骤。但与一般的中断相比,它有以下两个明显的区别:
- 在指令执行期间而非一条指令执行完后产生和处理中断信号,属于**
内部异常
** - —条指令在执行期间,
可能产生多次缺⻚中断
3、地址变换机构
在进行地址变换时,先检索快表:
- 若**
找到要访问的⻚
,则修改⻚表项中的访问位
(写指令还需要重置修改位),然后利用⻚表项中给出的物理块号和⻚内地址形成物理地址
** - 若**
未找到该⻚的⻚表项
,则应到内存中去查找⻚表
,再对比⻚表项中的状态位P,看该⻚是否已调入内存
,若⻚面已调入,则将该⻚的⻚表写入快表,若快表已满,则需采用某种算法替换
。若⻚面未调入,则产生缺⻚中断,请求从外存把该⻚调入内存
**
⻚框分配
1、驻留集大小
操作系统必须决定给特定的进程分配几个⻚框。给一个进程分配的物理⻚框的集合就是这个进程的驻留集
需要考虑以下几点:
分配给一个进程的⻚框越少,驻留在主存中的进程就越多
,从而**可提高CPU的利用率
**- 若一个**
进程在主存中的⻚面过少
,则尽管有局部性原理,缺⻚率仍相对较高
** - 若**
分配的⻚框过多
,则由于局部性原理,对该进程的缺⻚率没有太明显的影响
**
2、内存分配策略
在请求分⻚系统中,可采取两种内存分配策略,即**固定和可变分配策略
。在进行置换时,也可采取两种策略,即全局置换和局部置换
**。这四者可以组合出三种适用的策略:
- 固定分配局部置换
为每个进程分配一定数目的物理块,在进程运行期间都不改变
所谓**局部置换
,是指如果进程在运行中发生缺⻚,则只能从分配给该进程在内存的⻚面中选出一⻚换出,然后再调入一⻚,以保证分配给该进程的内存空间不变
**
实现这种策略时,难以确定应为每个进程分配的物理块数目
:太**少
会频繁出现缺⻚中断
,太多
又会降低CPU和其他资源的利用率
** - 可变分配全局置换
先为每个进程分配一定数目的物理块,在进程运行期间可根据情况适当地增加或减少
所谓**全局置换
,是指如果进程在运行中发生缺⻚,系统从空闲物理块队列中取出一块分配给该进程,并将所缺⻚调入
**
这种方法比固定分配局部置换更加灵活,可以动态增加进程的物理块
,但也存在弊端,如它会盲目地给进程增加物理块,从而导致系统多道程序的并发能力下降 - 可变分配局部置换
为每个进程分配一定数目的物理块,当某进程发生映⻚时,只允许从该进程在内存的⻚面中选出一⻚换出,因此不会影响其他进程的运行
若进程在运行中**频繁地发生缺⻚中断
,则系统再为该进程分配若干物理块,直至该进程的缺⻚率趋于适当程度
**
若进程在运行中的**缺⻚率特别低
,则可适当减少分配给该进程的物理块
,但不能引起其缺⻚率的明显增加
这种方法在保证进程不会过多地调⻚的同时
,也保持了系统的多道程序并发能力
**。当然它需要更复杂的实现,也需要更大的开销,但对比频繁地换入/换出所浪费的计算机资源,这种牺牲是值得的
另外说明一下为什么不存在固定全局分配:
对各进程进行固定分配时页面数不变,不可能出现全局置换
3、物理块调入算法
采用**固定分配策略
**时,将系统中的空闲物理块分配给各个进程,可采用下述几种算法:
- 平均分配算法,将系统中所有可供分配的物理块平均分配给各个进程
- 按比例分配算法,根据进程的大小按比例分配物理块
- 优先权分配算法,为重要和紧迫的进程分配较多的物理块。通常采取的方法是把所有可分配的物理块分成两部分:一部分按比例分配给各个进程;一部分则根据优先权分配
4、调入⻚面的时机
为确定系统将进程运行时所缺的⻚面调入内存的时机,可采取以下两种调⻚策略:
- 预调⻚策略。根据局部性原理,一次调入若干相邻的⻚会比一次调入一⻚更高效。但若调入的一批⻚面中的大多数都未被访问,则又是低效的。因此,需要采用以预测为基础的预调⻚策略,将那些预计在不久之后便会被访问的⻚面预先调入内存
- 请求调⻚策略。进程在运行中需要访问的⻚面不在内存,便提出请求,由系统将其所需⻚面调入内存。
由这种策略调入的⻚一定会被访问
,且这种策略比较易于实现,因此目前的虚拟存储器大多采用此策略。其缺点是**每次仅调入一⻚,增加了磁盘I/O开销
**
预调入实际上就是运行前的调入,请求调⻚实际上就是运行期间调入
5、从何处调入⻚面
请求分⻚系统中的外存分为两部分:用于存放文件的文件区
和用于存放对换⻚面的对换区
对换区采用连续分配方式
,而**文件区采用离散分配方式
,因此对换区的磁盘I/O速度比文件区的更快
**
当发生缺⻚请求时,系统从何处将缺⻚调入内存就分为三种情况:
- 系统**
拥有足够的对换区空间
。可以全部从对换区调入所需⻚面,以提高调⻚速度
**。为此,在进程运行前,需将与该进程有关的文件从文件区复制到对换区 - 系统**
缺少足够的对换区空间
。凡是不会被修改的文件都直接从文件区调入
;而当换出这些⻚面时,由于它们未被修改而不必再将它们换出
**。但对于那些可能被修改的部分,在将它们换出时须调到对换区,以后要时再从对换区调入(因为读比写的速度快 UNIX方式
。与进程有关的文件都放在文件区
,因此未运行过的⻚面都应从文件区调入。曾经运行过但又被换出的⻚面,由于是放在对换区,因此在下次调入时应从对换区调入。进程请求的共享⻚面若被其他进程调入内存,则无须再从对换区调入
6、如何调入⻚面
当进程所访问的⻚面**不在内存中时(存在位为0)
,便向CPU发出缺⻚中断
,中断响应后便转入缺⻚中断处理程序
**
该程序通过查找⻚表得到该⻚的物理块
此时如果内存未满,则启动磁盘I/O,将所缺⻚调入内存,并修改⻚表
如果内存已满,则先按某种置换算法从内存中选出一⻚准备换出
如果该⻚**未被修改过(修改位为0)
**,则无须将该⻚写回磁盘
如果该⻚已被修改(修改位为1),则必须将该⻚写回磁盘,将所缺⻚调入内存,并修改⻚表中的相应表项,置其存在位为1
调入完成后,进程就可利用修改后的⻚表形成所要访问数据的内存地址
⻚面置换算法
常⻅的置换算法有以下4种:
1、最佳(OPT)置换算法
最佳置换算法选择的被淘汰⻚面是以后永不使用的⻚面,或是在最⻓时间内不再被访问的⻚面,以便保证获得最低的缺⻚率
2、先进先出(FIFO)页面置换算法
优先淘汰最早进入内存的⻚面
,即淘汰在内存中驻留时间最久的⻚面
该算法实现简单,只需把已调入内存的⻚面根据先后次序链接成队列,设置一个指针总是指向最老的⻚面
但该算法与进程实际运行时的规律不适应,因为在进程中,有的⻚面经常被访问
FIFO算法还会产生**所分配的物理块数增大而⻚故障数不减反增的异常现象
**,称为Belady异常
注:只有FIFO算法可能出现Belady异常,LRU和OPT算法永远不会出现Belady异常
3、最近最久未使用(LRU)置换算法
选择最近最⻓时间未访问过的⻚面予以淘汰
,它认为过去一段时间内未访问过的⻚面,在最近的将来可能也不会被访问
该算法为每个⻚面设置一个访问字段,用来记录⻚面自上次被访问以来所经历的时间,淘汰⻚面时选择现有⻚面中值最大的予以淘汰
LRU算法的性能较好,但需要寄存器和栈的硬件支持
注:LRU是堆栈类的算法。理论上可以证明,堆栈类算法不可能出现Belady异常。FIFO算法基于队列实现,不是堆栈类算法
4、时钟(CLOCK)置換算法
- 简单的CLOCK置换算法
为每帧设置一位访问位,当某⻚首次被装入或被访问时,其访问位被置为1
对于替换算法,将内存中的所有⻚面视为一个循环队列,并有一个替换指针与之相关联,当某一⻚被替换时,该指针被设置指向被替换⻚面的下一⻚
在选择一⻚海汰时,只需检查⻚的访问位
若为0,就选择该⻚换出;若为1,则将它置为0,暂不换出,给予该⻚第二次驻留内存的机会,再依次顺序检查下一个⻚面,当检查到队列中的最后一个⻚面时,若其访问位仍为1,则返回到队首去循环检查
由于该算法是循环地检查各个⻚面的使用情况,故称CLOCK算法。但是,因为该算法只有一位访问位,而置换时将未使用过的⻚面换出,故又称最近未用(NRU)算法
- 改进型CLOCK置换算法
改进型将一个⻚面换出时,若该⻚**已被修改过,则要将该⻚写回磁盘
,若该⻚未被修改过,则不必将它写回磁盘
**
对于修改过的⻚面,替换代价更大
在改进型CLOCK算法中,除考虑⻚面使用情况外,还增加了置换代价–修改位。在选择⻚面换出时,优先考虑既未使用过又未修改过的⻚面。由访问位A
和修改位M
可以组合成下面四种类型的⻚面:
- 1类A=0,M=0:最近未被访问且未被修改,是最佳淘汰⻚
- 2类A=0,M=1:最近未被访问,但已被修改,不是很好的淘汰⻚
- 3类A=1,M=0:最近已被访问,但未被修改,可能再被访问
- 4类A=1,M=1:最近已被访问且己被修改,可能再被访问
内存中的每⻚必定都是这四类⻚面之一,页面的淘汰优先级可以简单的认为是:(0, 0) > (0, 1) > (1, 0) > (1, 1)
改进型CLOCK算法优于简单CLOCK算法的地方在于:可减少磁盘的I/O操作次数
。但为了找到一个可置换的⻚,可能要经过几轮扫描,即实现算法本身的开销将有所增加。操作系统中的⻚面置换算法都有一个原则,即**尽可能保留访问过的⻚面,而淘汰未访问的⻚面
**。
简单的CLOCK算法只考虑⻚面是否被访问过;改进型CLOCK算法对这两类⻚面做了细分,分为修改过的⻚面和未修改的⻚面。因此,若有未使用过的⻚面,则当然优先将其中未修改过的⻚面换出。若全部⻚面都用过,还是优先将其中未修改过的⻚面换出
抖动和工作集
1、抖动
在⻚面置换过程中,一种最糟糕的情形是,刚刚换出的⻚面⻢上又要换入主存,刚刚换入的⻚面⻢上又要换出主存,这种**频繁的⻚面调度行为
**称为抖动
或颠簸
系统发生料动的根本原因是,系统中同时运行的进程太多,由此分配给每个进程的物理块太少,不能满足进程正常运行的基本要求,致使每个进程在运行时频繁地出现缺⻚,必须请求系统将所缺⻚面调入内存
。这会**使得在系统中排队等待⻚面调入/调出的进程数目增加
**
显然,对磁盘的有效访问时间也随之急剧增加,造成**每个进程的大部分时间都用于⻚面的换入/换出,而几乎不能再去做任何有效的工作,进而导致发生处理机的利用率急剧下降并趋于零的情况
**
抖动的发生与系统为进程分配物理块的多少有关
2、工作集
工作集是指**在某段时间间隔内,进程要访问的⻚面集合
,基于局部性原理,可以用最近访问过的⻚面来确定工作集
**,即在某一时刻之前的窗口中的所有物理块
工作集可以由时间和工作集窗口大小来确定
实际应用中,工作集窗口会设置得很大,即对于局部性好的程序,工作集大小一般会比工作集窗口小很多
工作集反映了进程在接下来的一段时间内很有可能会频繁访问的⻚面集合
,因此,若**分配给进程的物理块小于工作集大小
,则该进程就很有可能频繁缺页
,所以为了防止这种抖动现象,一般来说分配给进程的物理块数(即驻留集大小)要大于工作集大小
**
工作集模型的原理:让操作系统跟踪每个进程的工作集,并**为进程分配大于其工作集的物理块
**
落在工作集内的⻚面需要调入驻留集中,而落在工作集外的⻚面可从驻留集中换出
若还有空闲物理块,则可再调一个进程到内存
若所有进程的工作集之和超过了可用物理块总数,则操作系统会暂停一个进程,将其⻚面调出并将物理块分配给其他进程,防止出现抖动现象
内存映射文件
内存映射文件:将磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关系,便可以直接访问被映射的文件,而不必执行文件I/O操作,也无须对文件内容进行缓存处理
。这种特性非常**适合用来管理大尺寸文件
**
使用内存映射文件所进行的**任何实际交互都是在内存中进行的
,并且是以标准的内存地址形式来访问的
**
磁盘的周期性分⻚是由操作系统在后台隐蔽实现的,对应用程序而言是完全透明的
系统内存中的所有⻚面都由虚拟存储器负责管理,虚拟存储器以统一的方式处理所有磁盘I/O
。当进程退出或显式地解除文件映射时,所有被改动的⻚面会被写回磁盘文件
多个进程允许并发地内存映射同一文件,以便允许数据共享
。实际上,很多时候,共享内存是通过内存映射来实现的。进程可以通过共享内存来通信,而共享内存是通过映射相同文件到通信进程的虚拟地址空间实现的
。内存映射文件充当通信进程之间的共享内存区域。一个进程在共享内存上完成了写操作,此刻当另一个进程在映射到这个文件的虚拟地址空间上执行读操作时,就能立刻看到上一个进程写操作的结果
虛拟存储器性能影响因素
缺⻚率(缺⻚率高即抖动)是影响虚拟存储器性能的主要因素,且缺⻚率又受到⻚面大小、分配给进程的物理块数(取决于工作集)、⻚面置换算法以及程序的编制方法的影响
根据局部性原理,⻚面较大则缺⻚率较低,⻚面较小则缺⻚率较高
⻚面较小时,一方面**減少了内存碎片,有利于提高内存利用率
;另一方面,也会使每个进程要求较多的⻚面,导致⻚表过⻓,占用大量内存
**
⻚面较大时,虽然**可以减少⻚表⻓度,但会使⻚内碎片增大
**
分配给进程的物理块数越多,缺⻚率就越低
,但是当物理块超过某个数目时,再为进程增加一个物理块对缺⻚率的改善是不明显的。可⻅,此时已没有必要再为它分配更多的物理块,否则也只能是浪费内存空间。只要保证活跃⻚面在内存中,保持缺⻚率在一个很低的范围即可
好的⻚面置换算法可使进程在运行过程中具有较低的缺⻚率。选择LRU、CLOCK等置换算法,将未来有可能访问的⻚面尽量保留在内存中,从而提高⻚面的访问速度
写回磁盘的频率。换出已修改过的⻚面时,应当写回磁盘,如果每当一个⻚面被换出时就将它写回磁盘,那么每换出一个⻚面就需要启动一次磁盘,效率极低
为此在系统中建立一个已修改换出⻚面的链表,对每个要被换出的⻚面(已修改),可以暂不将它们写回磁盘,而将它们挂在该链表上,仅当被换出⻚面数达到给定值时,才将它们一起写回磁盘,这样就可显著减少磁盘I/O的次数,即减少已修改⻚面换出的开销
此外,如果有进程在这批数据还未写回磁盘时需要再次访问这些⻚面,就不需从外存调入,而直接从已修改换出⻚面链表上获取,这样也可以减少⻚面从磁盘读入内存的频率,减少⻚面换进的开销
编写程序的局部化程度越高,执行时的缺⻚率就越低
。如果存储采用的是按行存储,访问时就要尽量采用相同的访问方式,避免按列访问造成缺⻚率过高的现象
地址翻译(书签)
本节小结
虚拟内存(虚存)空间的大小由什么因素决定?
虚存的容量要满足以下两个条件:
1、虚存的实际容量 ≤ 内存容量和外存容量之和,这是硬件的硬性条件规定的,若虚存的实际容量超过了这个容量,则没有相应的空间来供虚存使用
2、虚存的最大容量 ≤ 计算机的地址位数能容纳的最大容量
实际虚存的容量是取条件1和2的交集,即两个条件都要满足,仅满足一个条件是不行的
虚拟内存是怎么解决问题的?会带来什么问题?
虚拟内存使用外存上的空间来扩充内存空间,通过一定的换入/换出,使得整个系统在逻辑上能够使用一个远远超出其物理内存大小的内存容量
因为虚拟内存技术调换⻚面时需要访问外存,会导致平均访存时间增加,若使用了不合适的替换算法,则会大大降低系统性能
本节学习了4种⻚面置换算法,要把它们与处理机调度算法区分开
当然,这些调度算法之间也是有联系的,它们都有一个共同点,即通过一定的准则决定资源的分配对象
在处理机调度算法中这些准则比较多,有优先级、响应比、时间片等,而在⻚面调度算法中就比较简单,即是否被用到过或近段时间内是否经常使用
在操作系统中,几乎每类资源都会有相关的调度算法
提醒:
对于顺序执行程序,缺页中断的次数等于其访问的页帧数
页面尺寸增大,存放程序需要的页帧数减少,缺页中断次数减少
缺页中断由访存指令引起,要访问页面不在内存中,进行缺页中断处理并调入所要访问的页后,访存指令应重新执行
虚拟存储技术基于程序局部性原理,局部性越好,虚拟存储系统更能发挥作用
任何页面置换算法在每种页面第一次访问时不可能在内存中,因此必然会发生缺页
虚拟存储器最大容量由计算机的地址结构决定,与主存容量和外存容量没有必然的联系
需要对所有页最近一次被访问的时间进行记录,查找时间醉酒的纪念性替换,这设计排序,对置换宣发而言开销太大
虚拟存储器系统的页表项中,决定是否会发生页故障的是合法位,其显示本页面是否在内存中,即决定了是否会发生页面故障
要注意的是只有FIFO会发生Belady异常,理论上堆栈类算法是不会产生该异常的,而FIFO算法是用队列实现的
抖动是进程的页面置换中,频繁的页面调度(缺页中断)行为,所有页面调度策略都不可能完全避免抖动
请求分页存储管理的主要特点是简介扩充了内存
在进程运行时,若其工作集页面都在主存器内,则能够使该进程有效的运行,否则会出现频繁的页面调入/调出现象
调入新页面一定修改页表项和分配页框,同时内存没有页面需要从外村读入,会发生磁盘I/O
增大内存可使每个程序得到更多的页框,能减少缺页率,进而减少换入/换出过程,可提高CPU的利用率
减少多道程序的度数可以提高CPU的利用率
虚实地址转换是指逻辑地址和物理地址的转换,增大快表容量能把更多表项装入快表,会加快虚实地址转换的平均速率;让页表常驻内存可以省去一些不在内存中的页表从磁盘上调入的过程,也能加快虚实地址转换
对进程进行固定分配时页面数不变,不可能出现全局置换,因此不存在固定分配全局置换的策略
若内存中有空闲的页框,则不一定要淘汰内存冲的页
缺页率影响缺页中断的频率,缺页率越高,平均存访时间越长
磁盘读写时间与执行缺页处理程序的CPU时间影响缺页中断的处理时间,中断事件越长,平均访存事件越长
内存访问时间影响访问页表和访问目标物理地址的时间
系统调用是由用户进程发起的,请求操作系统的服务
当内存中的空闲页框不够时,操作系统会将某些页面调出,并将要访问的页面调入,这个过程由操作系统完成,不涉及系统调用
进程调度完全由操作系统完成无法通过系统调用完成
创建新进程可以通过系统调用来完成
工作集的大小决定了分配给进程的物理块数,分配给进程的物理块数越多,缺页率就越低
进程的数量越多,对内存资源的竞争就越激烈,每个进程被分配的物理块数越少,缺页率也就越高
页缓冲队列是将被淘汰的页面缓存下来,暂时不写回磁盘,队列长度会影响页面置换的速度,但不会影响缺页率