结构
使用存储管理的原因
- 使用方便性
- 提供内存利用率
高速缓存起效的原因
- 绝不性原理
磁盘缓存位置
- 利用主存中的存储空间,来暂存从磁盘中读出(或写入)的信息
程序的装入和链接
编辑——编译——链接——转入——运行
- 编译:将用户源代码编译成若干个目标模块
- 链接:将一组目标模块和所需要的库函数链接在一起,形成以一个完整的装入模块
- 装入:由装入程序将装入模块装入内存
装入
- 绝对装入(绝对地址、物理地址)
- 装入前已产生了绝对地址(内存地址),装入时不再作地址重定位
- 绝对地址产生:编译器完成(编程时用符号地址) + 程序员编程完成
- 只能用于单道系统
- 可重定位装入(相对地址、逻辑地址)
- 装入时完成,对相对地址中的指令和数据地址的调整过程
- 因为地址变换通常是在装入时一次完成的,以后不再改变,故称为静态重定位
动态重定位:运行时
- 动态运行时装入(相对地址)
- 装入内存时没有修改地址,运行时使用重定位寄存器与相对地址相加获得绝对地址
链接
目的:将多个模块组装成可装入模块
方式
- 静态链接(完整,产生的程序更大)
- 对相对地址的修改
- 变换外部调用符号
- 装入时动态链接(装入内存时,边装入边链接)
- 便于修改和更新
- 便于实现对目标模块的共享
- 运行时动态链接
- 避免装入和链接运行时并未使用的模块,节约内存,加快装入和链接速度(使用时链接)
哪些是可以支持内存模块共享
装入时和运行时动态链接
连续分配方式
单一连续分配
只能用于单用户、单任务的操作系统中
将内存分为两部分:系统区(OS使用)和用户区(其余所有供用户使用)
可能也有贮存保护,用于防止用户程序对操作系统破坏,但是现在比较少
固定分区分配(没有外零头)
内存划分为若干个固定大小的区域,每个分区只装入一道作业。存在分区空闲时,从作业队列中选择合适大小的作业装入分区。装入时不分割
划分方式
- 相等:缺乏灵活性,常用于控制多个相同对象的场合
- 不相等:内存利用率更高
内存分配
- 排序方式: 通常时按照分区大小排序,建立一张分区使用表
- 查询方式: 按分区号顺序检索分区表,找到一个大小满足尚未分配的分区
- 缺点:存在大量存储空间浪费
动态分区分配
数据结构:
- 空闲分区表: 需要占用额外的存储空间,且占用量较大
- 空闲分区链表: 每个分区的起始部分设置控制信息和前后指针
分区分配算法
理解前四种算法的排序方式、查询方式、优缺点
- 首次适应算法(FF)
- 排序方式:空闲分区链以首地址递增
- 查询方式:从链首开始顺序查找,直到找到一个满足大小的空闲地址
- 优点:有利于大进程分配空间
- 缺点:低地址部分不断被划分,会留下很多内零头,增加查找空闲分区的开销
如果每次回收时都回收了结束进程相邻最大范围的零头并合并是不是能解决这个问题?
回收后空闲分区的插入是按照地址顺序插入还是插到链表末尾
插入合适位置
- 循环首次适应算法(NF)
- 排序方式:空闲分区链以首地址递增
- 查询方式:从上次查询到的空闲分区的下一个空闲分区开始查找
- 优点:使得空闲分区分布更加均匀;空闲分区查找的开销减少
- 缺点:会缺乏大的空闲分区
- 最佳适应算法(BF)
- 排序方式:按分区大小从小到大排序
- 查询方式:从链首开始查询到最优(因为已经按大小排序,所以最先查询到的就是相对最小的)的分区
- 优点:剩下的空闲区不至于太小,产生碎片几率最小
- 缺点:因为划分后剩余部分总是最小的,产生大量难以利用的外部碎片1
- 最坏适应算法(WF)
- 排序方式:按分区大小从大到小顺序形成空闲分区链
- 查询方式:总是挑选最大的一个分区用于划分
- 优点:产生的碎片不至于太小,产生碎片的几率最小,查询效率很高
- 缺点:缺乏大的空闲分区
- 快速适应算法(quick fit,了解即可,不考)
- 排序方式:根据容量大小分类,同时在内存中建立一张管理索引表.
- 查询方式:根据进程长度,查找可容纳的最小空闲区链表
- 优点:查找效率高
- 缺点:分区归还主存时算法复杂,系统开销较大
分区分配
分配内存流程图如下
其中
m
.
s
i
z
e
−
u
.
s
i
z
e
≤
s
i
z
e
m.size-u.size \leq size
m.size−u.size≤size防止产生过小的碎片,size一般取为最小进程大小,转换为内零头
内存回收(物理地址相邻)
- 回收区和插入点的前一个空闲分区相邻接,合并,修改空闲分区大小
- 回收区和插入点的后一个空闲分区相邻接,合并,空闲分区以回收区的起始位置为新的首址
- 回收区与出入点的前后两个空闲分区相邻接,合并,修改前一个分区的大小,取消后一个分区
- 回收区不邻接,独立一个空闲分区
伙伴系统(重要,必考)(Liunx2.4版使用的方法)
- 核心思想
- 分区大小均为 2 k ( k ∈ N , 1 ≤ k ≤ m ) 2^k(k \in N,1 \leq k \leq m) 2k(k∈N,1≤k≤m)。 2 m 2^m 2m为总内存大小。
- 初始时系统中只有一个空闲分区,大小为 2 m 2^m 2m 。
- 在运行过程中因不断划分会形成若干不连续的空闲分区。相同大小的分区组织在同一个双向链表上。因此最多有m个链表
- 分配过程
- 先计算一个 i i i值,使 2 i − 1 < n ≤ 2 i 2^{i-1} < n \leq 2^i 2i−1<n≤2i,然后在空闲分区大小为2i的分区链表中查找;
- 若找到,则把它分配给进程。 否则在大小为 2 i + 1 2^{i+1} 2i+1的链表上查找,找到则将其划分为相等大小的两个分区,一个保存到大小为 2 i 2^i 2i的分区链表,另一个用于分配给进程。 若还找不到,就再向上找,再依次等比划分
-
回收
回收 2 i 2^i 2i大小的分区,若相邻也有 2 i 2^i 2i的空闲区,合并为 2 i + 1 2^{i+1} 2i+1,若合并后还有相邻接同大小分区,再合并. -
优点
分配和回收内存速度快,且不会产生很多小碎片 -
缺点
空间利用率低,存在较大的内部零头
liunx内存管理
将所有空闲页面块分为10个组块,每块大小为
2
n
2^n
2n,0组块:1页;9组块:512页,同时用位图数组(指针数组)存储每个队列,如下图所示
哈希算法(了解即可)
按照分区链表的大小建立哈希数组
动态重定位分区分配
核心思想:紧凑()——通过作业移动将原来分散的小分区拼接成一个大分区
需要动态重定位装入方式配合
对换
把内存中暂时不能运行的进程或者暂时不用的程序和数据,调出到外存上
优点:提高内存利用率
实现
方式:
- 整体对换(进程对换):整个进程为单位
- 页面/分段换出(部分对换):以页或段为单位
对换空间管理
- 进程在对换区中驻留的时间是短暂的、对换操作又较频繁
- 外存分区
- 文件区:为提高存储空间利用率,采用离散分配方式
- 对换区: 为提高进程换入换出的速度,采用连续分配方式
- 设置空闲分区表或链表,记录对换区首址(盘块号)、大小(盘块数)
程序换出
选择依据:
- 先阻塞状态,后选择就绪状态
- 尽量选择优先级低的
- 考虑内存驻留时间长短
注意:
- 只能换出非共享的程序和数据段
- 申请对换空间–>启动磁盘将换出的内容写出–>回收内存空间,并修改进程控制块和内存分配表等数据结构
程序换入
分页管理
引入原因
为了解决动态分配分区存在的碎片过多的问题,"紧凑"技术虽然可以解决,但是时间开销太大.
离散分配
- 分段:程序地址空间划分为若干个不同大小的段,每个段可以定义一组相对完整的信息。以段为单位分配内存,段间无需相邻(分区的改进)
- 分页:程序地址空间划分为多个固定大小的页或页面,内存空间也按改大小划分为若干个物理块或页框.将程序页放入任一页框
- 段页:综合分段分页
页面大小
页面过小:内存碎片减小,但页表过长,换入换出效率降低
页面过大:页内碎片增大,页表长度小,换入换出效率高
通常为512B~8KB
空间组织
逻辑地址总大小/分页大小=总页数
log2(页面大小)=页内偏移量位数
页号位数=log2(进程拥有页数)
页表
进程可能有多个页,允许进程的每一页离散地存储在内存地任一物理块中,所以每个进程要建立一张页面映像表(页表),页表起始地址存于PCB中
页表{页号,块号(物理块,页框号)}
页表项
页表地表项中设置一个存取控制字段,保护该页中的内容
存取字段为1位:读/写
存取字段为2位:读/写,只读,只执行
若一个进程试图区写一个只读的存储块,将引起操作系统的一次中断
地址变换
实现逻辑地址到物理地址的转换
系统设置一个页表寄存器(PTR,Page-Table Register):当前运行的进程的页表在内存中的起始地址+进程的页表长度
先读出页表的起始地址,并设置页表寄存器
页号换成物理块号,页内偏移量不变
转换过程如下:
快表
分页系统:至少访问两次物理内存
类似内存的高速缓存,CDN缓存
为进程页表设置一个专用的高速缓冲存储器(快表,TLB)
保存当前进程最近访问过的一组页表项
有效的原因:局部性
淘汰:先进先出
地址转换
查找快表是否存在对应的页表项
- 有,称为命中,取出页框号
- 不存在,命中失败,再查找页表,并更新快表
内存的有效访问时间(重要)
无快表:
E
A
T
=
t
+
t
=
2
t
EAT=t+t=2t
EAT=t+t=2t
有快表:
E
A
T
=
a
∗
λ
+
(
t
+
λ
)
(
1
−
a
)
+
t
=
2
t
+
λ
−
t
∗
a
EAT=a*λ+(t+λ)(1-a)+t=2t+λ-t*a
EAT=a∗λ+(t+λ)(1−a)+t=2t+λ−t∗a
其中a为命中率,λ为查找快表的时间
一般来说,命中率达到90%以上才能真正起作用
两级和多级页表
当逻辑地址更大时,页表也会占用很大的空间
- 采用离散的方式:引入二级页表(给页表创建页表)
- 只将当前需要的页表调入内存,剩余的需要时调入
两级页表逻辑地址结构
- 外层页号要连续存放
- 外层页内地址:存放页表页的页项的地址
- 页内地址:页表内的偏移量
如何组织多级页表
反置页表
每个物理块所对应的页框号和进程号
但同样需要进程的页表,因为进程的有些页没有调入内存
页面和页框大小
- 影响页面与页框大小的主要因素:页内零头、地址转换速度和页面交换效率。
- 较小的页面有利于减少内零头,从而有利于提高内存的利用率。然而,较小的页面也将导致页表过大,从而降低处理机访问页表时的命中率(Hit Rate)。
- 块越大,内/外存之间的数据交换效率越高。因此,对于支持交换技术的系统,较大的页面有利于提高页面换进/换出内存的效率。
优缺点
- 彻底消除了外零头,仅存在很少的内零头, 提高了内存利用率。
- 分页操作由系统自动进行,一个页面不能实现某种逻辑功能。用户看到的逻辑地址是一维的,无法调试执行其中的某个子程序或子函数。
- 采用分页技术不易于实现存储共享,也不便于程序的动态链接
分段管理(非考试重点)
引入原因
- 方便编程:分为代码段和数据段等独立信息段
- 信息共享:信息段共享和保护
- 信息保护
- 动态增长:较好得解决数据段增长问题
- 动态链接:需要时调入
基本原理
先确定段号的位数(先确定可以支持多少段)
- 采用动态划分技术,物理内存动态划分为许多尺寸不相等的分区
- 当一个进程被装入物理内存时,系统将为该进程的每一段独立地分配一个分区。同一进程的多个段不必存放在连续的多个分区中。
数据结构
- 段表:包含段号、段长、段基址以及对本段的存取控制权限等信息
- 空闲分区表:
存储 - 段表被保存在物理内存中
- 段表起始地址存储在PCB中(创建进程,建立段表时)
- 进程调度执行时,PCB中的段表首址,填入段表寄存器
地址变换
存储保护:越界中断等
评价
分段有编译器完成,硬件支撑
- 有效消除了内零头,提高了存储利用率。(也无外零头)
- 允许子程序独立编译和修改,而不需要重新编译或链接其它相关子程序。
- 容易实现存储共享。
- 具有较高的安全保障。
- 很容易满足程序段的动态增长需要 。
- 通常需要编译器的支持
比较分段与分页(重要,背下来)
- 页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率。 段则是信息的逻辑单位,它含有一组意义相对完整的信息。分段的目的是为了能更好地满足用户的需要。
- 页的大小固定且由系统决定,因而在系统中只能有一种大小的页面,而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时,根据信息的性质来划分
- 分页的作业地址空间是一维的,即单一的线性地址空间,程序员只需利用一个记忆符,即可表示一个地址;而分段的作业地址空间则是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址
- 分段更有利于实现信息的共享和保护(分页需要审查每个页的具体内容,分段只需要一个号即可)
段页管理
基本原理
采取分段方式存志用户程序,采用分页分配和管理内存
实现方式:先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。
地址映像表:每个作业或进程一张段表,每个段一张页表
虚拟存储
基本概念
- 引入情况:
- 有的作业很大,作业不能全部装入内存
- 有大量作业,只能装入少数作业,而其他大量作业留在外存上等待
- 常规存储器管理方式特征
- 一次性:作业一次性装入内存
- 驻留性:作业被装入内存,一直驻留在内存中
-
根本目的:提高CPU利用率
-
虚拟存储技术是补充相对地址空间的技术
-
局部性原理:一个较短的时间段内,程序的执行仅限于某个部分;相应的,它所访问的存储空间也局限于某个区域
具体体现- 时间局限性:某条指令一旦执行,则不久后该指令可能再次被执行;数据也可能被再次访问(循环控制语句的存在导致)
- 空间局限性:访问某个存储单元,其附近的存储单元也将被被访问(典型代表:数组)
-
虚拟存储器定义:具有请求调入功能和置换功能,能从逻辑上对内存内容加以扩充的一种存储器系统
-
虚拟存储器特征:
- 多次性
- 对换性
- 虚拟性
实现的一般过程
实现方法
请求分页存储管理方式
基本原理
- 这是在分页系统的基础上,增加了请求调页功能和页面置换功能所形成的页式虚拟存储系统。
- 允许只装入部分页面的程序(及数据),便启动运行。
- 以后,再通过调页功能及页面置换功能,陆续地把即将要运行的页面调入内存,同时把暂不运行的页面换出到外存上。
- 置换时以页面为单位。
硬件支撑
页表机制
主要数据结构:请求页表
页表项内容修改
- 状态位:也称存在位,标志该页是否驻留内存。
- 访问位:记录一段时间内该页被访问的情况,如一段时间内该页被访问的次数或者多长时间未被访问。(可以推测未来是否访问,然后决定是否换出)
- 修改位:标记该页是否被修改过。注:为减少置换开销,通常选择未被修改过的页面置换。
- 外存地址:用于记录该页在外存上的存储地址
缺页中断机构
在请求分页系统中,每当所要访问的页面不在内存时,便产生缺页中断(软中断),请求OS将所缺之页调入内存。 地址转换时,检查页面的页表项中的存在位,如果为0,则产生一个缺页中断
处理过程
- (1)操作系统接收到进程产生的缺页中断信号,启动中断处理例程,保留处理机现场;
- (2)操作系统通知处理机从外存读取指定的页面;
- (3)处理机激活I/O设备;
- (4) 检查内存有无足够的空闲空间装入该页面?若有,转(6),否则,执行(5);
- (5) 利用页面置换算法,选择内存中的某个页面,换出内存;
- (6) 将指定页面从外存装入内存;
- (7) 更新该进程的页表;
- (8) 更新快表;
- (9)计算物理地址
地址变换机构
在分页系统地址变换机构额基础上,再为实现虚拟存储器而增加了某些功能而形成
内存分配策略和算法
最小物理块数的确定(指能保证程序正常运行所需的最小物理块数)
- 若是单地址指令且使用直接寻址方式,所需最小物理块数为2:
- 存放指令的页面
- 存放数据的页面
- 如果允许间接寻址:则至少要求3个物理块
分配策略
在请求分页系统
- 对于内存分配策略:固定和可变
- 进行置换策略:全局和局部置换
- 1)固定分配局部置换
- 2)可变分配全局置换(常用方式)
- 3)可变分配局部置换
局部置换可以防止抖动(频繁换入换出)问题
物理块分配算法
- 平均分配算法
- 按比例分配算法
- 考虑优先权的分配算法
调页策略
何时把一个页面装入内存
- 预调页(预计在不久之后便会访问的页面调入内存)
- 当进程创建时,预先为进程装入多个页面。
- 缺页中断时,系统为进程装入指定的页面以及与之相临的多个页面
- 请求调页(检查页表,发现相应页面不再内存,装入页面)
- 一次装入一个页面,磁盘I/O的启动频率较高,开销比较大
何处调入页面
- 存放文件的文件区
- 存放对换页面的对换区
- 有足够的对换区空间(全部有关文件从文件区拷贝到对换区)
- 缺少足够的对换区空间
- 不被修改的文件直接从文件去调入
- 换出时,未被修改的直接丢弃,再调入时,仍从文件区调入
- 可能被修改的须调到对换区,以后需要时再从对换区调入
- UNIX方式
- 进程有关的都放在文件区,从文件区调入
- 未运行过的页面,都从文件区调入
- 曾经运行过的放在对换区,再次调入从对换区调入
- 允许页面共享
页面调入过程
- 每当程序所要访问的页面未在内存时,向CPU发出缺页中断
- 中断处理程序首先保留CPU环境,分析中断原因,转入缺页中断处理程序
- 若内存已满,则先换出一页;如果已被修改,须写回磁盘
- 所缺页面调入内存,并修改页表中的相应页表项,置存在位位1,并将此页表项写入快表中
- 形成访问数据的物理地址,访问内存数据
整个页面的调入过程对用户(运行的进程,程序员,编译器)是透明的
页面置换算法
- **最佳(优)置换算法 **
- 选择之后再也不会被访问或长时间不访问的页面调出
- 理想化算法,难于实现
如上图,共有9次缺页中断(前三次产生缺页中断,但是不换出),有6次换出
- 先进先出
- 总是淘汰最先进入内存的页面
- 总是淘汰最先进入内存的页面
- 最近最久未使用(LRU)
- 选择最近最久未使用的页面予以淘汰
- 添加一个访问字段,记录上次访问以来经历的时间t
- 在选择现有页面中t最大的淘汰
- 需要寄存器或栈支持
- 寄存器:为内存中的每个页面配置一个位移寄存器,每隔一定时间位移(访问时将最高位设为1,右移),比较数值大小(最小即最久)即可[成本大,效率不高]
- 栈: 每当进程访问页面,将栈中该页面号取出,并压入栈顶,所以栈顶始终是最新的页面号,栈底就是最近最久未使用的页面号
- 在内存页面置换中使用开销太大,不适用
- Clock置换算法
- 为每页设置一位访问位,内存中的所有页面通过链接指针链接形成一个循环队列
- 当某页被访问时,其访问位被置1
- 置换程序从上次停止位置开始检查页面的访问位
- 如果为0,则换出;为1,置为0,暂不换出
- 将最近未使用的页面换出,所以又称为最近未用算法(NRU)
带星号的表示访问位为1
在换入5时,扫描了一整个循环队列,然后再扫描到2后,访问位为0,换出
- Clock置换算法的改进
- 移除时,若驻留内存期间未被修改,不必写回辅存
- 换出未修改过的页面比被修改过的页面开销小
- 首选没有被使用过,没有被修改过的页面作为置换页面
- 增加访问位A和修改位M
- A=0,M=0表示未被访问,未被修改,最好的淘汰页
- 执行过程
- 指针当前位置开始 ,先找A=0,M=0的页面,并不改变A
- 未找到,找A=0,M=1,所有扫描过的页面的访问位置为0
- 未找到,重复第一步
缺页率:
抖动与工作集
虚拟存储管理中的抖动(thrashing)现象是指页面置换(page replacement)时用于换页的时间远
多于执行程序的时间
根本原因:同时在系统中运行的进程太多,由此分配给每个进程的物理块太少,不能满足进
程正常运行的基本要求,致使每个进程频繁出现缺页
工作集:工作集(working set)是指在某段时间间隔里 Δ \Delta Δ ,进程实际所要访问页面页面的集合,可用一个二元函数 W ( t , Δ ) W(t, \Delta) W(t,Δ)表示,其中:
- t是执行时刻。
- Δ \Delta Δ是一个虚拟时间段,称为窗口大小(window size),它采用“虚拟时间”单位(即阻塞时不计时),大致可以用执行的指令数目或处理器的执行时间来计算。
工作集是在 [ t − Δ , t ] [t-\Delta, t] [t−Δ,t]时间段内所访问的页面的集合, ∣ W ( t , Δ ) ∣ |W(t, \Delta)| ∣W(t,Δ)∣指工作集大小即页面数目
工作集特征:
- 随 Δ \Delta Δ单调递增: W ( t , Δ ) W ( t , Δ + a ) W(t, \Delta) W(t, \Delta + a) W(t,Δ)W(t,Δ+a),其中a>0。
- 工作集大小范围: 1 ≤ ∣ W ( t , Δ ) ∣ ≤ m i n ( Δ , n ) 1≤|W(t, \Delta)|≤min(\Delta, n) 1≤∣W(t,Δ)∣≤min(Δ,n),其中n是进程的总页面数
预防抖动的方法:
- 采用局部置换策略
- 局部置换防止抖动扩散,
- 但仍然会因磁盘I/O队列增长而延缓其他进程的缺页中断处理。
- 作业调入时检查缺页率,避免调入过多作业
- 统计每个进程的缺页率,发现某个进程的缺页率高时增加其物理内存页数,而不调入新作业。
- 缺页率统计开销大。
- 利用L=S准则调节缺页率
- L为缺页之间的平均时间,S是平均缺页服务时间,即用于置换一个页面所需时间。
- 当L远大于S时,说明缺页率低,磁盘能力尚未得到有效利用。
- 当L远小于S时,则缺页率高,磁盘过载。
- 当L接近S时,则磁盘和处理机都可达到最大利用率。
- 选择进程暂停
- 在多道程序度偏高时,选择某些进程换出,以腾出内存空间。
- 原则可以是:优先级,进程占用内存大小。
请求分段存储管理方式
它允许只装入若干段的用户程序和数据,即可启动运行。以后再通过调段功能和段的置换功能,将暂不运行的段调出,同时调入即将运行的段。 为了实现请求分段,系统同样需要必要的硬件支持。
一般需要下列支持:
(1)请求分段的段表机制,这是在纯分段的段表机制基础上增加若干项而形成的。
(2)缺段中断机构
(3)地址变换机构。
实现请求调段和段的置换功能也须得到相应的软件支持
分段保护
- 越界检查
- 存取控制检查
- 环保护机构
外部碎片指未分配不属于任何程序的小分区;内部分区是属于特定进程但未使用的分区 ↩︎