第三章 内存管理

考研考点
image-20260504133742103


一、内存管理基础

1. 内存管理的基本概念

1.1 逻辑地址空间和物理地址空间
  • 编译后,每个目标模块都从0号单元开始编址,这称为该目标模块的相对地址(或逻辑地址)。链接程序将各个模块链接成一个完整的可执行目标程序时,链接程序顺序依次按各个模块的相对地址构成统一的从0号单元开始编址的逻辑地址空间(或虚拟地址空间),对于32位系统,逻辑地址空间的范围为0~232-1。进程在运行时,看到和使用的地址都是逻辑地址。用户程序和程序员只需知道逻辑地址,而内存管理的具体机制则是完全透明的。不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。

  • 物理地址空间是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据,最后都要通过物理地址从主存中存取。装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定位

  • 操作系统通过内存管理部件(MMU)将进程使用的逻辑地址转换为物理地址。进程使用虚拟内存空间中的地址,操作系统在相关硬件的协助下,将它“转换”成真正的物理地址。逻辑地址通过页表映射到物理内存,页表由操作系统维护并被处理器引用

操作系统作为系统资源的管理者,操作系统负责内存空间的分配与回收。需要解决如下问题:

  • 很多位置都可以放,那应该放在哪里?
  • 操作系统要怎么记录哪些内存区域已经被分配出去了,哪些又还空闲?
  • 当进程运行结束之后,如何将进程占用的内存空间回收?
1.2 程序的链接与装入

用户程序要在系统中运行,必须先将它装入内存,然后再将其转变为一个可执行的程序,对用户程序的处理步骤可分为三步(编译、链接、装入):

  • 编译。由编译程序将用户源代码编译成若干目标模块。注意:此编译包含预处理、编译和汇编三步。
  • 链接。由链接程序将编译后形成的一组目标模块,以及它们所需的库函数链接在一起,形成一个完整的装入模块。此阶段形成逻辑地址
  • 装入。由装入程序将装入模块装入内存运行。此阶段形成物理地址

将用户程序变为可在内存中执行的程序的步骤如下图所示。
image-20260504133928190

注意:将高级语言源程序转换为可执行目标文件(还未装入到内存中)的主要过程:预处理→编译→汇编→链接

1.2.1 程序的链接
  • 源程序经过编译后,可得到一组目标模块。链接程序的功能是将这组目标模块以及它们所需要的库函数装配成一个完整的装入模块。在对目标模块进行链接时,根据链接的时间不同,可将链接方式分成:
    • 静态链接方式
    • 装入时动态链接
    • 运行时动态链接
  • 在虚拟内存管理中,地址变换机构将逻辑地址变换为物理地址,形成完整的逻辑地址的阶段是链接。程序的开发流程通常包括编辑、编译、链接和装载等几个主要阶段。每个阶段都对最终程序中的地址有着特定的影响:
    • 编辑阶段:程序员编写源代码,此时不涉及任何地址变换。
    • 编译阶段:编译器将源代码转换为机器代码,并生成目标文件(如.o文件)。在这个阶段,编译器会为程序中的每个函数、变量等分配相对地址(即逻辑地址),但这些地址仍然是相对于各自目标文件的,而不是全局的
    • 链接阶段:链接器将多个目标文件以及所需的库文件链接成一个可执行文件。在链接过程中,链接器会解决模块间的符号引用,并重新分配地址以形成整个程序的完整逻辑地址空间
    • 装载阶段:操作系统将可执行文件加载到内存中,并为其分配物理内存空间。在这个阶段,地址变换机构(如操作系统的内存管理单元MMU)将逻辑地址(或称为虚拟地址)转换为物理地址。这是通过查找页表(在分页式内存管理中)或描述符表(在分段式内存管理中)来实现的。

(1) 静态链接方式
image-20260504133939073
静态链接:在程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。

(2) 装入时动态链接
image-20260504133948177
装入时动态链接:将各目标模块装入内存时,边装入边链接的链接方式

(3) 运行时动态链接
image-20260504133955246
运行时动态链接:在程序执行中需要该目标模块时才对它进行链接。其优点是便于修改和更新,便于实现对目标模块的共享。

1.2.2 程序的装入

(1) 绝对装入方式
绝对装入:只适合单道程序设计。用户知道程序将调用内存的什么位置,无需进行地址的映射

(2) 可重定位装入方式
静态重定位:又称可重定位装入。适合多道程序设计。会有地址的映射过程,是在程序执行前一次性装入的。
image-20260504134031130

(3) 动态运行时的装入方式
动态重定位:又称动态运行时装入。适合程序在运行时会发生位置变化。作业转入内存后并不立即发生地址映射,到程序执行时才进行地址映射。需重定位寄存器(存放程序或数据在内存中的起始地址)的帮助。这种方式为动态可重定位分区分配提供了支持。

1.3 内存共享

并非所有的进程内存空间都适合共享,只有那些只读的区域才可以共享
可重入代码/纯代码:可允许多个进程同时访问但不允许被任何进程修改的代码。(可以在实际执行过程中,自己单独配一个局部数据区,从而只对自己的数据区进行修改而无需改变共享的代码)。

1.4 内存保护
  • 操作系统需要提供内存保护功能。保证各进程在各自存储空间内运行,互不干扰。
  • 内存保护可采取两种方法:
    • 方法一:在CPU中设置一对上、下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界。
    • 方法二:采用重定位寄存器(又称基址寄存器)和界地址寄存器(又称限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址

【示例】假设进程1的逻辑地址空间为 0~179;实际物理地址空间为 100~279

  • 方法一:
    image-20260504134044354

  • 方法二:
    image-20260504134050740

1.5 内存空间的分配与回收

image-20260504134057531

  • 连续分配:为用户进程分配一个连续的内存空间,即内存块在物理地址上是连续的。
  • 非连续分配:为用户进程分配的内存空间在物理地址上(整体)是不连续的,通过页表或段表等数据结构实现逻辑地址到物理地址的映射。这种分配方式能够更灵活地利用内存空间,减少碎片的产生。

发展动力:

  • 存储管理方式随着操作系统的发展而发展。在操作系统由单道向多道发展时,存储管理方式便由单一连续分配发展为固定分区分配
  • 为了更好地提高内存的利用率,进而从连续分配方式发展到离散分配方式中的页式存储管理
  • 引入分段存储管理的目的,主要时满足用户在编程和使用方面的要求,如方便编程、信息保护和共享、动态链接与动态增长等方面的要求。
1.6 进程的内存映像

不同与存放在硬盘上的可执行程序文件,当一个程序调入内存运行时,就构成了进程的内存映像。进程的内存映像一般包括:

  • 代码段:进程的二进制代码,只读,可被多个进程共享。
  • 数据段:程序运行时加工处理的对象,包括全局变量和静态变量。
  • 进程控制块(PCB):存放在系统内核区,OS通过PCB来控制和管理进程。
  • 堆:存放动态分配的变量,由低地址向高地址增长。
  • 栈:用来实现函数调用,由高地址向低地址增长。

代码段和数据段在调入内存时就指定了大小,而堆和栈时可动态变化的(可拓展和收缩)。每次调用一个函数,栈就会增长;每从一个函数返回,栈就会收缩。

【示例】一个进程在内存中的映像
image-20260504134206742

2. 内存空间的扩充

2.1 覆盖技术

早期的计算机内存很小,比如 IBM 推出的第一台PC机最大只支持 1MB 大小的内存。因此经常会出现内存大小不够的情况。
后来人们引入了覆盖技术,用来解决“程序大小超过物理内存总和”的问题。
覆盖技术的思想:将程序分为多个段(多个模块),常用的段常驻内存,不常用的段在需要时调入内存。

  • 内存中分为一个“固定区”和若干个“覆盖区”。
  • 需要常驻内存的段放在“固定区”中,调入后就不再调出(除非运行结束)。
  • 不常用的段放在“覆盖区”,需要用到时调入内存,用不到时调出内存。

必须由程序员声明覆盖结构,操作系统完成自动覆盖。缺点:对用户不透明,增加了用户编程负担。
覆盖技术只用于早期的操作系统中,现在已成为历史。

【示例】一个大小为52KB的程序装入内存
按照自身逻辑结构,让那些不可能同时被访问的程序段共享同一个覆盖区:
image-20260504134121689
覆盖区的大小为所有共享该覆盖区中的最大程序的大小。

2.2 交换技术

交换(对换)技术的设计思想:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)。
与交换技术对应的进程调度就是中级调度:

image-20260504134220931

注意:PCB 会常驻内存,不会被换出外存。

实现交换技术应解决如下三个问题:
① 应该在外存(磁盘)的什么位置保存被换出的进程?
具有对换功能的操作系统中,通常把磁盘空间分为文件区对换区两部分:

  • 文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式
  • 对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式

总之,对换区的I/O速度比文件区的更快

② 什么时候应该交换?
交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。

③ 应该换出哪些进程?

  • 可优先换出阻塞进程;
  • 可换出优先级低的进程;
  • 为了防止优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时间…

覆盖技术和交换技术的区别:

  • 覆盖在同一个程序或进程中;
  • 交换是在不同进程(作业)之间的。

3. 连续分配管理方式

3.1 单一连续分配

