现代操作系统读书笔记--第三章 内存管理

分层存储器体系、
存储管理器用于管理分层存储器体系,任务是有效地管理内存
3.1 无存储器抽象
1.最简单的存储器抽象就是根本没有抽象,每一个程序都直接访问物理内存。
2.在这种情况下,要想在内存中同时运行连个程序是不可能的
3.三种结构
4.按这种组织方式,通常一个时刻只能有一个进程在运行,一旦用户键入了一个命令,操作系统就把需要的程序从磁盘复制到内存中并执行
在不使用存储器抽象的情况下运行多个程序
解决方式:
操作系统只需要把当前内存中所有内存保存到磁盘文件中,然后把下一个程序读入到内存中再运行即可(交换概念);
在特殊硬件帮助下,不使用交换功能也能运行多个程序,如IBM 360(保护键),该方法存在重要缺陷(重定位问题):
把物理地址暴露给进程会带来下面几个严重问题:
(1)如果用户程序可以寻址内存的每个字节,它们就可以很容易地破坏操作系统
(2)想要同时运行多个程序是很困难的。
3.2 一种存储器抽象:地址空间
3.2.1 地址空间的概念
使得多个程序同时处于内存中并且互不影响的关键:
保护和重定位
解决办法:
地址空间(是一个进程可用于寻址内存的一套地址集合)
实现方法:
(1)基址寄存器与界限寄存器(以前常用)
使用动态重定位,简单地把每个进程的地址空间映射到物理内存的不同部分,使用基址寄存器(程序的起始物理地址)和界限寄存器(程序的长度)
缺点:每次访问内存都需要进行加法和比较运算,速度较慢

3.2.2 交换技术
由于前一种方法会导致内存超载,使用交换技术或虚拟内存来解决
交换技术:把一个进程完整地调入内存,使该进程运行一段时间,然后把它存回磁盘
虚拟内存:之后讨论。
内存紧缩技术:把小的空闲区合成一大块
一个问题:当进程被创建或被换入时应该为它分配多大的内存
解决方法:当换入或移动进程时为它分配一些额外的内存

3.2.3 空闲内存管理
位图和空闲区链表
1.使用位图的存储管理
内存被划分为分配单元,每个分配单元对应于位图中的一位,0表示空闲,1表示占用

2. 使用链表的存储管理
维护一个记录已分配内存段和空闲内存段的链表,每一个结点都包含以下域:空闲区(H)或进程(P)的指示标志、起始地址、长度、和指向下一节点的指针。
分配内存算法:
首次适配、下次适配、最佳适配、最差适配、快速适配

3.3 虚拟内存
由于程序大于内存,早期解决方法:覆盖
虚拟内存:每个程序拥有自己的地址空间,这个空间被分割为多个块,每一块称为一页或页面,每一页有连续的地址范围,这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序
3.3.1 分页
虚拟地址组成虚拟地址空间,内存管理单元(MMU)负责把虚拟地址映射为物理内存地址
虚拟地址空间按照固定大小划分为页面,在物理内存中对应的单元称为页框,页面和页框一般大小相同
缺页中断:当访问虚拟中没有被映射的页面时发生的系统调用,解决方法:操作系统找到一个很少使用的页框并把它写到磁盘,随后把需要访问的页面读到该页框,修改映射关系,然后重新启动引起陷阱的指令
MMU的内部结构:
3.3.2 页表
虚拟地址映射到物理地址的概括:虚拟地址被分为虚拟页号(高位)和偏移量(低位)两部分。虚拟页号用作页表的索引,找到该虚拟页面对应的页表项,由页表项可以找到页框号(如果可用),然后把页框号拼接到偏移量的高位,以替代虚拟页号,形成送往内存的物理地址。
页表的目的是把虚拟页面映射为页框
页表项的结构:
若某个页面不在内存中,用于保存该页面的磁盘地址不是页表的组成部分

