一、内存管理概念
1. 存储管理的功能
(1) 内存空间的分配与回收 , 包括内存的分配和共享。
(2) 地址转换 : 内存管理配合硬件进行地址转换 , 把逻辑地址转换成物理地址。
(3) 内存空间的扩充 : 借助于虚拟存储器或交换覆盖技术来达到扩充内存容量的目的。
(4) 内存保护和共享 : 为了避免相互干扰和破坏 , 必须提供保护功能。
(5) 虚地址
2. 地址重定位 [程序的装入]
在多道程序设计系统中,可用的内存空间通常被多个进程共享。通常情况下,程序员并不能事先知道在某个程序执行期间会有哪些程序驻留在内存中。为了使处理器的利用率最大化,希望通过提供一个巨大的就绪进程池能够把活动进程换入或换出内存。一旦程序被换出到磁盘,当下一次被换入时,如果必须被放在和被换出前相同的内存区域,这将会是很大的限制。为了避免这种限制,需要把程序重定位到不同的区域。
(1) 逻辑地址空间: 指与当前数据在内存中的物理分配地址无关的访问地址,在执行对内存访问之前必须把它转换成物理地址,cpu生成的地址为逻辑地址。通常逻辑地址为虚拟地址,从虚拟地址到
物理地址的映射是由称为内存管理设备单元MMU的硬件来完成的。
用户进程只生成逻辑地址,且认为其地址空间为0到max。逻辑地址使用前需映射到内存的物理地址。MMU动态的将逻辑地址加上重定位寄存器的值后映射成物理地址。映射后
的物理地址再送交内存单元。
(2) 物理地址空间:数据在内存中的实际位置
(3) 地址重定位:分为 静态重定位 和 动态重定位 两类。把作业在装入过程中随即进行的地址变换方式 , 称为静态重定位。在作业执行过程中 , 当访问内存单元时才进行的地址变换方式 , 称为动
态重定位 。
动态重定位是在程序执行过程中由硬件地址变换机构实现的。
动态重定位的主要优点如下 :
用户作业在执行过程中 , 可以 动态申请存储空间和在内存中移动 ;
有利于程序段的共享 。
(4) 重定位类型
二、连续分配存储管理方式
连续分配是指为一个用户程序分配连续的内存空间。连续分配有单一连续存储管理和分区式储管理两种方式。
2.1 单一连续存储管理
在这种管理方式中,内存被分为两个区域:系统区和用户区。应用程序装入到用户区,可使用用户区全部空间。其特点是,最简单,适用于单用户、单任务的操作系统。CP/M和 DOS 2.0以下就是采用此种方式。这种方式的最大优点就是易于管理。但也存在着一些问题和不足之处,例如对要求内存空间少的程序,造成内存浪费;程序全部装入,使得很少使用的程序部分也占用—定数量的内存。
2.2 分区式存储管理
为了支持多道程序系统和分时系统,支持多个程序并发执行,引入了分区式存储管理。分区式存储管理是把内存分为一些大小相等或不等的分区,操作系统占用其中一个分区,其余的分区由应用程序使用,每个应用程序占用一个或几个分区。分区式存储管理虽然可以支持并发,但难以进行内存分区的共享。
分区式存储管理引人了两个新的问题:内碎片和外碎片。
内碎片是占用分区内未被利用的空间,外碎片是占用分区之间难以利用的空闲分区(通常是小空闲分区)。
为实现分区式存储管理,操作系统应维护的数据结构为分区表或分区链表。表中各表项一般包括每个分区的起始地址、大小及状态(是否已分配)。
分区式存储管理常采用的一项技术就是内存紧缩(compaction)。
2.2.1 固定分区(nxedpartitioning)。
固定式分区的特点是把内存划分为若干个固定大小的连续分区。分区大小可以相等:这种作法只适合于多个相同程序的并发执行(处理多个类型相同的对象)。分区大小也可以不等:有多个小分区、适量的中等分区以及少量的大分区。根据程序的大小,分配当前空闲的、适当大小的分区。
优点:易于实现,开销小。
缺点主要有两个:内碎片造成浪费;分区总数固定,限制了并发执行的程序数目。
当每个分区相等时,小于或等于分区大小的进程都可以装入任何可用的分区。如果所有分区都满了,并且没有进程处于就绪态或运行太,则操作系统可以换出一个进程额所有分区,并装入一个进程,使得处理器有事可做。
难点:
1.程序可能太大而不能放入一个分区中。
2.内存利用率非常低。
当每个分区不等时,可以缓解上面两个问题,但也有缺点:
1.分区的数目在系统生成阶段已经确定,它限制了系统中活动进程的数目
2.由于分区的大小是在系统生产阶段预先设定好的,故小作业不能有效的利用分区空间。
2.2.2动态分区(dynamic partitioning)。
动态分区的特点是动态创建分区:在装入程序时按其初始要求分配,或在其执行过程中通过系统调用进行分配或改变分区大小。与固定分区相比较其优点是:没有内碎片。但它却引入了另一种碎片——外碎片。动态分区的分区分配就是寻找某个空闲分区,其大小需大于或等于程序的要求。若是大于要求,则将该分区分割成两个分区,其中一个分区为要求的大小并标记为“占用”,而另一个分区为余下部分并标记为“空闲”。分区分配的先后次序通常是从内存低端到高端。动态分区的分区释放过程中有一个要注意的问题是,将相邻的空闲分区合并成一个大的空闲分区。
下面列出了几种常用的分区分配算法:
首次适配法(first-fit):按分区在内存的先后次序从头查找,找到符合要求的第一个分区进行分配。该算法的分配和释放的时间性能较好,较大的空闲分区可以被保留在内存高端。但随着低端分区不断划分会产生较多小分区,每次分配时查找时间开销便会增大。
下次适配法(循环首次适应算法 next fit):按分区在内存的先后次序,从上次分配的分区起查找(到最后{区时再从头开始},找到符合要求的第一个分区进行分配。该算法的分配和释放的时间性能较好,使空闲分区分布得更均匀,但较大空闲分区不易保留。
最佳适配法(best-fit):按分区在内存的先后次序从头查找,找到其大小与要求相差最小的空闲分区进行分配。从个别来看,外碎片较小;但从整体来看,会形成较多外碎片,优点是较大的空闲分区可以被保留。
最坏适配法(worst- fit):按分区在内存的先后次序从头查找,找到最大的空闲分区进行分配。基本不留下小空闲分区,不易形成外碎片。但由于较大的空闲分区不被保留,当对内存需求较大的进程需要运行时,其要求不易被满足。
2.3 伙伴系统
固定分区和动态分区方式都有不足之处。固定分区方式限制了活动进程的数目,当进程大小与空闲分区大小不匹配时,内存空间利用率很低。动态分区方式算法复杂,回收空闲分区时需要进行分区合并等,系统开销较大。伙伴系统方式是对以上两种内存方式的一种折衷方案。
伙伴系统规定,无论已分配分区或空闲分区,其大小均为 2 的 k 次幂,k 为整数, l≤k≤m,其中:
2^1 表示分配的最小分区的大小,
2^m 表示分配的最大分区的大小,
通常 2^m是整个可分配内存的大小。
假设系统的可利用空间容量为2^m个字, 则系统开始运行时, 整个内存区是一个大小为2^m的空闲分区。在系统运行过中, 由于不断的划分,可能会形成若干个不连续的空闲分区,将这些空闲分区根据分区的大小进行分类,对于每一类具有相同大小的所有空闲分区,单独设立一个空闲分区双向链表。这样,不同大小的空闲分区形成了k(0≤k≤m)个空闲分区链表。
分配步骤:
1、当需要为进程分配一个长度为n 的存储空间时:
2、首先计算一个i 值,使 2^(i-1) <n ≤ 2^i,
3、然后在空闲分区大小为2^i的空闲分区链表中查找。若找到,即把该空闲分区分配给进程。
4、否则,表明长度为2^i的空闲分区已经耗尽,则在分区大小为2^(i+1)的空闲分区链表中寻找。若存在 2^(i+1)的一个空闲分区,则把该空闲分区分为相等的两个分区,这两个分区称为一对伙伴,其中的一个分区用于分配,而把另一个加入分区大小为2^i的空闲分区链表中。
5、若大小为2^(i+1)的空闲分区也不存在,则需要查找大小为2^(i+2)的空闲分区, 若找到则对其进行两次分割:
第一次,将其分割为大小为 2^(i+1)的两个分区,一个用于分配,一个加入到大小为 2^(i+1)的空闲分区链表中;
第二次,将第一次用于分配的空闲区分割为 2^i的两个分区,一个用于分配,一个加入到大小为 2^i的空闲分区链表中。
6、若仍然找不到,则继续查找大小为 2^(i+3)的空闲分区,以此类推。
由此可见,在最坏的情况下,可能需要对 2^k的空闲分区进行 k 次分割才能得到所需分区。
与一次分配可能要进行多次分割一样,一次回收也可能要进行多次合并,如回收大小为2^i的空闲分区时,若事先已存在2^i的空闲分区时,则应将其与伙伴分区合并为大小为2^(i+1)的空闲分区,若事先已存在2^(i+1)的空闲分区时,又应继续与其伙伴分区合并为大小为2^(i+2)的空闲分区,依此类推。
在伙伴系统中,其分配和回收的时间性能取决于查找空闲分区的位置和分割、合并空闲分区所花费的时间。与前面所述的多种方法相比较,由于该算法在回收空闲分区时,需要对空闲分区进行合并,所以其时间性能比前面所述的分类搜索算法差,但比顺序搜索算法好,而其空间性能则远优于前面所述的分类搜索法,比顺序搜索法略差。 需要指出的是,在当前的操作系统中,普遍采用的是下面将要讲述的基于分页和分段机制的虚拟内存机制,该机制较伙伴算法更为合理和高效,但在多处理机系统中,伙伴系统仍不失为一种有效的内存分配和释放的方法,得到了大量的应用。
2.4 内存紧缩
内存紧缩:将各个占用分区向内存一端移动,然后将各个空闲分区合并成为一个空闲分区。
这种技术在提供了某种程度上的灵活性的同时,也存在着一些弊端,例如:对占用分区进行内存数据搬移占用CPU时间;如果对占用分区中的程序进行“浮动”,则其重定位需要硬件支持。
紧缩时机:每个分区释放后,或内存分配找不到满足条件的空闲分区时。
堆结构的存储管理的分配算法:
在动态存储过程中,不管哪个时刻,可利用空间都是一个地址连续的存储区,在编译程序中称之为"堆",每次分配都是从这个可利用空间中划出一块。其实现办法是:设立一个指針,称之为堆指针,始终指向堆的最低(或锻联)地址。当用户申请N个单位的存储块时,堆指针向高地址(或 低地址)称动N个存储单位,而移动之前的堆指针的值就是分配给用户的占用块的初始地址。例如,某个串处理系统中有A、B、C、D这4个串,其串值长度分别為12,6,10和8. 假设堆指针free的初值为零,则分配给这4个串值的存储空间的初始地址分别为0.12.18和 28,如下图所示,分配后的堆指针的值为36。 因此,这种堆结构的存储管理的分配算法非常简单。
释放内存空间执行内存紧缩:
回收用户释放的空闲块就比较麻烦。由于系统的可利用空间始终是一个绝址连续的存储块,因此回收时必须将所释放的空间块合并到整个堆上去才能重新使用,这就是"存储策缩"的任务.通常,有两种做法:
一种是一旦有用户释放存储块即进行回收紧缩,例如上图(c)的堆,在c串释放存储块时即回收紧缩,同时修改串的存储映像成图(d)的状态;
另一种是在程序执行过程中不回收用户随时释放的存储块,直到可利用空间不够分配或堆指针指向最高地址时才进行存储紧缩。此时紧缩的目的是将堆中所有的空间块连成一块,即将所有的占用块部集中到可利用空间的低地地区,而剩余的高地址区成为一整个地继连续的空闲块,如下图所示,其中(a)为紧缩前的状态,(b)为紧缩后的状态。
a 紧缩前 b紧缩后
和无用单元收集类似,为实现存储紧缩,首先要对占用块进行“标志”,标志算法和无用单元收集类同(存储块的结构可能不同),其次需进行下列4步操作:
(1)计算占用块的新地址。从最低地址开始巡査整个存储空间,对每一个占用块找到它在紧缩后的新地址。 为此,需设立两个指针随巡查向前移动,这两个指针分别指示占用 块在紧缩之前和之后的原地址和新地址。因此,在每个占用块的第一个存储单位中,除了设立长度域(存储该占用换的大小)和标志域(存储区别该存储块是占用块或空闲块的标 志)之外,还需设立一个新地址域,以存储占用块在紧缩后应有的新地址,即建立一张新、旧地址的对照表m
(2)修改用户初始变量表,以便在存储紧缩后用户程序能继续正常运行。
(3)检查每个占用块中存储的数据, 若有指向其他存储块的指针,则需作相应修改.
(4)将所有占用块迁移到新地址走,这实质上是作传送数据的工作。
至此,完成了存储紧缩的操作,最后,将堆指针赋以新值(即紧缩后的空闲存储区的最低地址)。
可见,存储紧缩法比无用单元收集法更为复杂,前者不仅要传送数据(进行占用块迁移),而且还有需要修改所有占用块中的指针值。因此,存储紧缩也是个系统操作,且非不得已就不用。
三、 覆盖和交换技术 (现在用的不多)
3.1 覆盖技术
引入覆盖 (overlay)技术的目标是在较小的可用内存中运行较大的程序。这种技术常用于多道程序系统之中,与分区式存储管理配合使用。
覆盖技术的原理:一个程序的几个代码段或数据段,按照时间先后来占用公共的内存空间。将程序必要部分(常用功能)的代码和数据常驻内存;可选部分(不常用功能)平时存放在外存(覆盖文件)中,在需要时才装入内存。不存在调用关系的模块不必同时装入到内存,从而可以相互覆盖。
在任何时候只在内存中保留所需的指令和数据;当需要其它指令时,它们会装入到刚刚不再需要的指令所占用的内存空间;
如在同一时刻,CPU只能执行B,C中某一条。B,C之间就可以做覆盖。
覆盖技术的缺点是编程时必须划分程序模块和确定程序模块之间的覆盖关系,增加编程复杂度;从外存装入覆盖文件,以时间延长换取空间节省。
覆盖的实现方式有两种:以函数库方式实现或操作系统支持。
3.2 交换技术
交换 (swapping)技术在多个程序并发执行时,可以将暂时不能执行的程序(进程)送到外存中,从而获得空闲内存空间来装入新程序(进程),或读人保存在外存中而处于就绪状态的程序。交换单位为整个进程的地址空间。交换技术常用于多道程序系统或小型分时系统中,因为这些系统大多采用分区存储管理方式。与分区式存储管理配合使用又称作“对换”或“滚进/滚出” (roll-in/roll-out)。
原理:暂停执行内存中的进程,将整个进程的地址空间保存到外存的交换区中(换出swap out),而将外存中由阻塞变为就绪的进程的地址空间读入到内存中,并将该进程送到就绪队列(换入swap in)。
交换技术优点之一是增加并发运行的程序数目,并给用户提供适当的响应时间;与覆盖技术相比交换技术另一个显著的优点是不影响程序结构。交换技术本身也存在着不足,例如:对换如和换出的控制增加处理器开销;程序整个地址空间都进行对换,没有考虑执行过程中地址访问的统计特性。
3.3 覆盖与交换比较
(1)与覆盖技术相比,交换不要求程序员给出程序段之间的覆盖结构。
(2)交换主要是在进程与作业之间进行,而覆盖则主要在同一作业或进程内进行。 另外覆盖只能覆盖那些与覆盖程序段无关的程序段。
四、页式和段式存储管理
在前面的几种存储管理方法中,为进程分配的空间是连续的,使用的地址都是物理地址。如果允许将一个进程分散到许多不连续的空间,就可以避免内存紧缩,减少碎片。基于这一思想,通过引入进程的逻辑地址,把进程地址空间与实际存储空间分离,增加存储管理的灵活性。地址空间和存储空间两个基本概念的定义如下:
地址空间:将源程序经过编译后得到的目标程序,存在于它所限定的地址范围内,这个范围称为地址空间。地址空间是逻辑地址的集合。
存储空间:指主存中一系列存储信息的物理单元的集合,这些单元的编号称为物理地址。存储空间是物理地址的集合。
根据分配时所采用的基本单位不同,可将离散分配的管理方式分为以下三种:
页式存储管理、段式存储管理和段页式存储管理。其中段页式存储管理是前两种结合的产物。
五、页式存储管理
5.1 基本原理
将程序的逻辑地址空间划分为固定大小的页(page),而物理内存划分为同样大小的页框(page frame)。程序加载时,可将任意一页放人内存中任意一个页框,这些页框不必连续,从而实现了离散分配。该方法需要CPU的硬件支持,来实现逻辑地址和物理地址之间的映射。在页式存储管理方式中地址结构由两部构成,前一部分是页号,后一部分为页内地址w(位移量),如下图所示:
页式管理方式的优点是:
1)没有外碎片,每个内碎片不超过页大比前面所讨论的几种管理方式的最大进步是,
2)一个程序不必连续存放。
3)便于改变程序占用空间的大小(主要指随着程序运行,动态生成的数据增多,所要求的地址空间相应增长)。
缺点是:要求程序全部装入内存,没有足够的内存,程序就不能执行。