(1) 内存划分
image-20260504134329121

  • 系统区:通常位于内存的低地址部分,专门用于存放操作系统的相关数据。这部分内存对用户程序是不可见的,也不可被用户程序访问,以确保操作系统的稳定性和安全性。
  • 用户区:紧接在系统区之后,用于存放用户进程的相关数据。在单一连续分配方式中,内存中只能有 一道用户程序运行,该用户程序将独占整个用户区空间。

(2) 优缺点

  • 优点:

    • 实现简单:由于内存中只有一道用户程序,因此内存管理的复杂度大大降低。
    • 无外部碎片:由于用户区被单一用户程序独占,因此不会产生外部碎片(即空闲的、但由于太小而无法被有效利用的内存空间)。
    • 可采用覆盖技术:当程序大小超过物理内存总和时,可以采用覆盖技术来扩充内存,通过动态地调入和调出程序的不同部分来执行程序。
    • 不一定需要内存保护:由于内存中只有一道用户程序,且该程序独占用户区,因此可能不需要额外的内存保护机制来防止进程间的相互干扰。
  • 缺点:

    • 仅适用于单用户单任务的操作系统环境,限制了系统的并发性和多任务处理能力。
    • 存在内部碎片,即用户区内未被用户程序使用的部分内存空间将无法被其他程序利用,导致存储器利用率低。
3.2 固定分区分配

固定分区分配方法是一种简单而直接的多道程序存储管理方式,它将用户内存空间划分为若干个固定大小的区域,每个分区只装入一道作业。

(1) 分区划分

  • 固定大小:在处理作业之前,存储器就已经被划分成若干个分区,每个分区的大小可以是相同的,也可以是不同的。但是,一旦划分好分区后,主存储器中的分区个数和每个分区的大小就固定不变了。
    image-20260504134341271
    • 分区大小相等:缺乏灵活性,但是很适合用于用一台计算机控制多个相同对象的场合(比如:钢铁厂有n个相同的炼钢炉,就可把内存分为n个大小相等的区域存放n个炼钢炉控制程序);
    • 分区大小不等:增加了灵活性,可以满足不同大小的进程需求。根据常在系统中运行的作业大小情况进行划分(比如:划分多个小分区、适量中等分区、少量大分区)。
  • 分区说明表:系统中会有一张分区说明表,用于记录每个分区的大小、起始地址和分区的状态(如是否空闲)。这张表是内存管理的重要数据结构,用于在分配和回收内存时查找合适的分区。用数据结构的数组(或链表)即可表示这个表。

(2) 分配方式

  • 静态分配:在程序运行之前,就根据程序的大小和内存分区的大小来决定该程序应该装入哪个分区。如果程序的大小与某个分区的大小相匹配,或者小于某个分区的大小但可以接受内部碎片(即分区内未被程序使用的部分),则可以将该程序装入该分区。
  • 查找分区:当有用户程序要装入时,系统会根据该程序的大小和内存分区的大小,在分区说明表中查找一个足够大的空闲分区进行分配。如果找不到足够大的空闲分区,则这个作业暂时无法分配内存空间,系统可能会调度另一个作业或采取其他措施。

(3) 优缺点

  • 优点:

    • 实现简单,只需要少量的专用硬件支持(如存储保护机构);
    • 无外部碎片,因为每个分区的大小是固定的,所以不会产生由于分区划分不当而产生的外部碎片;
    • 可以通过分区说明表来灵活地管理内存分区,便于分配和回收。
  • 缺点:

    • 内部碎片严重,当程序小于固定分区大小时,分区内部会有空间浪费;
    • 存储空间的利用率低,因为分区的大小和数量是固定的,无法根据实际需要灵活调整;
    • 分区的大小和数量一旦确定,就难以改变,缺乏灵活性
3.3 动态分区分配(重点)

动态分区分配又称为可变分区分配。这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。

(1) 系统要用什么样的数据结构记录内存的使用情况?

  • 空闲分区表:每个空闲分区对应一个表项。表项中包含分区号、分区大小、分区起始地址等信息;
  • 空闲分区链:每个分区的起始部分和末尾部分分别设置前向指针和后向指针。起始部分处还可记录分区大小等信息。

【示例】有如下存分配情况
image-20260504134357233
空闲分区表:
image-20260504134403124
空闲分区链:
image-20260504134408042

(2) 当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?
把一个新作业装入内存时,须按照一定的动态分区分配算法,从空闲分区表(或空闲分区链)中选出一个分区分配给该作业。

动态分区分配算法:在动态分区分配方式中, 当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?

基于顺序搜索的分配算法

算法 算法思想 分区排列顺序 优点 缺点
首次适应 从头到尾找适合的分区 空闲分区以地址递增次序排列 综合看性能最好。算法开销小,回收分区后一般不需要对空闲分区队列重新排序
最佳适应 优先使用更小的分区,以保留更多大分区 空闲分区以容量递增次序排列 会有更多的大分区被保留下来,更能满足大进程需求 会产生很多太小的、难以利用的碎片;算法开销大,回收分区后可能需要对空闲分区队列重新排序
最坏适应 优先使用更大的分区,以防止产生太小的不可用的碎片 空闲分区以容量递减次序排列 可以减少难以利用的小碎片 大分区容易被用完,不利于大进程的分配;算法开销大(原因同上)
邻近适应 由首次适应演变而来,每次从上次查找结束位置开始查找 空闲分区以地址递增次序排列(可排列成循环链表) 不用每次都从低地址的小分区开始检索;算法开销小(原因同首次适应算法) 会使高地址的大分区也被用完

基于索引搜索的分配算法
动态分区分配算法中的基于索引搜索的分配算法,旨在提高内存分配的效率,尤其是在处理大量内存分区(系统很大)时。有如下三种算法:

算法 算法思想 优点 缺点
快速适应算法(Quick Fit, QF) 将空闲分区按其容量大小进行分类,并为每一类具有相同容量的空闲分区单独设立一个空闲分区链表。在内存中设立一张索引表,每个表项记录了对应类型空白分区链表的表头指针。 ① 查找效率高,能够快速定位到合适的空闲分区;② 保留大分区,满足大作业的要求;③ 不会产生外零头。 ① 归还分区时的算法复杂,系统开销较大;② 分配时以进程为单位,可能存在浪费;③ 空闲分区划分越细,浪费越严重。
伙伴系统(Buddy System) 将空闲分区组织成树状结构,每个节点代表一个分区,其子节点代表该分区被分割后的两个等大小分区。通过索引搜索和合并空闲分区来提高空间利用率。 ① 能够快速合并相邻的空闲分区,减少内存碎片;② 分区大小固定,便于管理和分配。 ① 可能产生内零头,影响内存利用率;② 分配非2的幂次方大小的内存块时,需要进行分割,可能导致浪费。
哈希算法 利用哈希快速查找的优点,以及空闲分区在可利用空间表中的分布规律,建立哈希函数,构造一张以空闲分区大小为关键字的哈希表。每个表项记录了对应空闲分区链表的表头指针。 ① 分配时根据所需空闲分区大小,通过哈希函数计算即可快速定位到相应的空闲分区链表;② 查找效率高,能够有效减少搜索时间。 ① 哈希表的构建和维护需要一定的系统开销;② 哈希冲突的处理可能会增加查找的复杂度。

(3) 如何进行分区的分配与回收操作?
假设系统采用的数据结构是“空闲分区表”…

如何分配?
分配内存的步骤:
① 请求内存

  • 当进程需要被装入内存时,它首先会向操作系统提出内存请求,包括所需内存的大小。

② 查找空闲分区

  • 操作系统会遍历空闲分区表,查找一个足够大的空闲分区来满足进程的内存请求。这个查找过程可能依据不同的算法进行,如首次适应算法、最佳适应算法、最坏适应算法或邻近适应算法等。

③ 分配内存

  • 如果找到满足条件的空闲分区,系统将从该分区中划分出所需大小的内存给进程,并更新空闲分区表。
    • 如果空闲分区的大小与请求的大小恰好相等,则整个分区被分配给进程,并从空闲分区表中删除该表项。
    • 如果空闲分区的大小大于请求的大小,则分区被分割为两部分:一部分分配给进程,另一部分保留为新的空闲分区,并更新空闲分区表以反映这一变化。
  • 如果找不到满足条件的空闲分区,则分配失败,进程可能需要等待或采取其他措施(如内存置换)。

【示例】假设空闲分区表如下所示(以起始地址和大小表示):

起始地址 大小(MB)
100 200
400 150
650 300

现在,有一个进程请求分配180MB的内存。
使用首次适应算法(First Fit):

  • 查找:从空闲分区表的第一个表项开始查找,即起始地址为100MB的分区。
  • 判断:该分区大小为200MB,大于请求的180MB,满足条件。
  • 分配:
    • 如果该分区只分配给这一个进程(不分割),则更新空闲分区表,删除起始地址为100MB、大小为200MB的分区表项。
    • 如果需要保留部分空闲区,则将该分区分割为两部分:一部分为180MB分配给进程,另一部分为剩余的20MB保留为新的空闲分区,并更新空闲分区表,添加起始地址为280MB(100MB+180MB)、大小为20MB的新表项,同时删除原表项。

分配后可能的空闲分区表:

  • 不分割原分区:
起始地址 大小(MB)
400 150
650 300
  • 分割原分区:
起始地址 大小(MB)
280 20
400 150
650 300

