介绍操作系统是如何来管理内存资源。
层次化存储体结构
计算机的存储体系
- 寄存器(register)
- 在CPU内部,非常快速,昂贵
- 高速缓存(cache)
- 非常快速,昂贵,容量小,易失性
- 主存(RAM)
- 中等速度,中等价格,易失性
- 外存
- 容量大,速度慢,种类多,不易失
操作系统的工作就是协调这些存储器的使用,管理存储器的部分程序被称为存储管理器
- 记录存储使用状况
- 分配、回收存储资源
- 数据的装入与写回
存储管理系统分类
在运行期间,进程需要在内存和磁盘之间换进换出的系统(交换和分页)和不需要换进换出的系统。
不需要换进换出的系统
- 特点:进程被调入运行后,它将始终位于内存中,直至运行结束
- 没有交换和分页的单道程序
- 固定分区的多道程序
不需要换进换出的系统实现起来是最简单的,但无法做到并发等现代操作系统的高级功能。
基本的存储管理
单道程序存储管理
同一时刻只运行一道程序,应用程序和操作系统共享存储器。
相应地,同一时刻只能有一个进程在存储器中运行。
- 一旦用户输入了一个命令,操作系统就把需要的程序从磁盘贝到存储器中并执行它;在进程运行结束后,操作系统显示出个提示符并等待新的命令。当收到新的命令时它把新的程序装入存储器,覆盖掉原来的程序。
实现方案
将操作系统和应用程序在RAM上存放位置的不同分为以下三种结构:
固定分区的多道程序系统
将内存划分为n个分区(可能不相等),分区的划分可以在系统启动时手工完成。
实现方案
-
每个分区分别有一个运行队列
- 当一个作业到达时,可以把它放到能够容纳它的最小的分区的输入队列中
- 这会造成小分区的队列是满的,而大分区的输入队列却是空的
-
各分区共享同一个输入队列
如果选择小进程先运行,则会浪费内存空间;而如果选择大进程运行,则对小进程不利。
- 一种算法是至少保留一个小分区,这样小进程就可以直接运行不与大进程竞争
- 另一种方式是制定一条规则:规定一个进程被忽略的次数不能超过k次
重定位和存储保护
多道程序引发了两个很重要的问题:
- 当一个程序被链接时,必须知道程序将在内存的什么地方运行
- 当一个程序运行时,它只能访问自己的内存空间
重定位
使用相对地址进行链接。
当一个程序被装入内存时,直接对指令代码进行修改,一次性实现文件内的相对地址到内存中绝对地址之间的转换(一般为线性)
在装入时重定位并没有解决保护问题,一个恶意的程序总可以生成一条新指令去访问任何它想访问的地址
地址保护
每个内存块分配4位的保护码,
PSW
中包含一个4位的**,若运行进程试图对保护码不同于PSW
中**的主存进行访问,则由硬件引起一个陷入-
另一种解决方式是在机器中增加两个特殊的硬件寄存器,基地址(base)和边界(limit)寄存器。
程序分区的起始地址存储在基地址寄存器中
分区的长度存储在边界寄存器中
当访问内存单元时,直接用基地址加上指令地址访问
缺点是每一次内存访问都增加了一次加法和比较操作。
交换技术(swapping)
随着程序越来越大,我们已经无法一次性把程序全部放到内存同时运行,因此产生了两种技术:交换技术(swapping)和虚拟存储器(virtual memory)
原理
把各个进程完整地调入主存,运行一段时间,再放回到磁盘上,过段时间再调入运行。
可采用固定分区和可变分区。
内存紧缩(memory compaction)
当交换在主存中生成了多个空洞时,可以把所有的进程向下移动至相互靠紧,从而把这些空洞结合成一大块。
但这样会造成CPU资源浪费。
使用可变内存策略来减少进程移动或换入换出的次数。
支持可变内存策略
如果预计大多数进程在运行时都要增长,那么可以在进程被换入或移动时分配多一点的内存,从而减小系统开销。
但如果进程需要换出磁盘,只需要交换进程实际占用的内存内容。
实现方案
基于位图的存储管理
内存被划分为可能小到几个字或大到几千字节的分配单位,每个分配单位对应于位图中的一位,0表示空闲,1表示占用(或者反过来)。
位图的大小仅仅取决于内存和分配单位的大小。
缺点:在位图中查找指定长度的连续0串是一个缓慢的操作。
基于链表的存储管理
跟踪内存使用的另一个方法是维持一个已分配和空闲的内存段的链表。
链表中的每一个表项都包含下列内容:
- 指明是空洞(H)还是进程(P)的标志
- 开始地址、长度
- 指向下一个表项的指针。
内存分配算法
首次适配算法(first fit)
- 存储管理器沿着内存段链表搜索直到找到一个足够大的空洞
下次适配(next fit)
- 每次找到合适的空洞时都记住当时的位置,在下次寻找空洞时从上次结束的地方开始搜索,而不是每次都从头开始
最佳适配算法(best fit)
- 试图找出最接近实际需要的大小的空洞,而不是把一个以后可能会用到的大空洞先使用
实际性能其实很差,因为会造成很多空洞
每次被调用时都要搜索整个链表,因此会比首次适配算法慢
最坏匹配算法(worst fit)
- 在每次分配时,总是将最大的那个空闲区切去一部分,分配给请求者
进程链表和空闲链表分离
- 这样可以加快链表的查找速度
- 但使得内存的回收变得更加复杂,速度更慢
快速匹配算法(quick fit)
- 为一些经常被用到长度的空洞设立单独的链表
- 快速适配算法寻找一个指定大小的空洞是十分迅速的,但在一个进程结束或被换出时寻找它的邻接块以查看是否可以合并是非常费时间的
虚拟存储管理
基本思想
- 操作系统把程序当前使用的那些部分保留在存储器中,而把其他部分保存在磁盘上。
- 程序的代码、数据、栈可以超过实际可用的物理内存的大小
分页技术(paging)
虚地址空间被划分成称为页面(pages)的单位,在物理存储器对应的单位称为页框(page frames),页和页框总是同样大小的。
由程序产生的地址被称为虚地址(virtual addresses),他们构成一个虚地址空间(virtual address space)
- 在使用虚拟存储器的情况下,虚地址不是被直接送到内存总上,而是送到存储管理单元(MMU),它在CPU中,其功能是把虚地址映射为物理地址
缺页故障(Page Fault)
当访问未有映射的虚拟页,会引发陷入,这个陷入称为缺页故障。
操作系统找到一个很少使用的页框并把它的内容写入磁盘,随后把需引用的页取到刚才释放的页框中,修改映射,然后重新启动引起陷入的指令。
页表(Page Table)
虚地址被分成虚页号(高位)和偏移(低位)两部分,
- 虚页号被用做页表的索引以找到该虚页对应的页表项,从页表项中可以找到页框号(如果有的话)。
- 页框号被拼接到偏移的高位端,形成送往内存的物理地址。
主要问题
- 页表可能会非常大
- 地址映射必须十分迅速
多级页表
使用多级页表来解决页表过大的问题。
因为局部性原理,程序实际访问的地址空间是很小的一部分,因此可以多次映射来将不需要的页表存储在磁盘中。
优点
- 避免将进程的所有页表项一直保存在内存中
缺点
- 需要多次访问内存,以查找页表
页表项的结构
页框号
- 物理页面号
有效位
- 这一位是1时这个表项是有效的可以被使用,如果是0,表示这个表项对应的虚页现在不在内存中,访问这一位为0的页会引起Page Fault
保护位
- 指出这个页允许什么样的访问。
- 在最简单的形式下这个域只有一位,0表示读写,1表示只读。一个更先进的安排是使用三位,各位分别指出是否允许读、写、执行这个页。
修改位和访问位跟踪页
- 在一个页被写入时硬件自动设置修改位,如果一个页已经被修改过,则必须把它写回磁盘,否则只用简单地把它丢弃就可以了
- 访问位在该页被引用时设置,被用来帮助操作系统在发生页面故障时选择淘汰的页,不再使用的页要比在使用的页更适合于被淘汰
禁止缓存位
- 这个特性对那些映射到设备寄存器而不是常规内存的页面是非常重要的。
TLB
由于访问页表(内存中)依然不够快,因此在CPU中有一小部分页表的拷贝,这部分页表是由最近访问页的页表项组成,称为TLB。
当一个虚地址被送到MMU翻译时,硬件首先把它和TLB中的所有条目同时(并行地)进行比较;如果找到了并且这个访问没有违反保护位,它的页框号将直接从TLB中取出而不用去查页表。
反置页表
物理存储器的每个页框对应一个页表项,而不是虚地址空间中的每个虚页对应一个页表项。
优点:节省大量为保存页表所需要内存空间
缺点:查找过程复杂
页面替换算法
动机
当发生缺页中断时,需从磁盘上调入相应的页面,然而内存已满,需要选取内存中的页面,将其换出,并装入新页面。
如何从众多的页面中选取被置换的页面?
- 为此提出了各种算法
- 可以应于缓存块的置换、Web缓冲区的更新等
页面替换算法
The Optimal Page Replacement Algorithm
- 选择等待时间最长的那个页面被替换。
- 无法实现,因为无法知道一个页面还要等待多久被访问。
The Not Recently Used Page Replacement Algorithm
- 统计访问位和修改位,在一个时钟周期内,选择未被访问也未被修改的页面被替换
- 在一个时钟周期结束后,访问位会被清零
FIFO Page Replacement Algorithm
不是很好的算法,最先进入的页面也有可能是经常访问的页面
The Second Chance Page Replacement Algorithm
- 对FIFO的改进,如果访问位为1,那么曾经访问过,就把其清零,然后把页面放到链表的尾端,修改装入时间。
- 如果访问位为0,则直接淘汰。
- 实际上式寻找古老而且从上一次时钟中断以来未被访问的页面
The Clock Page Replacement Algorithm
- 改进二次机会算法,使用环形链表形式,避免了链表指针移动。
Least Recently Used Algorithm
- 将内存中最久未被访问的页面淘汰
- 每次访问都会造成链表节点更新,开销较大。
- 可以使用硬件支持来加速
- 使用软件模拟(老化)
- Not Frequently Used Algorithm
参考资料
- Operating System:Design and Implementation,Third Edition