操作系统内存管理学习总结

内存管理的基本原理和要求

内存管理的功能有:

  • 内存空间的分配与回收
  • 地址转换
  • 内存空间的扩充 (利用虚拟存储技术)
  • 存储保护

程序的装入和链接

创建进程需要将程序和数据装入内存。把用户的源程序变为可执行程序。需要进行编译、链接、装入几步。

程序的链接方式

  • 静态链接:在程序运行之前将各个模块以及他们所需要的库函数链接成一个可执行的完整程序,不再分开。
  • 装入时动态链接:在装入内存时,边装入边链接。
  • 运行时动态链接:对于某些目标模块,是在程序执行中需要该目标模块时才进行链接。这种方式便于修改和更新,便于实现对目标模块的共享。

程序的装入方式

  • 绝对装入:在编译时,知道程序将驻留在内存某个位置,则编译程序将产生绝对地址的目标代码。绝对装入方式只适用于单道程序环境。
  • 可重定位装入:

连续分配管理方式

连续分配是为用户分配一个连续的内存空间。连续分配方式主要有单一连续分配、固定分区分配和动态分区分配。

单一连续分配

内存在此方式下分为系统区和用户区,系统区仅供操作系统使用,通常在低地址部分。用户区是除系统区以外的内存空间。这种方式不需要进行内存保护,因为内存中只会有一道程序,不会因为访问越界而干扰其他程序。
这种方式的优点是简单,没有外部碎片,可以采用覆盖技术,不需要额外的技术支持。缺点是只能用于单用户、单任务的操作系统中,有内部碎片,存储器利用率极低。

固定分区分配

固定分区分配是最简单的一种多道程序管理方式,它将内存划分为若干固定大小的区域,每个区域只装入一道作业。
固定分区分配有两种划区方法

  • 分区大小相等。用于一台计算机控制多个相同对象的场合,缺乏灵活性。
  • 分区大小不等。划分为多个较小的分区、适量的中等分区以及少量的大分区。

为了便于内存分配,通常将分区按照大小排队,并建立一张分区说明表,表项包括分区始址、大小以及状态。
这种分区方式存在两个问题:一是程序可能太大而放不进任何一个分区中,这时用户不得不使用覆盖技术来使用内存空间;二是内存利用率低,当程序小与固定分区分配的大小时,也占用一个完整的分区,存在内部碎片。


动态分区分配

动态分区分配不事先划分内存,而是在进程装入内存时根据进程的大小动态的建立分区,使分区的大小正好合适进程的大小。
动态分区分配随着时间的推移会产生越来越多的外部碎片。克服外部碎片可以使用紧凑技术(操作系统不时的对进程进行移动和整理)来解决。这需要动态重定位寄存器的支持,而且相对费时。
动态分区策略

  • 首次适应算法:空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小满足要求的第一个分区。这种方法是最简单而且最好最快的,但是会使内存的低地址出现很多小的空闲分区,每次分配时经过这些分区增加了开销。
  • 最佳适应算法:分区按照容量递增方式形成分区链,找到第一个满足要求的空闲分区。最佳利用算法的性能很差,因为每次最佳的分配会留下很小的难以利用的内存块,产生最多的外部碎片。
  • 最坏适应算法(最大适应算法):空闲分区以容量递减的次序链。找到以一个满足要求的分区,即最大分区。与最佳适应算法相反,它选择最大的可用块,看起来不容易产生碎片,但是把最大的联系内存划分开,很快导致没有可用的大内存块,因此性能也很差。
  • 邻近适应算法(循环首次适应算法):由首次适应算法演变而成,不同的是,分配内存时从上次结束位置开始查找。此算法试图解决首次适应算法中内存低地址产生小空闲分区的问题,但实际上,它常常导致内存的末尾分配空间分裂成小碎片。它的效果通常不如首次适应算法。

非连续分配管理方式

非连续分配管理方式根据分区大小是否固定,分为页式存储管理方式和段式存储管理方式。

基本分页式存储管理方式

分页的方法从形式上看,像分区相等的固定分区分配,分页管理不会产生外部碎片。但是分页的块比分区小很多,进程也按照块进行划分,进程运行时按块申请主存可用空间并执行。进程只会在为最后一个块申请主存空间时会产生内部碎片,但是这种内部碎片相对于主存也是很小的,每个进程平均只产生半个块大小的页内碎片。