如何回收?
回收内存的步骤:
① 回收内存请求

  • 当进程运行结束或不再需要某个内存区域时,会向操作系统发出内存回收请求。

② 查找并修改空闲分区表

  • a. 确定回收分区:操作系统首先需要根据进程的内存标识(如起始地址和大小)来确定要回收的内存分区。

  • b. 检查相邻空闲区:接下来,系统检查回收分区的前后是否存在空闲区。这通常涉及到遍历空闲分区表,查找与回收分区相邻的表项。

  • c. 合并空闲区:

    • 如果回收分区的前后都是空闲区,则将它们合并为一个更大的空闲区。这涉及到更新空闲分区表,删除相邻的空闲区表项,并创建一个新的表项来表示合并后的空闲区。
    • 如果回收分区只与一侧的空闲区相邻,则将该空闲区扩展到回收分区的大小。
    • 如果回收分区不与任何空闲区相邻,则直接在空闲分区表中添加一个新的表项来表示该回收分区。

③ 更新空闲分区表

  • 无论是否进行了空闲区的合并,都需要更新空闲分区表以反映最新的内存状态。这包括修改空闲区的大小、起始地址等信息,并可能涉及到删除或添加表项。

【示例】假设有以下空闲分区表(以起始地址和大小表示):

起始地址 大小(MB)
100 200
400 150
600 300

如果进程释放了一个起始地址为300、大小为100的内存区域,回收后的处理流程如下:

  • 确定回收分区:回收分区为起始地址300、大小100的内存区域。
  • 检查相邻空闲区:发现回收分区与起始地址为100、大小为200及其起始地址为400、大小为150两个空闲区相邻。
  • 合并空闲区:和三为一。
  • 更新空闲分区表:
起始地址 大小
100 450
600 300

(4) 优缺点

  • 优点:
    • 高内存利用率:动态分区分配能够根据进程的实际需求来分配内存,减少了因固定分区大小不匹配而导致的内存浪费,从而提高了内存利用率;
    • 灵活性:这种方法能够灵活地调整内存分区的大小,以适应不同大小和变化的进程需求。这使得系统能够更灵活地管理内存资源,支持多道程序同时执行;
    • 无内部碎片。
  • 缺点:
    • 外部碎片:可能产生外部碎片,即存在一些小的、无法被利用的空闲内存块。这些碎片可能会降低系统的内存利用率,尤其是在频繁分配和释放内存的情况下;
    • 内存分配效率低:在某些情况下,如使用最佳适应算法时,动态分区分配可能会因为频繁搜索和合并空闲分区而导致分配效率较低。此外,如果系统无法找到足够大的空闲分区来满足进程的内存请求,那么内存分配可能会失败,这也会影响系统的性能和稳定性;
    • 管理开销大:动态分区分配需要额外的数据结构(如空闲分区表)来维护内存信息,以及算法来搜索和管理空闲分区。这些都会增加系统的管理开销,降低性能。

可以通过紧凑(拼凑,Compaction)技术来解决外部碎片
紧凑技术通过以下步骤来解决这一问题:

  • 内存移动:操作系统会定期或根据需要将内存中的进程(或数据)进行移动,使它们尽可能地相邻排列。这个过程类似于磁盘碎片整理,但发生在主存(RAM)中。
  • 空闲区合并:通过移动进程,原本分散的小空闲区可以被合并成较大的连续空闲区,从而减少了外部碎片。这些合并后的空闲区可以更有效地满足大型进程的内存需求。
  • 地址重定位:由于紧凑过程会改变进程在内存中的位置,因此必须对进程和数据的地址进行重定位。这通常通过设置一个重定位寄存器来实现,该寄存器保存了程序(或数据)在内存中的新起始地址。当程序执行时,硬件地址变换机构会将程序中的逻辑地址(相对地址)与重定位寄存器中的地址相加得到物理地址。

4. 非连续分配管理方式

4.1 基本分页存储管理(重点)
4.1.1 分页存储的基本概念

(1) 页框和页面

  • 页框:将内存空间分为一个个大小相等的分区(比如:每个分区4KB),每个分区就是一个页框(页框=分区=页帧=内存块=物理块=物理页面)。每个页框有一个编号,即页框号(页框号=分区号=页帧号=内存块号=物理页号),页框号从0开始
  • 页面:将进程的逻辑地址空间也分为与页框大小相等的一个个部分,每个部分称为一个页面 。每个页面也有一个编号,即页号,页号也是从0开始
  • 操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系
  • 各个页面不必连续存放,可以放到不相邻的各个页框中。

注意:进程的最后一个页面可能没有一个页框那么大。也就是说,分页存储有可能产生内部碎片,因此页框不能太大,否则可能产生过大的内部碎片造成浪费。

(2) 页表
为了能知道进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表
进程的逻辑地址空间通过页表找到对应的内存空间:
image-20260504134442733

  • 一个进程对应一张页表;
  • 进程的每个页面对应一个页表项;
  • 每个页表项由“页号”和“块号”组成;
  • 页表记录进程页面和实际存放的页框之间的映射关系
  • 每个页表项的长度是相同的。

注:页表通常存在PCB(进程控制块)中。

每个页表项占多少字节
假设某系统物理内存大小为 4GB,页面大小为 4KB,则每个页表项至少应该为多少字节?

内存块大小=页面大小=4KB= 212 B
4GB 的内存总共会被分为 232 / 212 = 220个内存块
内存块号的范围应该是 0 ~ 220 -1
内存块号至少要用 20 bit 来表示,至少要用3B来表示块号(3*8=24bit)
页表项连续存放,因此页号可以是隐含的,不占存储空间(类比数组)

【示例】假设页表中的各页表项从内存地址为 X 的地方开始连续存放…,如何找到页号为 i 的页表项?
image-20260504134452888
i 号页表项的存放地址 = X+3*i

注意:
各页表项会按顺序连续地存放在内存中;
理论上,页表项长度为 3B 即可表示内存块号的范围,但是,为了方便页表的查询,常常会让一个页表项占更多的字节,使得每个页面恰好可以装得下整数个页表项

(3) 分页存储的逻辑地址结构
逻辑地址的组成部分:

  • 页号(Page Number):标识进程逻辑地址空间中的各个页面。
  • 页内偏移量(Offset):确定数据在页内的具体位置。

【示例】分页存储管理的逻辑地址结构如下所示:
image-20260504134502961
地址结构包含两个部分:前一部分为页号,后一部分为页内偏移量 W。在上图所示的例子中,地址长度为 32 位,其中 0~11位 为“页内偏移量”,或称“页内地址”;12~31 位为“页号”。

(4) 确定一个逻辑地址对应的页号、页内偏移量

  • 页号 = 逻辑地址 / 页面长度 (/:下取整)
  • 页内偏移量 = 逻辑地址 % 页面长度(%:取余)
  • 在计算机内部,地址是用二进制表示的,如果页面大小刚好是 2 的整数幂,则计算机硬件可以很快速的把逻辑地址拆分成(页号,页内偏移量)
    • 如果每个页面大小为 2KB,用二进制数表示逻辑地址,则末尾 K 位即为页内偏移量其余部分就是页号

【示例】假设某计算机用32 个二进制位表示逻辑地址,页面大小为 4KB = 212B = 4096B
解析:

  • 0号页的逻辑地址范围应该是 0~4095,用二进制表示应该是:
    00000000000000000000000000000000 ~ 00000000000000000000111111111111
  • 1号页的逻辑地址范围应该是 4096~8191,用二进制表示应该是:
    00000000000000000001000000000000 ~ 00000000000000000001111111111111
  • 2号页的逻辑地址范围应该是 8192~12287,用二进制表示应该是:
    00000000000000000010000000000000 ~ 00000000000000000010111111111111

逻辑地址 2,用二进制表示应该是 00000000000000000000000000000010
页号 = 2/4096 = 0 = 00000000000000000000,页内偏移量 = 2%4096 = 2 = 000000000010

逻辑地址 4097,用二进制表示应该是 00000000000000000001000000000001
页号 = 4097/4096 = 1 = 00000000000000000001,页内偏移量 = 4097%4096 = 1 = 000000000001

重点:

  • 如果有 K 位表示“页内偏移量”,则说明该系统中一个页面的大小是 2K个内存单元。
  • 如果有 M 位表示“页号”,则说明在该系统中,一个进程最多允许有 2M 个页面。
4.1.2 基本地址变换机构
  • 为了提高地址变换的速度,在系统中设置一个页表寄存器(PTR),存放页表在内存的始址F页表长度M

  • 由于寄存器的造价昂贵,因此在单CPU系统中只设置一个页表寄存器。进程未执行时,页表的始址和页表长度存放在本进程的PCB中。

  • 求逻辑地址A 对应的物理地址的步骤如下:
    image-20260504134517587
    ① 计算页号 P 和页内偏移量W;
    ② 比较页号P 和页表长度M,若 P≥M,则产生越界中断,否则继续执行;
    ③ 页表中页号P对应的页表项地址 = 页表起始地址F + 页号P * 页表项长度,取出该页表项内容b,即为内存块号。

    • 页表长度指的是这个页表中总共有几个页表项,即总共有几个页;
    • 页表项长度指的是每个页表项占多大的存储空间;
    • 页面大小指的是一个页面占多大的存储空间。

    ④ 计算 E = b * L + W,用得到的物理地址E 去访存。L表示内存块大小。

