【操作系统】 内存管理
内存管理概念
内存管理的功能有:
- 内存空间的分配与回收。由操作系统完成主存储器空间的分配和管理,提高编程效率。
- 地址转换。在多道程序环境下,程序中逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供存储变换功能,把逻辑地址转换成相应的物理地址。
- 内存空间的扩充。利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存。
- 存储保护。保证各道作业在各自的存储空间内运行,互不干扰。
创建进程首先要将程序和数据装入内存。将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤:
- 编译。由编译程序将用户源代码编译成若干目标模块。
- 链接。由链接程序将编译后形成的一组目标代码及所需的库函数链接在一起,形成一个完整的装入模块。
- 装入。由装入程序将装入模块装入内存运行。
程序的链接有以下三种方式:
- 静态链接。在程序运行前,先将各自目标模块及它们所需的库函数链接成一个完整的可执行程序,以后不再拆开。
- 装入时动态链接。将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的方式。
- 运行时动态链接。对某些目标模块的链接,是在程序执行中需要该模块时才进行的。其优点是便于修改和更新,便于实现对目标模块的共享。
内存的装入模块在装入内存时,同样有以下三种方式:
- 绝对装入。在编译时,若知道某个程序将驻留在内存的某个位置,则编译程序将产生绝对地址的目标代码。绝对装入程序按照装入模块中的地址,将程序和数据装入内存。由于程序中的逻辑地址与实际内存地址完全相同,故不需要对程序和数据的地址进行修改。 绝对装入的方式只适用于单道环境。
- 可重定位装入。在多道程序环境下,多个目标模块的起始地址(简称始址)通常都从 0 开始,程序中的其他地址都是相对于始址的,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入内存的适当位置。装入时对目标程序中指令和数据的修改过程称为重定位,地址变换通常是在装入时一次完成的,所以又称静态重定位。
- 动态运行时装入,也称动态重定位。程序在内存中若发生移动,则需要采用动态的装入方式。装入程序是把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存后的所有地址均为相对地址。这种方式需要一个重定位寄存器的支持。 动态重定位的特点如下:可以将程序分配到不连续的存储区中;在程序运行之前可以指装入它的部分代码即可投入运行,然后再程序运行期间,根据需要动态申请分配内存;便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。
编译后,每个目标模块都从 0 号单元开始编址,这称为目标模块的相对地址(或逻辑地址)。 物理地址空间是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据,最后都要通过物理地址从主存中存取。当装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定向。
覆盖与交换技术是在多道程序环境下用来扩充内存的两种方法。
覆盖的基本思想如下:由于程序运行时并非任何时候都要访问程序及数据的各个部分(尤其是大部分),因此可以把用户空间分成一个固定区和若干覆盖区。将经常活跃的部分放在固定区,其余部分按调用关系分段。首先将那些即将要访问的段放入覆盖区,其它段都放在外村中,在需要调用前,系统再将其调入覆盖区,替换掉覆盖区原有的段。
交换(对换)的基本思想是:把处于等待状态(或再 CPU 调度原则下被剥夺运行权力)的程序从内存移到辅存,把内存空间腾出来,这一过程又称换出;把准备好竞争 CPU 运行的程序从辅存移到内存,这一过程又称换入。(中级调度采用的就是交换技术。)
连续分配方式是指为一个用户程序分配一个连续的内存空间。连续分配方式主要包括单一连续分配、固定分区分配和动态区分配。
- 单一连续分配。内存在此方式下分为系统区和用户区,系统区仅供操作系统使用,通常在低地址部分;用户区是为用户提供的、除系统区之外的内存空间。这种方式无须进行内存保护。因为内存中永远只有一道程序,因此肯定不会因为访问越界而干扰到其他程序。 这种方式优点是简单、无外部碎片,可以采用覆盖技术,不需要提供额外的技术支持。缺点是只能用于单用户、单任务的操作系统中,有内部碎片,存储器利用率极低。
- 固定分区分配。是最简单的一种多道程序存储管理方式,它将用户内存空间划分为若干固定大小的区域,每个分区只装入一道作业。当有空闲分区时,便可再从外存的后备作业队列中选择适当大小的作业装入该分区,如此循环,固定分区分配在划分分区时有两种不同的方法:(1)分区大小相等。用于利用一台计算机去控制多个相同对象的场合,缺乏灵活性。(2)分区大小不等。划分为多个较小的分区、适量的中等分区和少量大分区。
- 动态分区分配又称可变分区分配,是一种动态划分内存的分区方法,这种分区方法不预先划分内存,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区地大小正好适合进程地需要。因此,系统中分区地大小和数目是可变的。
作业道数 | 内部碎片 | 外部碎片 | 硬件支持 | 可用空间管理 | 解决碎片方法 | 解决空间不足 | 提高作业道数 | |
单道连续分配 | 1 | 有 | 无 | 界地址寄存器、越界检查机构 | - | - | 覆盖 | 交换 |
多道固定分配 | <=N(用户空间划为N块) | 有 | 无 |
1.上下界寄存器、越界检查机构 2. 基地址寄存器、长度寄存器、动态地址转换机构 |
- | - | ||
多道可变连续分配 | - | 无 | 有 |
1.数组 2.链表 |
紧凑 |
动态分区策略:
- 首次适应( First Fit )算法。空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。
- 最佳适应( Best Fit )算法。空闲分区按容量递增的方式形成分区链,找到第一个满足要求的空闲分区。
- 最坏适应( Worst Fit )算法。又称最大适应( Largest Fit )算法,空闲分区以容量递减的次序链接。找到第一个满足要求的空闲分区,即挑选出最大的分区。
- 邻近适应算法( Next Fit )算法。又称循环首次适应算法,由首次适应算法演变而成。不同之处是,分配内存时从上次查找结束的位置开始继续查找。
固定分区会产生内部碎片,动态分区会产生外部碎片。
基本分页存储管理方式
把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位。每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间。
页面和页面大小。进程中的块称为页(Page),内存中的块称为页框(Page Frame,或页帧)。页面大小应是2的整数幂。
地址结构。分页存储管理的逻辑地址结构如图:
31 ... 12 | 11 ... 0 |
页号P | 页内偏移量W |
地址结构包含两部分:前一部分为页号P,后一部分为页内偏移量W,地址长度为32位,其中0~11位为页内地址,即每页大小为4KB;12~32位为页号,地址空间最多允许220页。
为了便于在内存中找到进程的每个页面所对应的物理快,系统为每个进程建立一张页表,它记录页面在内存中对应的物理块号,页表一般存放在内存中。页表项与地址都由两部分构成,第一部分都是页号,但页表项的第二部分是物理内存中的块号,而地址的第二部分是页内偏移;页表项的第二部分与地址的第二部分共同组成物理地址。
页式管理的地址空间是一维的。
若页表全部放在内存中,则存取一个数据或一条指令至少要访问两次内存:第一次是访问页表,确定所存取的数据或指令的物理地址;第二次是根据该地址存取数据或指令。这种方法比通常执行指令的速度慢了一半。为此,在地址变换机构中增设一个具有并行查找能力的告诉缓冲存储器——快表,又称相联存储器(TLB),用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,主存中的页表常称为慢表。
有些处理机设计为快表和慢表同时查找,若在快表中查找成功则终止慢表的查找。
二级页表实际上是在原有页表结构上再加上一层页表。为查询方便,顶级页表最多只能有一个页面。
分页管理方式是从计算机的角度考虑设计的,目的是提高内存的利用率,提升计算机的性能;分页通过硬件机制实现,对用户完全透明。分段管理方式的提出则考虑了用户和程序员,以满足方便编程、信息保护和共享、动态增长及动态链接等多方面的需求。
基本段式存储管理方式
段式管理方式按照用户进程中的自然段划分逻辑空间。每段从0开始编址,并分配一段连续的地址空间(段内要求连续,段间不要求连续,整个作业的地址空间是二维的)。其逻辑地址由段号S和段内偏移量W两部分组成。
31 ... 16 | 15 ... 0 |
段号S | 段内偏移量W |
段号为16位,段内偏移量16位,因此一个作业最多有216段,最大段长为64KB。
每个进程都有一张扩及空间与内存空间映射的段表,其中每个段表对应进程的一段,段表项记录该段在内存中的始址和长度。段表的内容如下图
段号 | 段长 | 本段在主存的始址 |
段的共享与保护。在分段系统中,段的共享是通过两个作业的段表中相应表项指向被共享段的同一个物理副本来实现的。当一个作业正从共享段中读取数据时,必须防止另一个作业修改此共享段中的数据。不能修改的代码称为纯代码或可重入代码(它不属于临界资源),这样的代码不能修改的数据可以共享,而可修改的代码和数据不能共享。
在段页式系统中,作业的地址空间首先被分为若干逻辑段,每段都有自己的段号,然后将每段分成若干大小固定的页。作业的逻辑地址分为三部分:段号、页号和页内偏移量,如图所示:
段号S | 页号P | 页内偏移量W |
为了实现地址变换,系统为每个进程建立一张段表,每个分段有一张页表。在一个进程中,段表只有一个,而页表可能有多个。
段页式管理的地址空间是二维的。
在虚拟内存管理中,地址变换机构将逻辑地址变换为物理地址,形成该逻辑地址的阶段是链接。编译过后的程序需要经过链接才能装在,而链接后形成的目标程序中的地址就是逻辑地址。
在使用交换技术时,若一个进程正在 I/O 则不能交换出内存,否则其 I/O 数据区将被新换入的进程占用,导致错误。
分区分配内存管理方式的主要保护措施是界地址保护。
存储管理方案中,单一连续存储管理可采用覆盖技术。覆盖技术是早期在单一连续存储管理中使用的扩大存储容量的一种技术,它同样可用于固定分区分配的存储管理。
静态装入是指在编程极端就把物理地址计算好。静态重定位是指在装入时把逻辑地址转换成物理地址,但装入后不能改变。动态重定位是指在执行时再决定装入的地址并装入,装入后可能会换出,所以同一个模块在内存中的物理地址是可能改变的。动态重定位是指在作业运行过程中执行到一条访存指令时,再把逻辑地址转换为主存中的物理地址,实际中时通过硬件地址转换机制实现的。
内存分页管理是在硬件和操作系统层面实现的,对用户、编译系统、连接装配程序等上层时不可见的。
引入段式存储管理结构,主要是为了满足用户的下列要求:方便编程、分段共享、分段保护、动态链接和动态增长。
对主存的访问是以字节或字为单位的。
将作业的逻辑地址变为物理地址的过程称为地址重定位。
在分页存储管理中,逻辑地址分配是按页为单位进行分配的,而主存的分配即物理地址分配是以块为单位分配的。
段页式存储管理兼有页式管理和段式管理的优点,采用分段来分配和管理用户地址空间,采用分页方法来管理物理存储空间。
虚拟内存管理
传统存储管理方式的特征:
- 一次性。作业必须一次性全部装入内存后,才能开始运行。
- 驻留性。作业被装入内存后,就一直驻留在内存,其任何部分都不会被换出,直至作业运行结束。
局部性原理表现在以下两个方面:
- 时间局部性。程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生时间局部性的典型原因是程序中存在着大量的循环操作。
- 空间局部性。一旦程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定范围之内,因为指令通常是顺序存放、顺序执行的,数据也是以向量、数组、表等形式簇聚存储的。
- 时间局部性。程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生时间局部性的典型原因是程序中存在着大量的循环操作。
基于局部性原理,在程序装入时,将程序的一部分装入内存,而将其余部分留在外存,就可启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。这样,系统好像为用户提供了一个比实际大得多的存储器,称为虚拟存储器。
虚拟存储器的大小由计算机的地址结构决定,并不是内存和外存的简单相加。虚拟存储器有以下三个主要特征:
- 多次性。多次性是指无须在作业运行时一次性地全部装入内存,而允许被分成多次调入内存运行。
- 对换性。对换性是指无须在作业运行时一直常驻内存,而允许在作业的运行过程中,进行换入和换出。
- 虚拟性。虚拟性是指从逻辑上扩充内存的容量,使用户所看到的内存容量远大于实际的内存容量。
虚拟内存的实现需要建立在离散分配的内存管理基础上。虚拟内存的实现有以下三种方式:
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
不管哪种方式,都需要一定的硬件支持。一般需要的有以下几个方面
- 一定容量的内存和外存。
- 页表机制(或段表机制),作为主要的数据结构。
- 中断机构,当用户要访问的部分尚未调入内存时,则产生中断。
- 地址变换机构,逻辑地址到物理地址的变换。
请求分页是目前最常用的一种实现虚拟存储器的方法。
请求分页系统的页表机制不同于基本分页系统,请求分页系统在一个作业运行之前不要求全部一次性调入内存,因此在作业运行过程中,必然会出现要访问的页面不再内存中的情况,如何发现和处理这种情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中增加了4个字段,如图所示:
页号 | 物理块号 | 状态位P | 访问字段A | 修改位M | 外存地址 |
增加的4个字段说明如下:
- 状态位P。用于指示该页是否已调入内存,供程序访问时参考。
- 访问字段A。用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法换出页面时参考。
- 修改位M。标识该页在调入内存后是否被修改过。
- 外存地址。用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考。
缺页中断在指令执行期间而非一条指令执行完后产生和处理中断信号,属于内部中断。一条指令在执行期间,可能产生多次缺页中断。
页面置换算法
最佳(OPT)置换算法(理想算法,无法实现)
选择的被淘汰的页面时以后不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率。
访问页面 | 7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
物理块1 | 7 | 7 | 7 | 2 | 2 | 2 | 2 | 2 | 7 | |||||||||||
物理块2 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | ||||||||||||
物理块3 | 1 | 1 | 3 | 3 | 3 | 1 | 1 | |||||||||||||
缺页 | √ | √ | √ | √ | √ | √ | √ | √ | √ |
先进先出(FIFO)页面置换算法
优先淘汰最早进入内存的页面,即在内存中驻留时间最久的页面。
访问页面 | 7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
物理块1 | 7 | 7 | 7 | 2 | 2 | 2 | 4 | 4 | 4 | 0 | 0 | 0 | 7 | 7 | 7 | |||||
物理块2 | 0 | 0 | 0 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | 1 | 1 | 0 | 0 | ||||||
物理块3 | 1 | 1 | 1 | 0 | 0 | 3 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | |||||||
缺页 | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
√ |
FIFO算法还会产生所分配的物理块增大而页故障数不减反增的异常现象,称为 Belady 异常。只有 FIFO 算法可能出现 Belady 异常。
最近最久未使用(LRU)置换算法
选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间未访问过的页面,在最近的将来可能也不会被访问。
访问页面 | 7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
物理块1 | 7 | 7 | 7 | 2 | 2 | 4 | 4 | 4 | 0 | 1 | 1 | 1 | ||||||||
物理块2 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 3 | 3 | 0 | 0 | |||||||||
物理块3 | 1 | 1 | 3 | 3 | 0 | 2 | 2 | 2 | 2 | 7 | ||||||||||
缺页 | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
LRU算法性能较好,但需要寄存器和栈的硬件支持。LRU是堆栈类算法。FIFO算法基于队列实现。
时钟(CLOCK)置换算法(最近未用(NRU)算法)(使用位)
改进型 CLOCK 置换算法(增加修改位)
给一个进程分配的物理页框的集合就是这个进程的驻留集。
现代操作系统通常采用三种策略:
- 固定分配局部置换。它为每个进程分配一定数目的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出。
- 可变分配全局置换。它为系统中的每个进程分配一定数目的物理块,操作系统自身也保持一个空闲物理块队列。当某进程发生缺页时,系统从空闲物理块队列中取出一个物理快分配给该进程,并将欲调入的页装入其中。
- 可变分配局部置换。它为每个进程分配一定数目的物理块,当某个进程发生缺页时,只允许从该进程在内存的页面中选出一页换出,因此不会影响其他进程的运行。若进程在运行中频繁地缺页,则系统再为该进程分配若干物理快,直至该进程缺页率趋于适当程度;反之,若进程运行中的缺页率特别低,则可适当减少分配给该进程的物理块。
在页面置换过程中,一种最糟糕的情形是,刚刚换出的页面马上又要换入主存,刚刚换入的页面马上又要换出主存,这种频繁的页面调度行为称为抖动或颠簸。频繁发生缺页中断(抖动)的主要原因是,某个进程频繁访问的页面数目高于可用的物理页帧数目。所有的页面调度策略都不可能完全避免抖动。
工作集是指在某段时间间隔内,进程要访问的页面集合。一般来说,工作集可由时间 t 和工作集窗口大小 Δ来确定。
实际应用中,工作集窗口会设置的很大,即对于局部性好的程序,工作集的大小一般会比工作集窗口 Δ小很多。
一般来说分配给进程的物理块数(即驻留集大小)要大于工作集大小。
导致 LRU 算法实现起来耗费高的原因是需要对所有的页进行排序。
在计算机系统中,快表用于地址变换。
覆盖技术与虚拟存储技术最本质的不同在于,覆盖程序段的最大长度要受内存容量大小的限制,而虚拟存储器中程序的最大长度不受内存容量的限制,只受计算机地址结构的限制。
最先进入主存的页面在此次缺页之前不能再被访问的情况下,该页面也就同时是最久未被访问的页面,此时 FIFO 与 LRU 替换算法一样。
以上内容均来自王道书籍及相关课程等