操作系统-内存管理
内存管理
一、内存的连续分配
1.1 单一连续分配方式
内存在此方式下分为系统区和用户区
系统区仅提供给操作系统使用,通常在低地址部分
用户区是为用户提供的、除系统区之外的内存空间
优点:简单、无外部碎片,可以釆用覆盖技术,不需要额外的技术支持
缺点:只能用于单用户、单任务的操作系统中,有内部碎片,存储器的利用率极低
1.2 固定分区分配
固定分区分配是最简单的一种多道程序存储管理方式。
将用户内存空间划分为若干个固定大小的区域,每个分区只装入一道作业。
当有空闲分区时,便可以再从外存的后备作业队列中,选择适当大小的作业装入该分区,如此循环。
划分分区方法:
- 分区大小相等
- 分区大小不等
存在的问题:
- 程序可能太大而放不进任何一个分区中,这时用户不得不使用覆盖技术来使用内存空间
- 主存利用率低,当程序小于固定分区大小时,也占用了一个完整的内存分区空间,产生内部碎片
1.3 动态分区分配
又称为可变分区分配,这种分区方法不预先将内存划分,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统中分区的大小和数目是可变的。
动态分区分配算法
首次适应(First Fit)算法:空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。
最佳适应(Best Fit)算法:空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区。
最坏适应(Worst Fit)算法:又称最大适应(Largest Fit)算法,空闲分区以容量递减的次序链接。找到第一个能满足要求的空闲分区,也就是挑选出最大的分区。
临近适应算法(Next fit)算法:从当前位置开始,搜索第一个能满足进程要求的内存空间
动态分区的缺点
随着时间的推移,内存中会产生越来越多的外部碎片,内存的利用率随之下降。
二、内存的非连续分配
2.1 页式存储
2.1.1 基本概念