以上整个地址变换过程均是由硬件自动完成的。

【示例】假设有一个分页存储管理系统,页面大小为1KB(即1024字节),物理内存中有32个可用的页框,进程有一个逻辑地址为2300的空间,需要被映射到物理内存中。
① 计算页号和页内偏移量
页面大小为1KB,即210 B。
页号 = ⌊2300/1024⌋ = 2
页内偏移量 = 2300 % 1024 = 252

② 2<32,页号没有越界

③ 找到2号页面在内存中的起始地址
通过页表(Page Table)查找逻辑页号对应的物理页框号。假设页表如下:

逻辑页号 内存块号
0 5
1 7
2 6

根据页表,逻辑页号2对应的内存块号是6。
注意:页表记录的只是内存块号,而不是内存块的起始地址!J 号内存块的起始地址 = J * 内存块大小
6号内存块的起始地址 = 6*1024B = 6144B

最后:逻辑地址2300的物理地址 = 6144B + 252B = 6396B

注意:页式管理中地址是一维的,因为只需要告诉CPU逻辑地址即可算出对应的物理地址。

4.1.3 具有快表的地址变换机构

(1) 什么是快表(TLB)

  • 快表,又称联想寄存器(TLB, translation lookaside buffer ),是一种访问速度比内存快很多的高速缓存(TLB不是内存!),用来存放最近访问的页表项的副本,可以加速地址变换的速度。与此对应,内存中的页表常称为慢表。
  • TLB 和 普通 Cache 的区别:TLB 中只有页表项的副本,而普通 Cache 中可能会有其他各种数据的副本。
  • 存储器分层结构:
    image-20260504134531785

(2) 引入快表后,地址的变换过程
① CPU给出逻辑地址,由某个硬件算得页号、页内偏移量,将页号与快表中的所有页号进行比较。
② 如果找到匹配的页号,说明要访问的页表项在快表中有副本,则直接从中取出该页对应的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表命中,则访问某个逻辑地址仅需一次访存即可。
③ 如果没有找到匹配的页号,则需要访问内存中的页表,找到对应页表项,得到页面存放的内存块号,再将内存块号与页内偏移量拼接形成物理地址,最后,访问该物理地址对应的内存单元。因此,若快表未命中,则访问某个逻辑地址需要两次访存注意:在找到页表项后,应同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照一定的算法对旧的页表项进行替换)

由于查询快表的速度比查询页表的速度快很多,因此只要快表命中,就可以节省很多时间
因为局部性原理,一般来说快表的命中率可以达到 90% 以上

【示例】某系统使用基本分页存储管理,并采用了具有快表的地址变换机构。访问一次快表耗时 1us,访问一次内存耗时 100us。若快表的命中率为 90%,那么访问一个逻辑地址的平均耗时是多少?
(1+100) * 0.9 + (1+100+100) * 0.1 = 111 us
有的系统支持快表和慢表同时查找,如果是这样,平均耗时应该是 (1+100) * 0.9 + (100+100) * 0.1 =110.9 us
若未采用快表机制,则访问一个逻辑地址需要 100+100 = 200us
显然,引入快表机制后,访问一个逻辑地址的速度快多了。

快表慢表先后查询和同时查询:
image-20260504134544056

(3) 局部性原理

  • 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
  • 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(因为很多数据在内存中都是连续存放的)

【示例】

int i = 0;
int a[100];
while (i < 100) {
    a[i] = i;
    i++;
}

上述代码在内存中的分布:
image-20260504134550988
这个程序执行时,会很频繁地访问 10号页面、23号页面

4.1.4 两级页表

(1) 单级页表存在的问题

  • 问题一:页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框
  • 问题二:没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面。

【示例】某计算机系统按字节寻址,支持 32 位的逻辑地址,采用分页存储管理,页面大小为4KB,页表项长度为 4B。
4KB = 212B,因此页内地址要用12位表示,剩余 20 位表示页号。
因此,该系统中用户进程最多有 220 页。相应的,一个进程的页表中,最多会有 220 = 1M = 1,048,576 个页表项,所以一个页表最大需要 220 * 4B = 222 B,共需要 222/212 = 210个页框存储该页表。
需要连续的 210 个页框空间来存储页表,这样的“连续分配”与离散分配的思想相悖。

(2) 两级页表原理和逻辑地址结构

  • 原理:将长长的页表再分页。
  • 逻辑地址结构:(一级页号,二级页号,页内偏移量),要能根据逻辑地址位数、页面大小、页表项大小 确定多级页表的逻辑地址结构。

【示例】32位逻辑地址空间,页表项大小为4B,页面大小为 4KB,则页内地址占12位,两级页表地址结构:
image-20260504134559408

  • 一级页表 = 一级页号+一级页号对应的二级页表在内存中的内存块号

(3) 两级页表如何实现地址变换
① 按照地址结构将逻辑地址拆分成三部分;
② 从PCB 中读出页目录表始址,再根据一级页号查页目录表,找到下一级页表在内存中的存放位置;
③ 根据二级页号查二级页表,找到最终想访问的内存块号;
④ 结合页内偏移量得到物理地址。

【示例】将逻辑地址 (0000000000,0000000001,111111111111) 转换为物理地址(用十进制表示)。
image-20260504134608924
最终要访问的内存块号为4
该内存块的起始地址为 4*4096 = 16384
页内偏移量为 4095
最终的物理地址为16384 + 4095= 20479

(4) 虚拟存储技术
可以在需要访问页面时才把页面调入内存(虚拟存储技术)。可以在页表项中增加一个标志位,用于表示该页面是否已经调入内存。
增加标志位后的一、二级页表:
image-20260504134614904
若想访问的页面不在内存中,则产生缺页中断(内中断/异常),然后将目标页面从外存调入内存。

(5) 多级页表

  • 若分为两级页表后,页表依然很长,则可以采用更多级页表,一般来说各级页表的大小不能超过一个页面
  • 在多级页表中,页表基址寄存器存放的是 顶级页表起始物理地址

【示例】某系统按字节编址,采用 40 位逻辑地址,页面大小为 4KB,页表项大小为 4B,假设采用纯页式存储,则要采用()级页表,页内偏移量为()位?
解析:
页面大小 = 4KB =212B,按字节编址,因此页内偏移量为12位
页号 = 40 - 12 = 28 位
页面大小 = 212B,页表项大小 = 4B ,则每个页面可存放 212 / 4 = 210 个页表项
因此各级页表最多包含 210 个页表项,需要 10 位二进制位才能映射到 210 个页表项,因此每一级的页表对应页号应为10位。总共28位的页号至少要分为三级:
image-20260504134620934

(6) 两级页表的访存次数分析(假设没有快表机构)

  • 第一次访存:访问内存中的页目录表
  • 第二次访存:访问内存中的二级页表
  • 第三次访存:访问目标内存单元
4.2 基本分段存储管理
4.2.1 分段
  • 进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址
  • 内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻
  • 写程序时使用的段名会被编译程序翻译成对应段号;单元会被编译程序翻译成段内地址。
  • 由于是按逻辑功能模块划分,用户编程更方便,程序的可读性更高。
  • 分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。如:
    image-20260504134633806
    • 段号的位数决定了每个进程最多可以分几个段
    • 段内地址位数决定了每个段的最大长度是多少

【示例】有一进程A,执行如下两行汇编代码

LOAD 1, [D] | <A>;      //将分段D中A单元内的值读入寄存器1
STORE 1, [X] | <B>;     //将寄存器1的内容存入X分段的B单元中

内存分布情况:
image-20260504134640533

4.2.2 段表

程序分多个段,各段离散地装入内存,为了保证程序能正常运行,就必须能从物理内存中找到各个逻辑段的存放位置。为此,需为每个进程建立一张段映射表,简称“段表”。

  • 每个段对应一个段表项,其中记录了该段在内存中的起始位置F(又称“基址”)和段的长度M
  • 各个段表项的长度是相同的。例如:某系统按字节寻址,采用分段存储管理,若段内地址16位,因此用16位即可表示最大段长。物理内存大小为4GB(可用32位表示整个物理内存地址空间)。因此,可以让每个段表项占 16+32 = 48位,即6B。由于段表项长度相同,因此段号可以是隐含的,不占存储空间
  • 若段表存放的起始地址为 F,则 K号段对应的段表项存放的地址为 F + K*6。

【示例】
image-20260504134647912

4.2.3 地址变换

步骤:
① 根据逻辑地址得到段号S、段内地址W;
② 判断段号是否越界。若S≥M(段表长度),则产生越界中断,否则继续执行;
③ 假设段表始址为F,查询段表,找到对应的段表项,段表项的存放地址为F+S*段表项长度;
检查段内地址是否超过段长C。若W≥C,则产生越界中断,否则继续执行;
⑤ 计算得到物理地址 = 段基址b+段内地址W;
⑥ 访问目标内存单元。

【示例】访问汇编代码LOAD 1, [D] | <A>; //将分段D中A单元内的值读入寄存器1中A单元所在的内存单元。
经过编译程序编译后,形成等价的机器指令:“取出段号为2,段内地址为 1024 的内存单元中的内容,放到寄存器1中”。
机器指令中的逻辑地址用二进制表示: 00000000000000100000000100000000。
流程图:
image-20260504134654791