3.3.3 加速分页过程
分页系统需要考虑两个主要的问题:
(1)虚拟地址到物理地址的映射必须非常快(避免映射成为一个主要瓶颈)
(2)如果虚拟地址空间很大,页表也会很大(计算机使用至少32位虚拟地址)
每个进程都需要自己的页表,因为他有自己的虚拟地址空间
加速分页问题的解决方法:
1. 转换检测缓冲区
为计算机设置一个小型的硬件设备,将虚拟地址直接映射到物理地址,而不必再访问页表,这种设备被称为 转换检测缓冲区(TLB),或称为块表,通常在MMU中  即缓存最常用的页面

2.软件TLB管理
当发生TLB访问失效时,不再是由MMU到页表中查找并取出需要的页表项,而是生成一个TLB失效并将问题交给操作系统解决
两种不同的TLB失效:
(1)软失效 (2)硬失效


3.3.4 针对大内存的页表

处理巨大虚拟地址空间问题的解决方法:
1.多级页表
32位虚拟地址被划分为10位PT1+10位PT2+12位Offset
引入的目的是避免把全部页表一直保存在内存中

2.倒排页表
实际内存中每个页框对应一个表项,而不是每个虚拟页面对应一个表项
缺点:从虚拟地址到物理地址的转换会变得很困难,使用TLB来解决,当发生TLB失效,用散列表方法来搜索倒排页表。

3.4 页面置换算法
3.4.1 最优页面置换算法(不可能实现)
算法描述:每个页面用在该页面首次被访问前所要执行的指令数作为标记,每次替换标记数目最大的页面
3.4.2 最近未使用页面置换算法
使用R位(访问位)和M位(修改位)设置一个简单的页面置换算法:当启动一个进程时,它所有的页面的俩位都由操作系统设置为0,R位被定期地清零(比如在每次时钟中断时),以区别最近没有被访问和访问的页面,当发生缺页中断,操作系统检查所有页面的这两位的值,并分为四类,NRU算法随机地从类编号最小的非空类中挑选一个页面淘汰
3.4.3 先进先出页面置换算法
FIFO:由操作系统维护一个所有当前在内存中地页面的链表。最新进入的页面放在表尾,最早的放在表头,淘汰表头的页面。
3.4.4 第二次机会页面置换算法
在FIFO基础上检查R位,为0,淘汰,为1,加入表尾
3.4.5 时钟页面置换算法
把所有页面保存在一个类似钟面的环形链表中,一个表针指向最古老的页面
3.4.6 最近最少使用页面置换算法
LRU:在缺页中断发生时,置换未使用时间最长的页面,硬件实现方法:64位计数器C
3.4.7 用软件模拟LRU
NFU(最不常用算法):将每一个页面与一个软件计数器相关联,每次时钟中断时,根据R位更新计数器,置换计数器最小的页面。
缺点:不会忘记任何事情,容易置换有用的页面
改进:(1)在R位被加进来之前先将计数器右移一位;(2)将R位加到计数器的最左端位  修改以后的算法称为 老化算法
老化算法与LRU区别:(1)LRU无法确定访问顺序 (2)老化算法的计数器只有有限位数,这就限制了其对以往页面的记录
3.4.8 工作集页面置换算法
请求调页策略:页面在被需要时调入,而不是预先
大部分进程采用局部性访问行为,即在进程运行的任何阶段,都只访问较少的一部分页面
工作集:一个进程当前正在使用的页面集合
颠簸:每执行几条指令程序就发生一次缺页中断
工作集模型(预先调页):在进程运行前就预先装入其工作集页面
大多数程序会任意访问一小部分页面,但是这个集合随时间缓慢变化
通过工作集推导出合理页面置换算法:淘汰不在工作集中的页面,关键:确定哪些页面在工作集中。确定k值
通过移位寄存器记录工作集(不可行)
代替算法:
不是向后找最近k次的内存访问,而是考虑其执行时间(更容易实现,因为每个进程只计算执行时间)
基于工作集的页面置换算法:
基本思想就是淘汰不在工作集的页面
3.4.9 工作集时钟页面置换算法
由于上面的算法每次置换都要扫描整个页表,比较费时,所以改进算法,基于时钟算法,,并且用到了工作集称为WSClock(工作集时钟算法)
3.4.10 页面置换算法小结
3.5 分页系统中的设计问题
为了使分页系统达到更好的性能
3.5.1 局部分配策略与全局分配策略
问题:怎样在互相竞争的可运行进程之间分配内存
方法:局部页面置换算法、全局页面置换算法
全局算法通常情况工作得更好,在使用全局算法时,系统必须不停确定应该给每个进程分配多少个页框
分配页框算法:
(1)监测工作集大小
(2)使用一个为进程分配页框的算法,等额或者按照进程大小分配
管理动态内存算法:PFF(缺页中断率算法)指出何时增加或减少分配给一个进程的页面
3.5.2 负载控制
一旦所有进程的组合工作集超出了内存容量,就可能发生颠簸
减少竞争内存的进程数的一个好办法是将一部分进程交换到磁盘,并释放他们所占有的所有页面
还需考虑多道程序设计的道数,因为进程数过低,cpu可能长时间处于空闲