基本思想:用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块或页帧(Page Frame),页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配:物理块不一定连续。每个页对应一个物理块。
逻辑地址结构:地址结构包含两部分:前一部分为页号P,后一部分为页内偏移量W。如果是32位地址,则011位为页内地址,即每页大小为4KB;1231位为页号,地址空间最多允许有220页
页表:因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,因此需要一个页表来记录逻辑地址和实际存储地址之间的映射关系,以实现从页号到物理块号的映射
每个进程也以块为单位进行划分。每一个进程都拥有一个自己的页表,PCB表中有指针指向页表。
分页存储管理方式中,又根据运行作业时是否要把作业的所有页面都装入内存才能运行分为基本分页存储管理方式和请求分页存储管理方式。
2.1.2 地址变换
地址变换的任务是将逻辑地址转换为内存中物理地址,地址变换是借助于页表实现的
当进程执行时,将页表始址和长度存入页表寄存器
设页大小为L(一般为4K),逻辑地址A到物理地址E的变换过程如下:
- 计算页号P(P=A/L)和页内偏移量W (W=A%L),这里的P取整
- 比较页号P和页表长度M,若P >= M,则产生越界中断,否则继续执行
- 页表中页号P对应的页表项地址 = 页表起始地址F + 页号P * 页表项长度(一般为4B),取出该页表项内容b,即为物理块号
- 计算E=b*L+W,用得到的物理地址E去访问内存
2.1.3 分页管理方式存在的问题
- 每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,否则访存速度会降低
- 每个进程引入了页表,用于存储映射机制,页表不能太大,否则内存利用率会降低
2.1.4 具有快表的地址变换机构
若页表全部放在内存中,则存取一个数据或一条指令至少要访问两次内存:
- 从内存中访问页表,从中找到指定的物理块号,加上页内偏移得到实际物理地址
- 根据第一次得到的物理地址访问内存取出数据
为提高地址变换速度,增设一个具有并行查询能力的特殊高速缓冲存储器,称为“联想存储器”或“快表”,存放当前访问的页表项。与此对应,主存中的页表也常称为慢表。
这样由逻辑地址的页号P直接在快表里查找:
- 若查找到,则得到对应的块号,直接转换成物理地址,这样,存取数据仅一次访问内存便可实现。
- 若不存在,则访问主存中的页表,在读出页表项后,应同时将其存入快表
理论依据:局部性原理
局部性原理:
时间上的局部性:最近被访问的页在不久的将来还会被访问
空间上的局部性:内存中被访问的页周围的页也很可能被访问
2.1.5 二级页表
如果内存的逻辑地址很大,将会导致程序的页表项会很多,而页表在内存中是连续存放的,所以相应的就需要较大的连续内存空间。
为了解决这个问题,可以采用两级页表或者多级页表的方法:外层页表一次性调入内存且连续存放,内层页表离散存放。
所以一共需要访问内存3次才可以读取一次数据:访问顶级页表->访问二级页表->访问内存中的数据
2.2 段式存储
分页是为了提高内存利用率,提升计算机的性能, 且分页通过硬件机制实现,对用户完全透明
而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。
2.2.1 基本概念
基本思想:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息,每个段内部从0开始编址。存储分配时,以段为单位,每个段内部连续分配内存,段与段在内存中可以不相邻接,也实现了离散分配。例如,用户进程由主程序、两个子程序、栈和一段数据组成,于是可以把这个用户进程划分为5个段。
逻辑地址结构:段号S与段内偏移量W两部分组成。如果是32位地址,段号为16位,段内偏移量为16位,则一个作业最多可有216个段,最大段长为65536B即64KB
页式系统中,逻辑地址的页号和页内偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显示提供,在髙级程序设计语言中,这个工作由编译程序完成
段表:每个进程都有一张逻辑空间与内存空间映射的段表,其中每一个段表项对应进程的一个段。
访问内存的时候根据段号和段表项的长度计算当前访问段在段表中的位置,然后访问段表,得到该段的物理地址,根据该物理地址以及段内偏移量就可以得到需要访问的内存。
2.2.2 地址变换
为了实现进程从逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器
段表寄存器存了段表始址F和段表长度M,从逻辑地址A到物理地址E之间的地址变换过程如下:
- 从逻辑地址A中取出前几位为段号S,后几位为段内偏移量W
- 比较段号S和段表长度M,若S多M,则产生越界中断,否则继续执行
- 段表中段号S对应的段表项地址 = 段表起始地址F + 段号S * 段表项长度,取出该段表项的前几位得到段长C。若段内偏移量>=C,则产生越界中断,否则继续执行
- 取出段表项中该段的起始地址b,计算 E = b + W,用得到的物理地址E去访问内存
2.2.3 段的共享
段的共享是通过两个作业的段表中相应表项指向被共享的段的同一个物理副本来实现的
不能修改的代码称为纯代码或可重入代码(它不属于临界资源),这样的代码和不能修改的数据是可以共享的
而可修改的代码和数据则不能共享
2.3 分页与分段的区别
段式管理
- 优点:没有内部碎片(因为段大小可变,改变段大小来消除内碎片)。
- 缺点:段换入换出时,会产生外部碎片(比如4k的段换5k的段,会产生1k的外碎片)
页式管理
- 优点:没有外部碎片(因为页的大小固定),
- 缺点:会产生内部碎片(一个页可能填充不满)
分页与分段的区别
目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;
大小不同:页的大小固定且由系统决定(一般为4k),而段的长度却不固定,由其所完成的功能决定;
地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;一个段在物理空间是连续的内存。
信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;
内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。
2.4 段页式存储页式存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。
如果将这两种存储管理方法结合起来,就形成了段页式存储管理方式
2.4 段页式存储
页式存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。
如果将这两种存储管理方法结合起来,就形成了段页式存储管理方式
2.4.1 基本概念
基本思想:
- 作业的地址空间首先被分成若干个逻辑段,每段都有自己的段号
- 再将每一段分成若干个大小固定的页
- 对内存空间的管理仍然和分页存储管理一样,将其分成若干个和页面大小相同的块,对内存的分配以块为单位
逻辑地址结构:段号S、页号P和页内偏移量W。
每个进程建立一张段表,而每个分段有一张页表
2.4.2 地址变换
为了实现进程从逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器
进行一次访问实际需要三次访问内存
- 首先利用段号S,将它与段表长TL进行比较。若S<TL,表示未越界
- 根据段表始址F和段号S来求出该段所对应的段表项在段表中的位置,从中得到该段的页表始址
- 根据段内页号P来获得对应页的页表项位置,从中读出该页所在的物理块号b
- 利用块号b和页内地址来构成物理地址
三、虚拟内存
虚拟内存的基本思想是:在程序装入时,可以将程序的一部分装入内存,而将其余部分留在外存,就可以启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。
虚拟内存容量:内存+外存
理论依据:局部性原理
与传统存储器比较虚拟存储器有以下三个主要特征:
- 多次性,是指无需在作业运行时一次性地全部装入内存,而是允许被分成多次调入内存运行。
- 对换性,是指无需在作业运行时一直常驻内存,而是允许在作业的运行过程中,进行换进和换出。
- 虚拟性,是指从逻辑上扩充内存的容量,使用户所看到的内存容量,远大于实际的内存容量。
虚拟内存的实现有以下两种方式:
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
3.1 请求分页存储管理
将当前需要的一部分页面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的页面不在内存时,再通过调页功能将其调入,同时还可以通过置换功能将暂时不用的页面换出到外存上,以便腾出内存空间
3.1.1 地址变换
请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的
请求分页系统中,每当所要访问的页面不在内存时,便产生一个缺页中断,请求操作系统将所缺的页调入内存
缺页中断处理中:
- 如果内存中有空闲块,则分配一个块,将要调入的页装入该块
- 若此时内存中没有空闲块,则要淘汰某页
- 若被淘汰页在内存期间被修改过,则要将其写回外存
3.2 页面置换算法
进程运行时,若其访问的页面不在内存而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。
选择调出页面的算法就称为页面置换算法
- 最优页面置换算法
最理想的状态下,所选择的被淘汰页面将是最长时间内不再被访问的页面,当然,这样的算法不可能实现,因为不确定一个页面在何时会被用到。
- 先进先出页面置换算法(FIFO)
这种算法的思想和队列是一样的,该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予淘汰。
实现简单,把一个进程已调入内存的页面按先后次序链接成一个队列,并且设置一个指针总是指向最老的页面。
缺点:对于有些经常被访问的页面如含有全局变量、常用函数、例程等的页面,不能保证这些不被淘汰。
- 最近最久未使用页面置换算法LRU(Least Recently Used)
根据页面调入内存后的使用情况做出决策。LRU置换算法是选择最近最久未使用的页面进行淘汰。
实现:
1.为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰
2.利用一个特殊的栈保存当前使用的各个页面的页面号。每当进程访问某页面时,便将该页面的页面号从栈中移出,将它压入栈顶。因此,栈顶永远是最新被访问的页面号,栈底是最近最久未被访问的页面号。
- 时钟置换算法(Clock Page Replacement Algorithm)
把所有的页面都保存在一个类似钟面的环形结构中
每一个页面增加一个使用位
- 当某一页首次装入主存时,该帧的使用位设置为1,当该页随后再被访问到时,它的使用位也被置为1
- 当需要替换一页时,操作系统扫描缓冲区,以查找使用位被置为0的一帧,每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0
- 如果在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换
- 如果所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并且停留在最初的位置上,替换该帧中的页
3.3 页面抖动( 颠簸)
颠簸本质上是指频繁的页面调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)
内存颠簸的解决策略包括:
- 如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;
- 如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;
- 终止该进程或增加物理内存容量。
3.4 驻留集大小
驻留集:对于分页式的虚拟内存,不需要把一个进程的所有页都读取到主存,操作系统必须决定读取多少页。也就是说,给特定的进程分配多大的主存空间,这就是驻留集。
主要考虑以下几点:
- 分配给一个进程的存储量越小,在任何时候驻留在主存中的进程数就越多,从而可以提高处理机的时间利用效率
- 如果分配给一个进程的存储量太小,尽管有局部性原理,页错误率仍然会相对较高
- 如果存储量太大,页数过多,由于局部性原理,给特定的进程分配更多的主存空间对该进程的错误率没有明显的影响
操作系统通常釆用三种策略:
- 固定分配局部置换
为每个进程分配一定数目的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后再调入需要的页面
- 可变分配全局置换
为系统中的每个进程分配一定数目的物理块,操作系统自身也保持一个空闲物理块队列。
当某进程发生缺页时,系统从空闲物理块队列中取出一个物理块分配给该进程,并将欲调入的页装入其中。
- 可变分配局部置换
为每个进程分配一定数目的物理块
当某进程发生缺页时,只允许从该进程在内存的页面中选出一页换出,这样就不会影响其他进程的运行
如果进程在运行中频繁地缺页,系统再为该进程分配若干物理块,直至该进程缺页率趋于适当程度
反之,若进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块
3.5 调入页面的时机
为确定系统将进程运行时所缺的页面调入内存的时机,可釆取以下两种调页策略
- 预调页策略
根据局部性原理,一次调入若干个相邻的页可能会比一次调入一页更高效。但如果调入的一批页面中大多数都未被访问,则又是低效的。所以就需要釆用以预测为基础的预调页策略,将预计在不久之后便会被访问的页面预先调入内存
- 请求调页策略
进程在运行中需要访问的页面不在内存而提出请求,由系统将所需页面调入内存。
由这种策略调入的页一定会被访问,且这种策略比较易于实现,故在目前的虚拟存储器中大多釆用此策略。
缺点在于每次只调入一页,调入调出页面数多时会花费过多的I/O开销
3.6 从何处调入页面
请求分页系统中的外存分为两部分:
- 用于存放文件的文件区,釆用连续分配方式
- 用于存放对换页面的对换区,釆用离散分配方式
故对换区的磁盘I/O速度比文件区的更快,这样从何处调入页面有三种情况:
- 系统拥有足够的对换区空间
可以全部从对换区调入所需页面,以提髙调页速度。为此,在进程运行前,需将与该进程有关的文件从文件区复制到对换区
- 系统缺少足够的对换区空间
凡不会被修改的文件都直接从文件区调入,而当换出这些页面时,由于它们未被修改而不必再将它们换出。
对于那些可能被修改的部分,在将它们换出时须调到对换区,以后需要时再从对换区调入。
- UNIX方式
与进程有关的文件都放在文件区,故未运行过的页面,都应从文件区调入
曾经运行过但又被换出的页面,由于是被放在对换区,因此下次调入时应从对换区调入
进程请求的共享页面若被其他进程调入内存,则无需再从对换区调入