4.2.4 分段、分页管理的对比
  • 信息的物理单位。分页的主要目的是为了实现离散分配,提高内存利用率。分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的

  • 信息的逻辑单位。分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。

  • 页的大小固定且由系统决定。段的长度却不固定,决定于用户编写的程序。

  • 分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址;

  • 分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址。

  • 分段比分页更容易实现信息的共享保护

  • 访问一个逻辑地址需要几次访存?

    • 分页(单级页表):第一次访存——查内存中的页表,第二次访存——访问目标内存单元。总共两次访存;
    • 分段:第一次访存——查内存中的段表,第二次访存——访问目标内存单元。总共两次访存。
      与分页系统类似,分段系统中也可以引入快表机构,将近期访问过的段表项放到快表中,这样可以少一次访问,加快地址变换速度。
4.2.5 共享段表(重点)

(1) 定义
共享段表是一种在操作系统中用于管理和记录共享段(即被多个进程共同使用的数据段或代码段)信息的表格。

(2) 功能

  • 记录共享段的详细信息,如段号、段长、内存始址等。
  • 跟踪哪些进程正在共享该段,并维护一个共享进程计数(count),以决定何时回收共享段的物理内存。
  • 控制不同进程对共享段的访问权限,确保信息的安全性和访问的合法性。

(3) 共享段表的表项内容

  • 段号:唯一标识一个共享段。由于共享段在不同进程的地址空间中的位置可以不同,故不同的进程可以使用不同的段号来共享同一个段,但这并不改变段本身的唯一性。
  • 段长:表示共享段在内存中的长度(或大小)。
  • 内存始址:指向共享段在内存中的起始地址。
  • 存在位(P):指示该共享段是否已调入内存。如果已调入内存,则存在位为1;否则为0。
  • 共享进程计数(count):记录当前正在共享该段的进程数量。当某个进程不再需要该段时,该计数会减1;当计数减至0时,表示没有进程再使用该段,系统可以回收其占用的内存。
  • 存取控制字段:用于规定不同进程对共享段的访问权限。例如,文件主可能拥有读写权限,而其他进程可能只有读权限或执行权限。
  • 进程名/进程号:记录正在共享该段的进程的名称或标识符。这有助于系统追踪哪些进程正在使用该段,并在必要时进行权限检查或内存回收。

(4) 共享段表的特点
image-20260504134706196
共享段的物理唯一性:操作系统通过让不同进程的段表项指向同一物理内存区域(段框号相同)实现共享。
② 不同的进程可以使用不同的段号来共享同一个段。
③ 不同进程的共享段的段框号是相同的。

4.3 段页式存储管理
4.3.1 分页、分段的优缺点分析
优点 缺点
分页管理 内存空间利用率高,不会产生外部碎片,只会有少量的页内碎片 不方便按照逻辑模块实现信息的共享和保护
分段管理 很方便按照逻辑模块实现信息的共享和保护 如果段长过大,为其分配很大的连续空间会很不方便。另外,段式管理会产生外部碎片

分段管理中产生的外部碎片也可以用“紧凑”来解决,只是需要付出较大的时间代价。

4.3.2 段页式管理

(1) 原理
将进程按逻辑模块分段,再将各段分页(如每个页面4KB),再将内存空间分为大小相同的内存块/页框/页帧/物理块。
image-20260504134718185

(2) 段页式管理的逻辑地址结构
段页式系统的逻辑地址结构由段号、页号、页内地址(页内偏移量)组成。如:
image-20260504134723871

  • 段号的位数决定了每个进程最多可以分几个段;
  • 页号位数决定了每个段最大有多少页;
  • 页内偏移量决定了页面大小、内存块大小是多少。

注意:“分段”对用户是可见的,程序员编程时需要显式地给出段号、段内地址。而将各段“分页”对用户是不可见的。系统会根据段内地址自动划分页号和页内偏移量。因此段页式管理的地址结构是二维的

(3) 段表、页表

  • 每个段对应一个段表项,每个段表项由段号、页表长度、页表存放块号组成。每个段表项长度相等,段号是隐含的。
  • 每个页面对应一个页表项,每个页表项由页号、页面存放的内存块号组成。每个页表项长度相等,页号是隐含的。

【举例示意图】
image-20260504134730226

4.3.3 地址变换

① 由逻辑地址得到段号、页号、页内偏移量;
② 段号与段表寄存器中的段长度比较,检查是否越界;
③ 由段表始址、段号找到对应段表项;
④ 根据段表中记录的页表长度,检查页号是否越界;
⑤ 由段表中的页表地址、页号得到查询页表,找到相应页表项;
⑥ 由页面存放的内存块号、页内偏移量得到最终的物理地址;
⑦ 访问目标单元。

【示例】
image-20260504134735835
也可引入快表机构,用段号和页号作为查询快表的关键字。若快表命中则仅需一次访存。

二、虚拟内存管理

1. 虚拟内存的基本概念

1.1 传统存储管理方式的特征
  • 一次性:作业必须一次性全部装入内存后才能开始运行。这会造成两个问题:
    ① 作业很大时,不能全部装入内存,导致大作业无法运行
    ② 当大量作业要求运行时,由于内存无法容纳所有作业,因此只有少量作业能运行,导致多道程序并发度下降
  • 驻留性:一旦作业被装入内存,就会一直驻留在内存中,直至作业运行结束。事实上,在一个时间段内,只需要访问作业的一小部分数据即可正常运行,这就导致了内存中会驻留大量的、暂时用不到的数据,浪费了宝贵的内存资源,导致内存利用率不高
1.2 局部性原理

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

  • 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
  • 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(因为很多数据在内存中都是连续存放的,并且程序的指令也是顺序地在内存中存放的)
1.3 虚拟存储器的定义和特征

(1) 定义

  • 基于局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
  • 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序,这个过程就是请求调页(或请求调段)功能。
  • 内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存,这个过程就是页面置换(段置换)功能。

这样,系统好像为用户提供了一个比实际内存容量大得多的存储器,称为虚拟存储器

(2) 特征

  • 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。这意味着程序可以在需要时逐步加载到内存中,而不是一开始就全部加载。多次性是虚拟存储器最重要的特征。
  • 对换性:在作业运行时无需一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。这允许系统在内存不足时将部分数据暂时移动到硬盘上,并在需要时再将其加载回内存。
  • 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量远大于实际的容量。虚拟内存技术使得用户能够使用比物理内存更大的内存空间,从而提高了系统的灵活性和性能。
1.4 虚拟存储器的最大容量

虚拟存储器的最大容量是由计算机的地址结构决定的,与主存容量和外存容量没有必然的联系

  • 处理器位数:处理器位数决定了CPU能够寻址的物理内存大小。例如,32位处理器可以访问的最大物理内存为4GB,而64位处理器则可以访问更大的内存空间,理论上可以达到18EB(即18亿亿字节)。
  • 计算机地址总线宽度:计算机地址总线宽度决定了CPU可以直接寻址的内存空间大小。
1.5 虚拟内存技术的实现

虚拟内存技术,允许一个作业分多次调入内存。如果采用连续分配方式,会不方便实现。因此,虚拟内存的实现需要建立在离散分配的内存管理方式基础上。
虚拟内存的实现由一下三种方式:

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

不管那种方式,都需要有一定的硬件支持,一般需要的支持有以下几个方面:

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

2. 请求分页管理方式

(1) 请求分页存储管理与基本分页存储管理的主要区别
请求分页存储管理通过请求调页功能和页面置换功能实现内存的动态分配与置换,而基本分页存储管理是静态分配

  • 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。 —— 操作系统要提供请求调页功能,将缺失页面从外存调入内存。
  • 若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。 —— 操作系统要提供页面置换的功能,将暂时用不到的页面换出外存。

(2) 页框和页面

  • 页框:将内存空间分为一个个大小相等的分区(比如:每个分区4KB),每个分区就是一个页框。每个页框有一个编号,即页框号,页框号从0开始
  • 页面:将进程的逻辑地址空间也分为与页框大小相等的一个个部分,每个部分称为一个页面 。每个页面也有一个编号,即页号,页号也是从0开始
  • 操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系。通常页框数<页数,也就是说有的页没有页框可以对应,即缺页。
  • 各个页面不必连续存放,可以放到不相邻的各个页框中。

注意:进程的最后一个页面可能没有一个页框那么大。也就是说,分页存储有可能产生内部碎片,因此页框不能太大,否则可能产生过大的内部碎片造成浪费。

2.1 页表机构

与基本分页管理相比,请求分页管理中,为了实现“请求调页”,操作系统需要知道每个页面是否已经调入内存;如果还没调入,那么也需要知道该页面在外存中存放的位置。当内存空间不够时,要实现“页面置换”,操作系统需要通过某些指标(访问次数)来决定到底换出哪个页面;有的页面没有被修改过,就不用再浪费时间写回外存。有的页面修改过,就需要将外存中的旧数据覆盖,因此,操作系统也需要记录各个页面是否被修改的信息。为此在请求页表项中增加了4个字段:
image-20260504134805711

  • 状态位P:标记该页是否已被调入内存,供程序访问时参考;
  • 访问字段A:记录最近被访问过几次,或记录上次访问的时间,供置换算法选择换出页面时参考;
  • 修改位:页面调入内存后是否被修改过;
  • 外存地址:页面在外存中的存放位置。