3.5.3 页面大小
确定最佳页面大小需要在几个互相矛盾的因素之间进行权衡
选择小页面原因:
(1)大页面容易出现内部碎片,浪费最后一个页面
(2)分阶段执行每阶段可能只需要小的内存
不选择小页面的原因:
(1)需要更大的页表,页面传输(内存和磁盘之间)用时长
(2)小页面需更多TLB表项
(3)切换程序装载页面寄存器用时长
通过数学推导出最优页面大小:P=根号(2se) s:进程平均字节 e:每个页表项字节

3.5.4 分离的指令空间和数据空间
大多数计算机只有一个地址空间,既放程序也放数据
链接器必须知道如何使用分离的I空间和D空间,因为当使用他们时,数据被重定位到虚拟地址0,而不是程序之后
在使用这种设计的计算机,两种地址空间都可以进行分页

3.5.5 共享页面
程序可以共享,数据不能,共享I空间,各自维护自己的D空间

专门的数据结构记录共享页面,防止大量缺页
数据也能共享,页表指向同样的只读数据页面
如果某个进程更新了数据,触发 写时复制

3.5.6 共享库
共享库(DLL或动态链接库):被进程共享的库
链接的概念:链接器寻找未定义外部函数,找到后把他们加载成一个可执行二进制文件写到磁盘,其中包括了所需的全部函数。
由于每次链接加载库函数太麻烦,所以使用共享库。当一个程序链接共享库时,链接器没有加载被调用的函数而是加载了一小段能够在运行时绑定被调用函数的存根例程。如果其他程序已经装载了某个共享库,就没有必要再次装载它了(共享关键所在)。
共享库优点:可执行文件小、节省内存空间、如果共享库中一个函数被修改,不需要重新编译调用这个函数的程序,旧的二进制文件依旧可以执行
共享库的一个问题:重定位问题 解决方案:(1)写时复制(不行,与共享库思想相悖) (2)使用相对地址的指令的代码(位置无关代码)
3.5.7 内存映射文件
共享库是内存映射文件的一个特例:进程可以通过发起一个系统调用,将一个文件映射到虚拟空间中的一部分
内存映射文件提供了一种IO可选模型,提供了一个进程之间的高带宽通信

3.5.8 清除策略
分页守护进程:为了保证有足够的空闲页框
一种清除策略方法:双指针时钟,前指针由分页守护进程控制,增加干净页面,后指针用于页面置换

3.5.9 虚拟内存接口
允许程序员对内存映射进行控制的一个原因是为了允许共享内存,或实现高性能的信息传递系统
分布式共享内存:允许网络上的多个进程共享一个页面集合