分页存储基本概念

  • 页面和页面大小。进程中的块称为页,内存中的块称为页框或页帧。外存也以同样的单位进行划分,称为块。进程在执行时需要申请主存空间,即为每个页面分配主存中的可用页框。页与页框一一对应,为了方便地址转换,页面大小应该为2的整数幂。页面大小太大产生内部碎片,太小进程页面数过多,页表过长占用大量内存,而且增加硬件地址转换的开销。
  • 地址结构。地址结构包含两部分,前一部分为页号,后一部分为页内偏移量。
  • 页表。为了便于在内存中找到进程中每个页面对应的页框,系统为每个进程建立一张页表,它记录页面在内存中的物理块号,页表一般存放于内存中。

基本地址变换过程

image

  • 逻辑地址除页面大小得到页号:P = A/L
  • 比较页号和页表长度是否产生越界中断
  • 计算对应的页表项地址:页表始址 + 页号*页表项长度
  • 取出页表项的物理块号
  • 物理块号*页面大小 = 页面在内存中的始址,再加上页内偏移量得到物理地址

分页管理方式的两个问题;

  • 每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,否则访存速度会降低
  • 每个进程引入页表,用于存储映射机制,页表不能太大,否则内存利用率会降低

具有快表的地址变换机构

如果页表全放在内存中,则取一个数据需要访存两次,第一次访存取出物理地址,第二次访存取出数据。为了减少访存次数,加快地址变换过程,在地址变换机构中增加一个具有并行查找能力的高速缓冲存储器——快表,又称相联存储器(TLB)来存放当前访问的若干页表项。与快表相对应,主存中的页表称为慢表。

image

在具有快表的分页机制中的地址变换过程:

  • CPU给出逻辑地址后,由硬件进行地址转换,将页号传入快表中,并将页号与快表中所有页号进行比较。
  • 如果在快表中找到匹配的页号,说明要访问的页表在快表中,直接取出该页对应的页框号,与页内地址拼接形成物理地址。这样只对主存访问了一次。
  • 若未找到,则访问主存中的页表,在读出页表项之后,将其放入快表,方便之后可能再次访问。如果快表已慢,则通过特定算法进行替换。
    有些处理机设计为快表和慢表同时查找,若查找成功则终止在慢表中的查找,具体方式看题目说明

两级页表
若进程的逻辑地址空间过大,其页表也会过大,例如一个40M的进程,页面大小4KB,页表项大小4B为例,页表项总共10K个占40KB内存,将所有页表项放入内存需要10个内存页框来保存整个页表。为了压缩页表,我们进一步延伸页表映射的思想,就可以得到二级分页。顶级页表最多只有一个页面

基本分段式存储管理方式

分页存储主要从计算机角度考虑,目的是提高内存的利用率,提升计算机的性能。分页通过硬件机制实现,对用户完全透明。分段管理方式的提出考虑了用户和程序员,以满足方便编程、信息保护和共享、动态增长及动态链接等多方面需要。

在页式存储中,逻辑地址的页号以及页内偏移量对用户是透明的,但是在段式存储中,段号以及段内偏移量需要由用户显式提供,在高级语言中,这个工作由编译程序完成。

与页式存储相同,每个进程中也有一张段表,每个段表项对应程序中的一段,段表项记录了该段在内存中的始址和长度。

段式系统地址变换过程

image

  • 从逻辑地址中取出前几位为段号,后几位为段内偏移量
  • 比较段号和段表长度是否产生越界中断
  • 段表项地址 = 段表始址 + 段号 * 段表项长度
  • 取出该段表项前几位为段长,后几位为始址。
  • 比较段内偏移量与段长判断是否越界
  • 始址加上段内偏移量为物理地址

段的共享与保护
分段系统中,段的共享是通过两个作业的段表中相应页表项指向被共享的同一个物理副本来实现的。当一个作业正在从共享段中读取数据时,必须防止另一个作业修改此共享段数据。不能修改的代码称为纯代码可重入代码(它不属于临界资源),这样的代码和不可修改的数据可以共享,而可修改的代码和数据不能共享。
分段管理的保护方法主要有两种,一种是存取控制保护,另一种是地址越界保护。地址越界保护是将段号与段表的寄存器中的段表长度进行比较,以及将段表项中的段长与段内偏移量进行比较,若越界则产生越界中断。分页管理中的地址越界保护只需要判断页号是否越界,页内偏移量不会越界。

段式管理不能通过给出一个整数就确定物理地址,因为每段的长度不固定,无法通过整数除法以及取余得出段号以及段内偏移量,所有段号和段内偏移需要显示给出(段号,段内偏移),因此分段管理的地址空间是二维的。

段页式存储管理方式

页式存储管理能有效的提高内存利用率,分段存储管理能反映程序的逻辑结构并有利于段的共享。将两种方式的优点进行结合,就形成了段页式存储管理方式。