注意:其中页号是隐含的;内存块号又称页框号。

2.2 缺页中断机构

定义:缺页中断(Page Fault)是指当程序试图访问已映射在虚拟地址空间中,但是并未被加载在物理内存中的一个分页时,由中央处理器的内存管理单元(MMU)所发出的中断。缺页中断机构负责处理这种中断,将缺失的页面从磁盘等辅助存储器中调入内存,以满足程序的访问需求。

工作流程:

① 产生中断:当程序要访问的页面不在内存时,MMU会发出缺页中断信号。
② 保护CPU现场:操作系统在接收到缺页中断信号后,会首先保护CPU的现场,包括程序计数器、通用寄存器等状态信息。
③ 分析中断原因:操作系统通过分析中断原因,确定需要哪个虚拟页面,并检查是否有空闲的页框。
④ 页面置换:如果内存中没有空闲页框,操作系统会根据页面置换算法选择一个页面进行淘汰。如果被淘汰的页面在内存期间被修改过,则需要将其写回磁盘。
⑤ 页面调入:操作系统从磁盘上找到所需页面的地址,通过磁盘操作将其装入内存中的空闲页框。
⑥ 恢复CPU现场:页面调入后,操作系统会恢复CPU的现场,包括程序计数器、通用寄存器等状态信息,然后继续执行被中断的程序。

缺页中断与一般中断的区别:

  • 缺页中断是因为当前执行的指令想要访问的目标页面未调入内存而产生的,因此属于内中断
  • 一条指令在执行期间,可能产生多次缺页中断
2.3 地址变换机构(具有快表结构)

大致步骤如下:

① 逻辑地址分解:首先,将逻辑地址分解为页号和页内偏移量;
② 先快表检索:若命中,则从相应表项中取出该页的物理块号,并修改页表项中的访问位,以供置换算法换出页面时参考。对于“写指令”,还需要将修改位置为1
③ 若快表未命中,则要到页表中查找,若找到,则从相应表项中取出物理块号,并将该页表项写入快表,若快表已满,则需采用某种算法替换;
④ 若在页表中未找到,则需要进行缺页中断处理,请求系统将该页从外存换入内存,页面被调入内存后,由操作系统负责更新页表和快表,并获得物理块号;
⑤ 利用得到的物理块号和页内地址拼接形成物理地址,用该地址去访存。

流程图:
image-20260504134819863

在具有快表机构的请求分页系统中,访问一个逻辑地址时,若发生缺页,则地址变换步骤是:查快表(未命中)——查慢表(发现未调入内存)——调页(调入的页面对应的表项会直接加入快表)——查快表(命中)——访问目标内存单元

3. 页框分配与回收

3.1 驻留集

驻留集:指请求分页存储管理中给进程分配的物理块(页框)的集合。需要考虑以下两点:

  • 若驻留集太小,会导致缺页频繁,系统要花大量的时间来处理缺页,实际用于进程推进的时间很少;
  • 驻留集太大,又会导致多道程序并发度下降,资源利用率降低。

所以应该选择一个合适的驻留集大小。在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小

3.2 内存分配策略
  • 在请求分页系统中,可采取两种内存分配策略:固定可变分配策略。
    • 固定分配:操作系统为每个进程分配一组固定数目的物理块,在进程运行期间不再改变。即驻留集大小不变;
    • 可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可根据情况做适当的增加或减少。即驻留集大小可变。
  • 在进行置换时,也可采取两种置换策略:全局局部置换。
    • 局部置换:发生缺页时只能选进程自己的物理块进行置换。
    • 全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程。

全局置换意味着一个进程拥有的物理块数量必然会改变,因此不可能是固定分配,于是可组合出下面三种适用的策略。

(1) 固定分配局部置换
系统为每个进程分配一定数量的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后再调入需要的页面。
这种策略的缺点是:很难在刚开始就确定应为每个进程分配多少个物理块才算合理。(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)

(2) 可变分配全局置换
刚开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时,从空闲物理块队列中取出一块分配给该进程;若已无空闲物理块,则可选择一个未锁定的页面换出外存,再将该物理块分配给缺页的进程。
采用这种策略时,只要某进程发生缺页,都将获得新的物理块,仅当空闲物理块用完时,系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程中的页,因此这个被选中的进程拥有的物理块会减少,缺页率会增加

(3) 可变分配局部置换
刚开始会为每个进程分配一定数量的物理块。当某进程发生缺页时,只允许从该进程自己的物理块中选出一个进行换出外存。如果进程在运行中频繁地缺页,系统会为该进程多分配几个物理块,直至该进程缺页率趋势适当程度;反之,如果进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块。

对比:
可变分配全局置换:只要缺页就给分配新物理块
可变分配局部置换:要根据发生缺页的频率来动态地增加或减少进程的物理块

3.3 物理块调入算法

采用固定分配策略时,将系统中的空闲物理块分配给各个进程,可采用下述几种该算法:

  • 平均分配算法:将系统中所有可供分配的物理块平均分配给各个进程。
  • 按比例分配算法:根据进程的大小按比例分配物理块。
  • 优先权分配算法:为重要和紧迫的进程分配较多的物理块。

通常采取的方法是将所有可分配的物理块分成两部分:一部分按比例分配给各个进程,一部分则根据优先权分配

3.4 调入页面的时机

为确定系统将进程运行时所缺的页面调入内存的时机,可采用以下两种调页策略:

  • 预调页策略:根据局部性原理,一次调入若干个相邻的页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过,则又是低效的。因此可以预测不久之后可能访问到的页面,将它们预先调入内存,但目前预测成功率只有50%左右。故这种策略主要用于进程的首次调入,由程序员指出应该先调入哪些部分。调入时机:运行前调入
  • 请求调页策略:进程在运行期间发现缺页时才将所缺页面调入内存。由这种策略调入的页面一定会被访问到,但由于每次只能调入一页,而每次调页都要磁盘I/O操作,因此I/O开销较大。调入时机:运行时调入
3.5 从何处调入页面

(1) 系统拥有足够的对换区空间
页面的调入、调出都是在内存与对换区之间进行,这样可以保证页面的调入、调出速度很快。在进程运行前,需将进程相关的数据从文件区复制到对换区。
image-20260504134848379

(2) 系统缺少足够的对换区空间
凡是不会被修改的数据都直接从文件区调入,由于这些页面不会被修改,因此换出时不必写回磁盘,下次需要时再从文件区调入即可。对于可能被修改的部分,换出时需写回磁盘对换区,下次需要时再从对换区调入。
image-20260504134853482

(3) UNIX 方式
运行之前进程有关的数据全部放在文件区,故未使用过的页面,都可从文件区调入。若被使用过的页面需要换出,则写回对换区,下次需要时从对换区调入。
image-20260504134859305

4. 页面置换算法

页面的换入、换出需要磁盘I/O,会有较大的开销,因此好的页面置换算法应该追求更少的缺页率
页面置换算法有以下5种:

  • 最佳置换算法(OPT)
  • 先进先出置换算法(FIFO)
  • 最近最久未使用置换算法(LRU)
  • 时钟置换算法(CLOCK)
  • 改进型的时钟置换算法
4.1 最佳置换算法(OPT)
  • 最佳置换算法(OPT,Optimal):每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。
  • 最佳置换算法可以保证最低的缺页率,但实际上,只有在进程执行的过程中才能知道接下来会访问到的是哪个页面。操作系统无法提前预判页面访问序列。因此,最佳置换算法是无法实现的

【示例】:假设系统为某进程分配了三个内存块,并考虑到有以下页面号引用串(会依次访问这些页面):7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1
解析:
访问页面时缺页情况和置换情况如下表:
image-20260504134914643
以标红的列为例:选择从0,1,7中淘汰一页。按最佳置换的规则,往后寻找,最后一个出现的页号就是要淘汰的页面,所以淘汰7。

整个过程缺页中断发生了9次,页面置换发生了6次。
注意:缺页时未必发生页面置换。若还有可用的空闲内存块,就不用进行页面置换。
缺页率 = 9/20 = 45%

4.2 先进先出置换算法(FIFO)
  • 先进先出置换算法(FIFO):每次选择淘汰的页面最早进入内存的页面
  • 实现方法:把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择队头页面即可。
  • 队列的最大长度取决于系统为进程分配了多少个内存块。

【示例】假设系统为某进程分配了三个内存块,并考虑到有以下页面号引用串:3, 2, 1, 0, 3, 2, 4, 3, 2, 1, 0, 4
解析:
访问页面时缺页情况和置换情况如下表:
image-20260504134923363
分配三个内存块时,缺页次数:9次

假设系统为某进程分配了四个内存块,页面号引用串不变:
访问页面时缺页情况和置换情况如下表:
image-20260504134927873
分配四个内存块时,缺页次数:10次

Belady 异常:当为进程分配的物理块数增多时,缺页次数不减反增的异常现象
只有 FIFO 算法会产生 Belady 异常。另外,FIFO算法虽然实现简单,但是该算法与进程实际运行时的规律不适应,因为先进入的页面也有可能最经常被访问。因此,算法性能差