3.6 有关实现的问题
3.6.1 与分页有关的工作
操作系统做分页工作的时间段:进程创建时、进程执行时、缺页中断时、进程终止时
3.6.2 缺页中断处理
缺页中断发生时的事件顺序:
(1)硬件陷入内核,在堆栈中保存程序计数器
(2)启动一个汇编代码例程(例程是某个系统对外提供的功能接口或服务的集合。)保存通用寄存器和其他易失信息,以免被操作系统破坏
(3)当操作系统发现一个缺页中断时,尝试发现需要哪个虚拟页面
(4)一旦知道了发生缺页中断的虚拟地址,操作系统检查这个地址是否有效,并检查存取与保护是否一致。然后找空闲页框,若没有,淘汰一个页面
(5)如果页框是脏的,安排该页写回磁盘,并发生一次上下文切换,挂起缺页中断的进程,让其他进程运行直至磁盘传输结束。
(6)一旦页框干净,操作系统查找所需页面在磁盘上的地址,通过磁盘操作将其装入
(7)当磁盘中断发生,表明该页已经被装入,页表已经更新可以反映其位置,页框也被标记为正常
(8)恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令
(9)调度引起缺页中断的进程,操作系统返回调用他的汇编语言例程
(10)该例程恢复寄存器和其他信息,返回到用户空间继续执行,就好像缺页中断从未发生。

3.6.3 指令备份
重新启动引起陷阱指令不是一件容易的事情
解决方法:通过使用一个隐藏的内部寄存器,在每条指令执行之前,把程序计数器的内容复制到该寄存器,有些机器可能有第二个寄存器,用来提供哪些寄存器已经自动增加或者自动减少以及增减的数目等信息。

3.6.4 锁定内存中的页面
虚拟内存和IO通过微妙的方式相互作用着

3.6.5 后备存储
问题:当页面被换出时会存在磁盘上的哪个位置。
一个简单的算法:在磁盘上设置特殊的交换分区,甚至从文件系统划分一块独立的磁盘。
进程启动前必须初始化交换分区,一种办法是将整个进程映像复制到交换区,一种方法是将整个进程装入内存中
另一个极端情况:事先什么也不分配,在页面换出时为其分配磁盘空间,并在换入时回收磁盘空间,这样内存中的进程就不需要任何交换空间。
不能总保证能够实现固定的交换分区

3.6.6 策略和机制的分离
通过使大多数存储管理器作为用户级进程运行,就可以把该项原则应用到存储管理器中
优点:有更多的模块化代码和更好的适应性
缺点:由于多次交叉用户-内核边界引起的额外开销,以及系统模块间信息传递所造成的额外开销

3.7 分段
多个独立地址空间比一个好
一个编译器在编译过程中会建立许多表,其中可能包括:
(1)被保存起来供打印清单用的源程序正文(用于批处理)
(2)符号表,包含变量的名字和属性
(3)包含用到的所有整型量和浮点常量的表
(4)语法分析树,包含程序语法分析的结果
(5)编译器内部过程调用使用的栈。
每个区的大小变化不一致,需要一种令程序员不用管理表扩张和收缩的办法,一个通用的办法是提供多个相互独立的段

段内寻址需要:(1)段号 (2)段内地址
段是一个逻辑实体,一般不会包含多种不同类型的内容
段的优点:(1)简化数据结构管理  (2)方便过程的链接 (3)有助于在几个进程之间共享过程和数据
3.7.1 纯分段的实现
分段和分页本质不同:页面是定长而段不是

3.7.2 分段和分页结合:MULTICS
如果一个段比较大,放入内存不方便,于是产生了对他分页的想法
MULTICS的设计者决定把每个段都看作一个虚拟内存并对他进行分页
每个MULTICS程序都有一个段表,每个段对应一个描述符,段表本身也是一个段并被分页
每个段都是一个普通的虚拟空间,采用非分段式分页存储方式进行分页
段地址组成:
进行内存访问步骤:
忽略描述符段自己也要分页的事实,实际上通过寄存器找描述符段
TLB的应用加快搜索速度
3.7.3 分段和分页结合:Intel X86
X86处理器虚拟内存核心是两张表:LDT(局部描述符表)和GDT(全局描述符表)
为了访问一个段,一个X86程序必须把这个段的选择子(用于寻找描述符)装入机器中6个寄存器之一
描述符组成:


描述符地址的(选择子,偏移量)二元组被转化为物理地址的过程

禁止分页时解释为物理地址,反之解释为虚拟地址,采用两级映射
也存在TLB减少内存访问
不再使用的原因(UNIX和windows不支持)

3.8有关内存管理的研究
3.9 小结

猜你喜欢

转载自blog.csdn.net/qq_28897525/article/details/80136885
今日推荐