段页式存储系统中,作业的地址空间首先被分成若干逻辑段,然后将每段分成若干大小固定的页。对内存空间的管理和分页存储管理一样,将其分成若干大小相同的存储块。对内存的分配以块为单位。

在段页式系统中,逻辑地址分为3部分:段号 | 页号 | 页内偏移量

每个进程拥有一张段表,每个分段有一张页表。段表项中至少包含段号、页表长度和页表始址,页表项中至少包含页号和页框号。每个系统中应该有一个段表寄存器,指出段表始址和段表长度。

段页式系统地址变换过程

image

  • 通过段表查到页表始址
  • 再通过页表找到页框号
  • 加上页内偏移量形成物理地址

段页式管理的地址空间是二维的

虚拟内存管理

虚拟内存的基本概念

传统存储管理方式的特征
之前的内存管理策略都是为了同时将多个进程包留在内存中,以便于进行多道程序设计。他们都具有以下两个特征:

  • 一次性:作业必须一次性全部装入内存后,才能开始运行。这样会有两个问题。一是当作业很大而不能全部装入内存时,该作业无法运行。二是当大量作业要求运行时,由于内存不能装入所有作业,只能让少数作业先运行,导致多道程序度的下降。
  • 驻留性:作业被装入内存后,就一直驻留在内存中,任何部分都不会换出,直到作业运行结束。运行过程中会因等待I/O而阻塞,可能处于长期等待状态。

可以看出,许多程序运行过程中不用或者暂时不用的程序占据了大量内存空间,而一些需要运行的作业却无法装入,浪费了内存资源。

局部性原理
快表、页高速缓存以及虚拟内存技术从广义上说,都属于高速缓存技术。这个技术所依赖的原理就是局部性原理。
局部性原理表现在两个方面:

  • 时间局部性:程序中的某条指令一旦执行,不久后可能再次执行;某数据一旦被访问,不久后可能再次被访问。产生时间局部性的经典原因是程序中存在大量的循环操作。
  • 空间局部性:一旦程序访问了某个单元,在不久后,其附近的单元也将被访问,即程序在一段时间内访问的地址,可能集中在一定的范围内。因为指令通常是顺序存放,顺序执行的。数据也通常是以向量、数组、表等形式聚集存储的。

时间局部性通过将近来使用的指令和数据保存到高速缓冲存储器中,并使用高速缓存的层次结构实现。空间局部性通常使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟存储技术实际上建立了内存——外存的两级存储器结构,利用局部性原理实现高速缓存。

虚拟存储器的定义和特性
在局部性原理的基础上,在程序装入时,一部分装入内存,其余部分留在外存,就可以启动程序执行。执行过程中,若访问的信息不在内存时,由操作系统将所需部分调入内存,然后继续执行程序。同时可以将内存中暂时不用的程序调出内存至外存,从而腾出空间存放需要调入内存的信息。这样系统好像提供了一个比内存大得多的存储器,称为虚拟存储器。
虚拟存储器的大小由计算机的地址结构决定,并不是简单的内存与外存的和。虚拟存储器主要由三个特征。

  • 多次性:无需在作业运行时一次性的装入内存,允许被多次调入内存运行。
  • 对换性:作业运行时无需一直常驻内存,允许在允许过程中进行换入换出。
  • 虚拟性:只是从逻辑上扩大内存的容量,使用户看到的内存容量远大于实际的内存容量。

虚拟存储技术的实现
虚拟存储的实现需要建立在离散分配内存管理方式的基础上。因此虚拟内存的实现有三种方式:

  • 请求分页式存储管理
  • 请求分段式存储管理
  • 请求段页式存储管理

无论哪种方式都需要一定的硬件支持,一般需要的支持有以下几个方面:

  • 页表机制(段表机制),作为主要的数据结构。
  • 中断机构,当用户程序要访问的部分尚未调入内存时,产生中断。
  • 地址变换机构,逻辑地址到物理地址的变换。

请求分页管理

请求分页系统在基本分页系统上,为了支持虚拟存储器功能增加了请求调页功能和页面置换功能。
页表机制
请求分页系统的页表机制由于不需要一次性将所有程序装入内存,所以在程序运行过程中,必然会出现访问的页面不在内存中的情况。如何发现和处理这两种情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中加入了四个字段。

image

  • 状态位P:用于指示该页是否已经调入内存,供程序访问时参考。
  • 访问字段A:用于记录本页在一段时间内被访问的次数,或者记录本页有多长时间未被访问,供页面置换算法参考。
  • 修改为M:标识该页在调入内存后是否被修改过。
  • 外存地址:该页在外存上的地址,通常是物理块号