4.3 最近最久未使用置换算法(LRU)
  • 最近最久未使用置换算法(LRU,least recently used):每次淘汰的页面最近最久未使用的页面
  • 实现方法:赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的时间t
  • 当需要淘汰一个页面时,选择现有页面中 t 值最大的,即最近最久未使用的页面。这就涉及排序,对于置换算法而言,开销太大,耗费高
  • 该算法的实现需要专门的硬件支持,虽然算法性能好,但是实现困难,开销大

【示例】假设系统为某进程分配了四个内存块,并考虑到有以下页面号引用串:1, 8, 1, 7, 8, 2, 7, 2, 1, 8, 3, 8, 2, 1, 3, 1, 7, 1, 3, 7
解析:
访问页面时缺页情况和置换情况如下表:
image-20260504134938561

在手动做题时,若需要淘汰页面,可以逆向检查(从后往前)此时在内存中的几个页面号。在逆向扫描过程中最后一个出现的页号就是要淘汰的页面。

4.4 时钟置换算法(CLOCK)
  • 最佳置换算法性能最好,但无法实现;先进先出置换算法实现简单,但算法性能差;最近最久未使用置换算法性能好,是最接近OPT算法性能的,但是实现起来需要专门的硬件支持,算法开销大。
  • 时钟置换算法是一种性能和开销较均衡的算法,又称CLOCK算法,或最近未用算法(NRU,Not Recently Used)
  • 简单的CLOCK 算法实现方法:为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需检查页的访问位。如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK 算法选择一个淘汰页面最多会经过两轮扫描)。

【示例】假设系统为某进程分配了五个内存块,并考虑到有以下页面号引用串:1, 3, 4, 2, 5, 6, 3, 4, 7
解析:
以到页面号6为例子:
初始状态:
image-20260504134948212
由于五个页面的访问位都为1,所以第一轮扫描将所有访问位置为0:
image-20260504134952891
第二轮扫描开始,扫描到1号页的访问位为0,替换为6号页,并将6号页的访问位置为1:
image-20260504135602096

4.5 改进型的时钟置换算法(重点)
  • 简单的时钟置换算法仅考虑到一个页面最近是否被访问过。事实上,如果被淘汰的页面没有被修改过,就不需要执行I/O操作写回外存。只有被淘汰的页面被修改过时,才需要写回外存
  • 因此,除了考虑一个页面最近有没有被访问过之外,操作系统还应考虑页面有没有被修改过。在其他条件都相同时,应优先淘汰没有修改过的页面,避免I/O操作。这就是改进型的时钟置换算法的思想。
  • 修改位=0,表示页面没有被修改过;修改位=1,表示页面被修改过
  • 为方便讨论,用(访问位,修改位)的形式表示各页面状态。如(1,1)表示一个页面近期被访问过,且被修改过。