缺页中断机构
在请求分页系统中,每当所要访问的页面不在内存时,便产生一个缺页中断,请求操作系统将所缺的页调入内存。此时应将缺页的进程阻塞(调入后唤醒)。
缺页中断作为中断,同样要经历例如包含CPU环境、分析中断原因、转入缺页中断处理程序、恢复CPU环境等几个步骤。但与一般的中断相比,它有两个明显的区别。

  • 在指令执行期间而非一条指令执行完后产生和处理中断信号,属于内部中断。
  • 一条指令执行期间,可能产生多次缺页中断。

地址变换机构

iamge

页面置换算法

当进程运行时,若其访问的页面不在内存中,就需要将其调入,但是当内存已满时,就需要从内存中调出一页程序或者数据,送入磁盘的对换区。
选择调出页面的算法就叫页面置换算法。好的页面置换算法应该有较低的页面更换频率,也就是说换出的页面以后不再访问或者以后较长时间不再访问。

最佳置换算法(OPT)
最佳置换算法是选择以后永远不再使用或者在最长时间内不会被访问的页面,以保证获得最低的缺页率。但是由于我们无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,所以该算法无法实现。

先进先出页面置换算法(FIFO)
优先淘汰最早进入内存的页面,即在内存中驻留最久的页面。该算法实现简单,只需借助队列数据结构即可。但是该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。
FIFO算法还会产生所分配的物理块数增大而缺页次数不减反增的现象,称为Belady异常。只有FIFO算法可能出现Belady异常,LRU和OPT不会出现该异常。

最近最久未使用(LRU)
选择最佳最长久时间未访问过的页面进行淘汰,它认为过去一段时间没有访问的页面,在最近将来也不会被访问。该算法为每个页面分配一个访问字段,记录页面自上次被访问以来所经历的时间,淘汰页面时选择值最大的进行淘汰。
LRU算法性能较好,但是需要寄存器和栈的硬件支持。LRU是堆栈类算法。理论上可以证明,堆栈类算法不可能出现Belady异常。FIFO是基于队列实现。

时钟置换算法(CLOCK)
LRU算法的性能接近OPT算法,但是实现起来比较困难,而且开销大。FIFO算法实现简单,但是性能差。因此提出了CLOCK时钟算法,因为该算法需要循环扫描缓冲区,像时针一样转动,所以称为CLOCK算法。
简单CLOCK算法(NRU) 给每帧关联一个附加位,称为使用位。当某页首次装入主存时,将该帧的使用位设置为1;当该页随后再被访问时,其使用位也设置为1。当某一页被替换时,指针被设置为指向缓存区的下一帧。当需要替换一个页时,操作系统扫描缓存区,查找使用位为0的帧。每当遇到一个使用位为1的帧时,操作系统将其变为0;该算法会循环检查各页面的情况,所以称为CLOCK算法,也称最近未用算法(NRU)。
CLOCK算法的性能比较接近LRU算法,通过增加使用的位数,可以使CLOCK算法更加高效。在使用位的基础上再增加一个修改位,得到改进型的CLOCK置换算法。每帧都属于以下几种情况:

  • 最近未被访问,也未被修改(u = 0, m = 0)。
  • 最近被访问,但是未被修改(u = 1, m = 0)。
  • 最近未被访问,但是被修改(u = 0, m = 1)。
  • 最近被访问,也被修改(u = 1, m = 1)。

改进型CLOCK算法执行步骤:

  • 从当前指针开始扫描缓冲区,扫描过程中对使用位不做修改。选择第一个遇到(0,0)的进行替换。
  • 若第一步失败,则重新扫描,查找(0,1)的帧,这个过程中,对于不符的帧,将使用位置为0.
  • 若上一步失败,则指针回到最初位置,且集合中所以帧使用位为0。重复第一步,并且若有必要,重复第二步,找到供替换的帧。

改进型CLOCK算法替换帧的顺序可以看出是(0,0)、(0,1)、(1,0)、(1,1)。
改进型CLOCk算法在替换时优先选择没有被修改过的帧。修改过的帧在换回时需要写回外存,会花费较多时间。

页面分配策略

驻留集大小
对分页式的虚拟内存,在进程准备执行时,不需要也不可能将一个进程的所以页都读入主存。因此必须决定给进程分配多少页框,给一个进程分配的页框的集合就是这个进程的驻留集。

  • 分配给一个进程的驻留集越小,驻留在主存中的进程就越多,从而提高处理机的时间利用效率。
  • 若一个进程被分配到的驻留集太小,尽管有局部性原理,页错误率任然会相对较高。
  • 若驻留集过大,由于局部性原理,给特定进程分配更多的主存空间对该进程没有明显影响。