算法规则:将所有可能被置换的页面排成一个循环队列

  • 第一轮:从当前位置开始扫描到第一个(0, 0)的帧用于替换。本轮扫描不修改任何标志位;(第一优先级:最近没访问,且没修改的页面
  • 第二轮:若第一轮扫描失败,则重新扫描,查找第一个(0, 1)的帧用于替换。本轮将所有扫描过的帧访问位设为0;(第二优先级:最近没访问,但修改过的页面
  • 第三轮:若第二轮扫描失败,则重新扫描,查找第一个(0, 0)的帧用于替换。本轮扫描不修改任何标志位;(第三优先级:最近访问过,但没修改的页面
  • 第四轮:若第三轮扫描失败,则重新扫描,查找第一个(0, 1)的帧用于替换。(第四优先级:最近访问过,且修改过的页面

由于第二轮已将所有帧的访问位设为0,因此经过第三轮、第四轮扫描一定会有一个帧被选中,因此改进型CLOCK置换算法选择一个淘汰页面最多会进行四轮扫描

5. 内存映射文件

(1) 内存映射文件定义和作用
内存映射文件:操作系统向上层程序员提供的功能(系统调用)。
作用:

  • 方便程序员访问文件数据;
  • 方便多个进程共享同一个文件。

(2) 传统的文件访问方式

  • open 系统调用——打开文件
  • seek 系统调用——将读写指针移到某个位置
  • read 系统调用——从读写指针所指位置读入若干数据(从磁盘读入内存)
  • write 系统调用——将内存中的指定数据,写回磁盘(根据读写指针确定要写回什么位置)

(3) 内存映射文件的访问方式

  • open 系统调用——打开文件
  • mmap 系统调用——将文件映射到进程的虚拟地址空间
    image-20260504135015975

优点:

  • 以访问内存的方式访问文件数据;
  • 文件数据的读入、写出由操作系统自动完成;
  • 进程关闭文件时,操作系统自动将文件被修改的数据写回磁盘。

多个进程可以映射同一个文件,实现共享
image-20260504135022978
在物理内存中,一个文件对应同一份数据,当一个进程修改文件数据时,另一个进程可以立马“看到”。

6. 虚拟存储器性能的影响因素及改进方法

  • 缺页率是影响虚拟存储器性能的主要因素,而缺页率又受到如下因素的影响:
    ① 页面大小
    ② 分配给进程的物理块数(驻留集、工作集大小)
    ③ 页面置换算法
    ④ 程序的编制方法(程序局部性原理)
  • 写回磁盘的频率。换出已修改过的页面时,应当写回磁盘,如果每当一个页面被换出就将它写回磁盘,那么每换出一个页面就需要启动一次磁盘,效率极低。为此在系统中建立一个己修改换出页面的链表,对每个要被换出的页面(已修改),可以暂不将它们写回磁盘,而将它们挂在该链表上,仅当被换出页面数达到给定值时,才将它们一起写回磁盘,这样就可显著减少磁盘I/ O的次数,即减少已修改页面换出的开销。此外,若有进程在这批数据还未写回磁盘时需要再次访问这些页面,则不需从外存调入,而直接从已修改换出页面链表上获取,这样也可以减少页面从磁盘读入内存的频率,减少页面换进的开销。

7. 地址翻译(重点)

结合“计算机组成原理”的Cache部分。来说明虚实地址的变换。
地址翻译流程图
简略版:
image-20260504135035858

完整版:(注意,TLB和Cache是组相联的):
image-20260504135043425

注意:在访问Cache之前CPU已经完成逻辑地址向物理地址的转换。
改正:在上图中Cache缺失时,将对应主存块送到Cache的同时也送给了CPU,故本次的存取不需要再从Cache那里存取数据了。

【示例】设某系统满足以下条件:

  • 有一个TLB与一个data Cache。TLB存储的是虚拟地址, data Cache存储的是物理地址
  • 存储器以字节为编址单位
  • 虚拟地址14位
  • 物理地址12位
  • 页面大小为64B
  • TLB为四路组相联,共有16个条目
  • data Cache是物理寻址、直接映射的,行大小为4B,共有16组

问:写出访问地址为0x03d4,0x00f1和0x0229的过程。

解析:
本系统以字节编址,页面大小为64B,则页内偏移地址为log2(64B/1B) = 6位,所以虚拟页号为14-6 = 8位,物理页号为12-6 = 6位。
TLB为四路组相联(四个存储块为一组),共有16个条目,则TLB共有16/4 = 4组,因此虚拟页号中低log24 = 2位就为组索引,高6位就为TLB标记(标记Tag是用于区分不同内存块的关键标识)。
Cache行大小为4B,因此物理地址中低log24 = 2位为块偏移,Cache共有16组,可知接下来log216 = 4位为组索引,剩下高6位作为标记。

地址结构如下图:
image-20260504135111842
TLB、页表、data Cache内容如表3.1、表3.2及表3.3所示:
image-20260504135117685
image-20260504135123583
先将十六进制的虚拟地址0x03d4,0x00f1和0x0229转化为二进制形式,如表3.4所示:
image-20260504135131747
得到每个地址的组索引和 TLB 标记,接下来就要找出每个地址的页面在不在主存中,若在主存中,则还要找出物理地址。

  • 对于 0x03d4,组索引为 3,TLB 标记为 0x03,査TLB,第3组中正好有标记为 03 的项,有效位为 1,可知页面在主存中,对应的物理页号为0d(001101),再拼接页内地址 010100,可得物理地址为0x354(001101010100)。
  • 对于 0x00f1,组索引为3,TLB 标记为 0x00,查 TLB,第3组中没有标记为 00 的项,再去找页表,虚拟页号为 0x03,页表第3行的有效位为1,可知页面在主存中,物理页号为 02(000010),再拼接页内地址 110001,可得物理地址为0x0b1(000010110001)。
  • 对于 0x0229,组索引为 0,TLB 标记为 0x02,查 TLB,第0组中没有标记为 02 的项,再去找页表,虚拟页号为 0x08,页表第8行的有效位为0,页面不在主存中,产生缺页中断。

找出在主存中的页面的物理地址后,就要通过物理地址访问数据,接下来要找该物理地址的内容在不在 Cache 中,物理地址结构如表 3.5 所示:
image-20260504135137833

  • 对于 0x354,Cache 索引为5,Cache 标记为 0x0d,对照 Cache 中索引为5的行,标记正好为有效位为 1,可知该块在 Cache 中,偏移 0,即块0,可得虚拟地址 0x03d4 的内容为 36H。
  • 对于 0x0b1,Cache 索引为c,Cache 标记为 0x02,对照 Cache 中索引为c的行,有效位为 0,可知该块不在 Cache 中,要去主存中査找物理页号为 2、偏移为 0x31 的内容。

以上例子基本覆盖了从虚拟地址到 Cache 查找内容的所有可能出现的情况,读者务必要掌握此节的内容,查找顺序是从 TLB到页表(TLB不命中),再到Cache和主存,最后到外存。

8. 考点总结

(1)在固定页框的情况下,进行页面置换只是要进行置换的页框的内容及其一些标记位改变了,该页框的页框号不会改变;此外,页表中与该页框对应的页号也发生了改变。

【示例】某请求分页存储系统的页大小为4KB,按字节编址。系统给进程P分配2个固定的页框,并采用改进型Clock置换算法,进程P页表的部分内容见下表。
image-20260504135153348
若P访问虚拟地址为02A01H的存储单元,则经地址变换后得到的物理地址是()。
A. 00A01H    B. 20A01H    C. 60A01H    D.80A01H

解析:
页面大小为4KB,低12位是页内偏移。虚拟地址为02A01H,页号为02H,02H页对应的页表项中存在位为0,进程P分配的页框固定为2,且内存中已有两个页面存在。根据Clock算法,选择将3号页换出,将2号页放入60H页框,经过地址变换后得到的物理地址是60A01H。

(2) 每个进程的地址空间、页目录、页目录基地址寄存器(PDBR)的内容都是一一对应的关系。

(3) 页表本质上也是由若干个页记录的数据,自身由若干个页构成的,所以也需要由若干个页表项记录这些页和页框的映射关系,这些页表项是存放在页表自身的。简单点说就是页表本身也是页,也需要记录其映射关系。就像班长虽然会记录班上所有同学的信息,但因为他自己也是一个同学,所以他也需要记录自己的信息。

【示例】某计算机按字节编址,采用页式虚拟存储管理方式,虚拟地址和物理地址的长度均为32位,页表项的大小为4字节,页大小为4MB,虚拟地址结构如下。
image-20260504135159565
进程P的页表起始虚拟地址为B8C00000H,被装载到从物理地址65400000H开始的连续主存空间中。请回答下列问题,要求答案用十六进制表示。
(1)CPU在执行进程P的过程中,访问虚拟地址12345678H时发生了缺页异常,经过缺页异常处理和MMU地址转换后得到的物理地址是BAB45678H。在此次缺页异常处理过程中,X需要为所缺页分配页框并更新相应的页表项,该页表项的虚拟地址和物理地址分别是什么?该页表项中的页框号更新后的值是什么?
(2)进程P的页表所在页的页号是什么?该页对应的页表项的虚拟地址是什么?该页表项中的页框号是什么?

解析:
(1)页表项的虚拟地址为B8C00000H+48H<<2=B8C00120H。页表项的物理地址为65400000H+48H<<2=65400120H。相应页表项中的页框号为BAB45678H222EAH。

(2)进程P的页表所在页的页号为B8C00000H>>22=2E3H。页表项的虚拟地址为B8C00000H+2E3H<<2=B8C00B8CH。页表项中的页框号为65400000H>>22=195H。

注意,符号“<<”表示左移操作,符号“>>”表示右移操作。将二进制数左移1位,与“乘以2”的效果等价。页表项中的页框号为物理地址的前10位可以通过右移22位得到。

(4) 页表相关计算

  • 总页数=虚拟空间大小/页大小
  • 总页框数=物理空间大小/页大小
  • 页表项大小=log2(总页框数)+[有效位,控制位…]
  • 页表大小=页表项大小×页表项个数=页表项大小×总页数
  • 页表所占据页数=页表大小/页大小
  • 页内地址位数d=log2(页大小/编址单元大小)
  • 虚拟地址位数n=log2(虚拟空间大小/编址单元大小)
  • 物理地址位数n=log2(物理空间大小/编址单元大小)
  • 页内偏移量=虚拟地址后d位=物理地址后d位
    image-20260504135207817
  • 页号=虚拟地址>>d,即虚拟地址的前n-d位
  • 页框号=物理地址>>d,即物理地址的前m-d位
重点

(1) 求页表项的虚拟地址和物理地址

  • 虚拟地址(进程虚拟内存中的位置):起始虚拟地址+页号×页表项大小。
  • 物理地址(内存中的位置):起始物理地址+页号×页表项大小。

注意:页表在虚拟地址空间和物理地址空间中均连续存储

(2) 求页表所在页的页号和页框号
image-20260504135207817

  • 页号(进程虚拟内存中的位置):起始虚拟地址中的前a位。
  • 页框号(内存中的位置):起始物理地址中的前b位。

注:页表本身是进程虚拟地址空间中的一个页(因为页表可能跨页,一般题目说“页表所在页”,即页表起始地址所在的页)。

【2024统考真题】某计算机按字节编址,采用页式虚拟存储管理方式,虚拟地址和物理地址的长度均为32位,页表项的大小为4字节,页大小为4MB,虚拟地址结构如下。
image-20260504135220013
进程P的页表起始虚拟地址为B8C00000H,被装载到从物理地址65400000H开始的连续主存空间中。请回答下列问题,要求答案用十六进制表示。
(1)CPU在执行进程P的过程中,访问虚拟地址12345678H时发生了缺页异常,经过缺页异常处理和MMU地址转换后得到的物理地址是BAB45678H。在此次缺页异常处理过程中,X需要为所缺页分配页框并更新相应的页表项,该页表项的虚拟地址和物理地址分别是什么?该页表项中的页框号更新后的值是什么?
(2)进程P的页表所在页的页号是什么?该页对应的页表项的虚拟地址和物理地址是什么?该页表项中的页框号是什么?

解析:
(1)步骤:
① 先拆分虚拟地址12345678H的页号§虚拟地址结构:高10位=页号§,低22位=页内偏移(W)。将12345678H转换为32位二进制:12345678H = 0001 0010 0011 0100 0101 0110 0111 1000B取高10位:0001001000B=48H。
② 计算页表项的虚拟地址
页表在虚拟地址空间中连续存储,起始虚拟地址为B8C00000H,每个页表项占4字节。第k个页表项(对应页号k)的虚拟地址为:页表起始虚拟地址+k×页表项大小
代入k=48H:B8C00000H + 48H x 4 = B8C00000H + 120H = B8C00120H
③ 计算页表项的物理地址
页表被装载到连续的物理内存,起始物理地址为65400000H,每个页表项占4字节。第k个页表项的物理地址为:页表起始物理地址+k×页表项大小
代入k=48H:65400000H + 48H × 4 = 65400000H + 120H = 65400120H
④ 计算页表项中的页框号
题目给出转换后的物理地址为BAB45678H,需从该地址中提取高10位作为页框号,1010101101B=2E3H

(2)步骤:
① 计算页表所在页的页号
页表本身是进程虚拟地址空间中的一个页(因为页表可能跨页,但题目说“页表所在页”,即页表起始地址所在的页)。
页表起始虚拟地址为B8C00000H,可以通过右移22位得到页号。进程P的页表所在页的页号等于B8C0 0000H的前10位,即101110 0011B=2E3H。
② 计算页表对应的页表项的虚拟地址
第k个页表项的虚拟地址为:页表起始虚拟地址+k×页表项大小
代入k=2E3H:B8C00000H + 2E3H x 4 = B8C00000H + B8CH = B8C00B8CH
③ 计算页表对应的页表项的物理地址
第k个页表项的物理地址为:页表起始物理地址+k×页表项大小
代入k=2E3H:65400000H + 2E3H × 4 = 65400000H + B8CH = 65400B8CH
④ 计算页表对应的页表项中的页框号
页表被装载到从物理地址65400000H开始的连续主存空间中,需从该地址中提取高10位作为页框号,011001 0101B=195H。

三、本章疑难点

1. 分页管理方式和分段管理方式

分页管理方式和分段管理方式在很多地方是相似的,比如在内存中都是不连续的、都有地址变换机构来进行地址映射等。但两者也存在许多区别。
分页管理方式 vs 分段管理方式

分 页 分 段
目的 分页仅是系统管理上的需要,是为实现离散分配方式,以提高内存的利用率。而不是用户的需要 段是信息的逻辑单位,它含有一组意义相对完整的信息。分段的目的是能更好地满足用户的需要
长度 页的大小固定且由系统决定,由系统将逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的 段的长度不固定,决定于用户所编写的程序,通常由编译程序在编译时根据信息的性质来划分
地址空间 分页的程序地址空间是一维的,即单一的线性地址空间,程序员利用一个记忆符即可表示一个地址 分段的程序地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址
碎片 有内部碎片,无外部碎片 有外部碎片,无内部碎片

2. 操作系统的内存管理 vs 计组的存储器

特性 操作系统的内存管理 计组的存储器
目标 虚拟化物理内存,为应用程序提供统一、安全的内存访问接口,并高效分配资源 描述存储器硬件的物理设计与工作方式,包括存储介质、访问时序和接口
抽象层级 逻辑层(虚拟内存、进程视角) 物理层(电路、信号、硬件接口)
核心问题 如何分配、保护、扩展内存资源 如何设计高速、大容量、低延迟的存储设备
关键技术 分页、分段、页面置换算法 存储器电路、总线时序、缓存一致性
用户可见性 对开发者透明(通过系统调用/API) 对用户完全隐藏(硬件黑盒)
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