基于这些因素,现代操作系统通常采用三种策略:

  • 固定分配局部置换: 它为每个进程分配一定数目的物理块,在整个运行期间都不改变。若进程发生缺页,只能从该进程中的页面中选择一块换出。实现这种策略时,难以确定应该为每个进程分配的物理块数目,太少会出现频繁的缺页中断,太多使CPU和其他资源的利用率下降。
  • 可变分配全局置换: 这是最易于实现的物理块分配和置换策略,为每个进程分配一定数目的物理块,操作系统自身保持一个空闲物理块队列。当某个进程发生缺页时,系统从空闲物理块中取出一个物理块分配给该进程,并将想要调入的页装入其中。这种方法比固定分配局部置换更加灵活,可以动态的增加物理块,但是它会盲目的给进程增加物理块,导致系统多道程序的并发能力下降。
  • 可变分配局部置换: 它为每个进程分配一定数目的物理块,当某个进程发生缺页时,只允许该进程在内存的页面中选择一页换出,不会影响其他程序的运行。若进程在运行过程中频繁出现缺页,则系统再为该进程分配若干物理块,直到该进程缺页频率达到适当程度。反之若该进程缺页率特别低,可以适当减少分配给该进程的物理块。比起可变分配全局置换,这种方法不仅可以动态增加物理块,还可以动态减少进程的物理块数量。虽然该方法需要更复杂的实现,也需要更大的开销,但是对比起频繁的换入换出浪费的资源,这种耗费是值得的。

调入页面的时机

  • 预调入策略:根据局部性原理,一次调入若干相邻的页可能会比一次调入一页更高效。但若调入的一批页面大多数未被访问,这种方法的效率又变低了。这种策略主要用于进程的首次调入,由程序员指出哪些页应该首先调入。
  • 请求调页策略:进程在运行中需要访问不在主存中的页面而题出请求,由系统将页面调入内存。这种策略调入的页一定会被访问,也易于实现,所以目前虚拟存储器大多采用此策略。它的缺点是每次只调入一页,调入调出页面时会花费过多的I/O开销。

一般情况下,两种策略会同时使用。

从何处调入页面
请求分页系统中的外存分为两部分:用于存放文件的文件区以及用于存放对换页面的对换区。通常对换区采用连续分配方式,而文件区采用离散分配方式,因此对换区的磁盘I/O速度比文件区的更快。这样,系统调入页面存在三种情况:

  • 系统拥有足够的对换区空间。可以全部从对换区调入所需页面,以提高调页速度。为此,在进程运行前,需要将与该进程相关的文件从文件区复制到对换区。
  • 系统缺少足够的对换区空间。凡是不会被修改的文件都由文件区调入;而当换出这些页面时,未被修改的页面不用换出,直接用换入页面进行覆盖即可。但是对于那些可能被修改的部分,将它们换出时必须调到对换区,以后需要时再从对换区调入。
  • UNIX方式。与进程有管的文件都放文件区,未运行的页面都应从文件区调入。曾经运行过但是又被换出的页面,由于放在对换区,下次调入时从对换区调入。进程请求的共享页面若被其他进程调入内存,则无需再从对换区调入。

抖动

页面置换过程种最糟糕的情况是刚刚换出的页面马上又要换入主存,刚刚换入主存的页面马上又要换出主存,这种频繁的页面调度称为抖动或者颠簸,若一个进程再换页上花费的时间比执行时间多,则这个进程就在抖动。
频繁发生缺页中断的主要原因是某个进程频繁访问的页面数高于可用物理页帧数。

工作集

工作集是指在某个时间间隔内访问的页面集合。基于局部性原理,可用用最近访问的页面来确定工作集。一般工作集可由时间和工作集窗口大小确定。
实际应用中,工作集窗口会设置得很大,对于局部性良好的程序,工作集大小一般会比工作集窗口小很多。工作集反应了接下来一段时间很有可能会频繁访问的页面集合,因此若分配给进程的驻留集小于工作集,该进程可能会出现频繁缺页,所以一般分配的驻留集大小会比工作集大小大。
工作集模型的原理是,让操作系统跟踪每个进程的工作集,并为进程分配大于工作集的驻留集。处于工作集中的页面需要调入驻留集中,而在工作集外的页面可用从驻留集调出。若还有空闲物理块,可以再调入一个进程到内存以增加多道程序数。若所有进程的工作集超过了可用物理块的总数,则操作系统会暂停一个进程,将其页面分配给其他进程,防止抖动现象。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值