【学习笔记】计算机操作系统(四)—— 存储器管理
第四章 存储器管理
文章目录
- 第四章 存储器管理
- 4.1 存储器的层次结构
- 4.1.1 多层结构的存储器系统
- 4.1.2 主存储器与寄存器
- 4.1.3 高速缓存和磁盘缓存
- 4.2 程序的装入和链接
- 4.2.1 程序的装入
- 4.2.2 程序的链接
- 4.3 连续分配存储管理方式
- 4.3.1 单一连续分配
- 4.3.2 固定分区分配
- 4.3.3 动态分区分配
- 4.3.4 基于顺序搜索的动态分区分配算法
- 4.3.5 基于索引搜索的动态分区分配算法
- 4.3.6 动态可重定位分区分配
- 4.4 对换(Swapping)
- 4.4.1 多道程序环境下的对换技术
- 4.4.2 对换空间的管理
- 4.4.3 进程的换出与换入
- 4.5 非连续分配 - 分页存储管理方式
- 4.5.1 分页存储管理的基本方法
- 4.5.2 地址变换机构
- 4.5.3 访问内存的有效时间
- 4.5.4 两级和多级页表
- 4.5.5 反置页表(Inverted Page Table)
- 4.6 非连续分配 - 分段存储管理方式
- 4.6.1 分段存储管理方式的引入
- 4.6.2 分段系统的基本原理
- 4.6.3 信息共享
- 4.7 非连续分配 - 段页式存储管理方式
4.1 存储器的层次结构
内存:内存可存放数据。程序执行前需要先放到内存中才能被CPU处理
——缓和CPU与硬盘之间的速度矛盾
存储单元:内存被划分成的一个个小的、可独立寻址的空间,每个单元有唯一的地址并能存储固定大小的数据
内存地址:从0开始,每个地址对应一个存储单元
“按字节编址”
,则每个存储单元大小为 1字节,即 1B,即 8个二进制位
“按字编址”
,则每个存储单元大小为 1个字;1K = 210 ; 1M = 220 ; 1G = 230 ;
逻辑地址/相对地址:相对于进程的起始地址
而言的地址【程序经过编译、链接后生成的指令中指明的是逻辑地址】
物理地址/绝对地址:内存硬件实际使用的唯一地址
,直接对应内存芯片上的物理存储单元
操作系统与内存 :
-
操作系统负责
内存空间的分配与回收
-
操作系统需要提供某种技术从
逻辑上对内存空间进行扩充
-
操作系统需要提供
地址转换功能
,负责程序的逻辑地址与物理地址的转换 -
操作系统需要提供
内存保护功能
。保证各进程在各自存储空间内运行,互不干扰-
方法一: 在CPU中设置一对
上、下限寄存器
,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界 -
方法二: 采用
重定位寄存器(又称基址寄存器)
和界地址寄存器(又称限长寄存器)
进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址。
-
进程映像
覆盖技术:
-
目的:
解决“程序大小超过物理内存总和”的问题
-
思想:
- 将
程序分为多个段(多个模块)
;常用的段常驻内存,不常用的段在需要时调入内存, 内存
中分为一个“固定区”
和若干个“覆盖区”
- 需要
常驻内存的段放在“固定区”
中,调入后就不再调出(除非运行结束);不常用的段放在“覆盖区”
,需要用到时调入内存用不到时调出内存
- 将
交换/对换技术:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度
)
4.1.1 多层结构的存储器系统
1、存储器的多层结构
对于通用计算机而言,存储层次至少应具有三级
: 最高层为CPU 寄存器
,中间为主存
,最底层是辅存
。
在较高档的计算机中,还可以根据具体的功能细分
为寄存器
、高速缓存
、主存储器
、磁盘缓存
、固定磁盘
、可移动存储介质
等6层
。
在存储层次中,层次越高(越靠近CPU),存储介质的访问速度越快,价格也越高,相对所配置的 存储容量也越小。
寄存器、高速缓存、主存储器和磁盘缓存
均属于 操作系统存储管理 的管辖范畴,掉电后存储的信息不再存在
。
固定磁盘和可移动存储介质
则属于 *设备管理 *的管辖范畴,存储的信息将被长期保存
。
2、可执行存储器
寄存器
和主存储器
又被称为可执行存储器
。
进程可以在很少的时钟周期内使用一条load 或 store 指令对可执行存储器进行访问
。
对辅存的访问
需要通过I/O设备实现
【涉及到中断、设备驱动程序以及物理设备的运行】,所需耗费的时间远远高于访问可执行存储器的时间。
4.1.2 主存储器与寄存器
1、主存储器
主存储器简称内存或主存
,用于保存进程运行时的程序和数据
,也称可执行存储器
。
通常,处理机都是从主存储器中取得指令和数据的
,并将其所取得的指令放入指令寄存器中,而将其所读取的数据装入到数据寄存器中; 或者反之将寄存器中的数据存入到主存储器
。
主存储器访问速度远低于 CPU执行指令的速度
,为缓和这一矛盾,在计算机系统中引入了寄存器和高速缓存。
2、寄存器
寄存器具有与处理机相同的速度
,故对寄存器的访问速度最快,完全能与CPU协调工作,但价格昂贵。
4.1.3 高速缓存和磁盘缓存
1、高速缓存
介于寄存器和存储器之间的存储器
,主要用于备份主存中较常用的数据
,以减少处理机对主存储器的访问次数
,可大幅度地提高程序执行速度
。- 高速缓存
容量远大于寄存器
,而比内存小
,访问速度快于主存储器
。 - 通常,进程的程序和数据存放在主存储器中,每当要访问时,才被临时复制到一个速度较快的高速缓存中。当CPU访问一组特定信息时,须首先检查它是否在高速缓存中,如果已存在,便可直接从中取出使用,以避免访问主存,否则,就须从主存中读出信息。
2、磁盘缓存
磁盘的 I/O速度远低于对主存的访问速度
,为了缓和两者之间在速度上的不匹配,而设置了磁盘缓存。- 用于
暂时存放频繁使用的一部分磁盘数据和信息
,以减少访问磁盘的次数
。 - 它本身并
不是一种实际存在的存储器
,而是利用主存中的部分存储空间暂时存放从磁盘中读出(或写入)的信息
。
4.2 程序的装入和链接
4.2.1 程序的装入
在将一个装入模块装入内存
时,可以有如下三种装入方式:【以下是无需进行链接的单个目标模块的装入过程】
1、绝对装入方式(Absolute Loading)
适用场景
单道程序系统
:计算机系统仅能运行单个程序,且内存分配固定。确定的内存位置
:已知程序始终驻留在内存的固定区域(如起始地址为R
)。
绝对地址(物理地址)
编译/汇编时直接生成的目标代码中
,地址为 真实的内存地址(如 0x5000
)。
例:已知程序从地址 R
开始,编译后所有地址基于 R
生成(如变量地址为 R+0x100
)。
时机:在编译时,逻辑地址就转换成物理地址
过程
已知用户程序(进程)驻留在从R处开始的位置
- 编译程序所产生的
目标模块(即装入模块 ---- 包含绝对地址)
,便可从R处开始向上扩展
。 - 绝对装入程序
按照装入模块中的地址,将程序和数据装入内存
。 程序中的逻辑地址与实际内存地址完全一致
则无需重定位。
为什么绝对装入不适合多道程序?
→ 多程序需共享内存,固定地址会导致冲突。符号地址的作用
→ 解耦逻辑与物理地址,通过编译阶段转换,提升可维护性
2、可重定位装入方式(Relocation Loading Mode)
适用场景
多道程序环境
:多个程序共享内存,编译时无法预知程序的实际加载位置。动态内存分配
:程序需灵活装入内存的任意空闲区域,而非固定地址。
逻辑地址(相对地址)
目标模块从地址 0
开始编址,程序内所有地址均为相对于起始地址的偏移量。
物理地址 = 逻辑地址 + 程序起始地址
静态重定位
- 时机:程序
装入内存时
由装入器(Loader)一次性完成地址转换
。 - 特点:重定位后地址固定不变,
运行时无需再次转换
。
过程
- 编译阶段:生成从
0
开始编址的目标模块(逻辑地址)。 - 装入阶段:
- 分配内存空间(如起始地址
10000
)。 - 装入器遍历目标模块,将所有逻辑地址调整为物理地址(如
2500 → 12500
)。
- 分配内存空间(如起始地址
- 执行阶段:CPU 直接使用重定位后的物理地址访问内存。
3、动态运行时的装入方式(Dynamic Run-time Loading)
适用场景
需要动态内存管理的系统
:允许程序运行时被移动或换出/换入内存(如具有对换功能的系统)。现代多道程序环境
:支持虚拟内存、进程调度和内存共享的高级操作系统(如Linux、Windows)。
动态重定位(Dynamic Relocation)
-
时机:地址转换推迟到
程序执行时进行
(而非装入时)。 -
实现方式:通过 硬件支持(
重定位寄存器: 存放装入模块存放的起始位置
) 实时计算物理地址。物理地址 = 逻辑地址 + 重定位寄存器值
-
特点:
- 程序
装入后,内存中的地址仍为逻辑地址
。 CPU每次访问内存时
,由MMU(内存管理单元)自动完成地址转换
。- 若进程被换出后换入到新位置,只需更新重定位寄存器,无需修改程序内的地址。
- 程序
特性 | 绝对装入 | 静态重定位 | 动态重定位 |
---|---|---|---|
地址转换时机 | 编译时 | 装入时 | 执行时 |
地址绑定方式 | 绝对物理地址 | 装入时根据起始地址修正 | 运行时通过重定位寄存器转换 |
进程移动支持 | ❌ 不可移动(固定地址) | ❌ 不可移动(装入后固定) | ✔️ 可换入/换出(动态更新基址) |
硬件需求 | ❌ 无需特殊硬件 | ❌ 无需特殊硬件 | ✔️ 需重定位寄存器(或MMU) |
内存灵活性 | ❌ 只能装入指定内存区域 | ✔️ 可装入任意空闲区域 | ✔️ 支持动态内存分配与共享 |
适用系统 | 单道程序、嵌入式系统 | 早期多道批处理系统 | 现代多任务操作系统(如Linux/Windows) |
典型场景 | 裸机程序、ROM固化代码 | DOS时代程序 | 进程对换、虚拟内存系统 |
4.2.2 程序的链接
源程序经过编译后,可得到一组目标模块。链接程序的功能是将这组目标模块以及它们所需要的库函数装配成一个完整的装入模块。
链接分成如下三种。
1、静态链接(Static Linking)方式
事先进行链接而以后不再拆开的链接
:先进行链接所形成的一个完整的装入模块【称可执行文件】。通常都不再把它拆开,要运行时可直接将它装入内存。
在将几个目标模块装配成一个装入模块时,须解决以下两个问题:
对相对地址进行修改
。在由编译程序所产生的所有目标模块中,使用的都是相对地址,其起始地址都为0,每个模块中的地址都是相对于起始地址计算的。在链接成一个装入模块后,除去第一个模块其余在装入模块的起始地址不再是0,所以此时须修改其余模块中的相对地址。变换外部调用符号
。将每个模块中所用的 外部调用符号也都变换为相对地址。
2、装入时动态链接(Load-time Dynamic Linking)
在程序 装入内存时
,采用边装入边链接的链接方式
——若调用外部模块,则**立即加载并链接
** 对应的目标模块,且要 修改模块内的相对地址
。
装入时动态链接方式有以下优点:
1. 便于模块的修改与更新
- 静态链接问题:
修改一个模块需重新链接所有依赖它的程序
,生成完整的新装入模块
(低效且可能无法修改)。 - 动态链接优势:
- 模块分开独立存放,
修改或更新各目标模块
。 - 下次程序装入时,
自动链接新版本模块,无需重新编译其他部分
。
- 模块分开独立存放,
2. 支持模块共享
- 静态链接问题:每个程序均包含所链接模块的
独立副本
,浪费内存。 - 动态链接优势:
内存中仅保留一份共享模块
,所有程序通过地址映射共用
同一物理副本——显著减少内存占用,尤其对公共库(如数学库、图形库)。
3、运行时动态链接(Run-time Dynamic Linking)
将对某些模块的链接推迟到程序执行时才进行
。
- 在执行过程中,当发现
一个被调用模块尚未装入内存
时,立即由OS去找到该模块
,并将之装入内存
,将其链接到调用者模块
上。 - 凡在执行过程中
未被用到的目标模块
,都不会被调入内存和被链接到装入模块上
。
优点:加快程序的装入过程,而且可节省大量的内存空间
4.3 连续分配存储管理方式
为一个用户程序分配一个连续的内存空间,即程序中代码或数据的逻辑地址相邻,体现在内存空间分配时物理地址的相邻。
连续分配方式可分为四类: 单一连续分配
、固定分区分配
、动态分区分配
以及动态可重定位分区分配
。
内部碎片,分配给某进程的内存区域中,如果有些部分没有用上。
外部碎片,是指内存中的某些空闲分区由于太小而难以利用。
4.3.1 单一连续分配
背景:早期单道批处理系统。
核心思想:将内存划分为 系统区
和 用户区
,每次仅允许 一个用户程序独占用户区
。
内存布局:
- 系统区(低地址):
存放操作系统内核
(如监控程序、驱动程序)。 - 用户区(高地址):
整个空间由单个用户程序独占
。
优缺点:
- 优点:
实现简单
;无外部碎片
; 可以采用覆盖技术扩充内存
; 不一定需要采取内存保护。 - 缺点:
只能用于单用户、单任务
的操作系统中;有内部碎片
;存储器利用率极低
。
4.3.2 固定分区分配
目的:支持 多道程序并发,解决单一连续分配的 单任务低效问题。
核心思想:
- 将用户内存空间
预先划分
为若干固定大小的分区
,每个分区装入一道作业
,多道程序共享内存
。 - 各程序互不干扰(地址隔离),OS 通过
分区分配表
管理分区状态(已分配/空闲)。
1、划分分区的方法
-
分区大小相等(指所有的内存分区大小相等)。
缺点是缺乏灵活性:
- 当程序太小时,会造成内存空间的浪费。
- 当程序太大时,一个分区又不足以装入该程序,致使该程序无法运行。
-
分区大小不等。增加存储器分配的灵活性。对常在该系统中运行的作业大小进行调查,根据用户的需要来划分。通常,可把
内存区划分成含有多个较小的分区、适量的中等分区及少量的大分区
,便可根据程序的大小,为之分配适当的分区。
2、内存分配
将分区按其大小进行排队,并为之建立一张 分区使用表
,其中各表项包括每个分区的起始地址
、大小
及状态(是否已分配)
。
核心思想:当有一用户程序要装入时,由内存分配程序
依据用户程序的大小
检索该表:
- 若从中
找到
一个能满足要求的、尚未分配的分区,则将之分配给该程序
,然后将该表项中的状态置为“已分配”
。 - 若
未找到
大小足够的分区,则拒绝
为该用户程序分配内存。
优缺点:
- 优点: 实现简单,
无外部碎片
。 - 缺点:
- 当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会降低性能;
- 会产生
内部碎片
,内存利用率低。
4.3.3 动态分区分配
动态分区分配又称为可变分区分配,它是在进程装入内存时,根据进程的实际需要,动态地为之分配内存空间。动态分区分配没有内部碎片,但是有外部碎片。
1、动态分区分配中的数据结构
常用的数据结构有以下两种形式:
- ① 空闲分区表,在系统中设置一张空闲分区表,用于记录每个空闲分区的情况。
每个空闲分区占一个表目
,表目中包括分区号、分区大小和分区始址等数据项
。 - ② 空闲分区链。
- 为了实现对空闲分区的
分配和链接
,在每个分区的起始部分设置一些用于控制分区分配的信息,以及用于链接各分区所用的前向指针,在分区尾部则设置一后向指针。通过前、后向链接指针,可将所有的空闲分区链接成一个双向链
。 - 为了
检索方便
,在分区尾部
重复设置状态位和分区大小
表目。 - 当分区被
分配出去以后
,把状态位由“0”改为“1”
。此时,前、后向指针已无意义
。
- 为了实现对空闲分区的
2、动态分区分配算法
为把一个新作业装入内存,须按照一定的分配算法,从空闲分区表或空闲分区链中选出一分区分配给该作业。包括了传统的四种顺序式搜索算法
以及三种较新的索引武搜索算法
。
3、分区分配操作
在动态分区存储管理方式中,主要的操作是分配内存
和回收内存
。
-
分配内存
-
回收内存
当进程运行完毕释放内存时,
系统根据回收区的首址,从空闲区链(表)中找到相应的插入点
,此时可能出现以下四种情况之一:- ① 回收区与插入点的前一个空闲分区F1相邻接:将
两分区合并
,形成新的空闲分区,不必分配新表项
,只需修改:其前一分区F1的大小。 - ② 回收分区与插入点的后一空闲分区F2相邻接:将
两分区合并
,形成新的空闲分区,修改:用回收区的首址作为新空闲区的首址,大小为两者之和。 - ③ 回收区同时与插入点的前、后两个分区邻接:将
三个分区合并
,形成新的空闲分区,修改:使用F1的表项和F1的首址,取消F2的表项,大小为三者之和。 - ④ 回收区既不与F1邻接,又不与F2邻接:修改:为回收区单独建立一个新表项,填写回收区的首址和大小,并根据其首址插入到空闲链中的适当位置。
- ① 回收区与插入点的前一个空闲分区F1相邻接:将
4.3.4 基于顺序搜索的动态分区分配算法
顺序搜索,是指依次搜索空闲分区链上的空闲分区
,去寻找一个其大小能满足要求的分区。比较适用于不太大的系统。
基于顺序搜索的动态分区分配算法有如下四种:
1、首次适应(first fit, FF)算法
核心思想:从 低地址
开始 顺序查找
空闲分区链, 第一个
大小满足条件的分区,进行分配。
分配流程:
- Step 1: 从
链首(最低地址)开始顺序扫描
。 - Step 2: 找到
第一个大小 ≥ 请求大小的空闲分区
。 - Step 3: 切割分区:
分配所需的大小
,多余部分仍作为空闲块保留
。 - Step 4: 若扫描完所有分区均不满足,返回分配失败。
优点 | 缺点 |
---|---|
高效:查找速度快(通常低地址即可命中)。 | 碎片化:低址部分易产生许多难以利用的、很小的空闲分区 - 碎片。 |
保留大块:高址部分可能留有大空闲区。 | 分配不均:低址频繁切割,导致后续查找变慢。 |
2、循环首次适应(next fit,NF)算法
改进目标: 解决首次适应(FF)算法的以下问题:
- 低址碎片化(低地址出现过多小碎片)。
- 查找开销高(每次从链首开始扫描)。
核心思想:
不每次都从链首开始查找
,而是基于上次分配位置(指针)
继续往后搜索。- 采用
循环查找
策略(到达链尾后跳回链首
)。 分配成功
后,**更新指针位置
**到该分区的下一个空闲块。
算法流程
初始化
:维护一个起始查找指针(ptr)
,初始指向链首。分配请求
:- Step 1: 从
ptr
位置开始顺序查找,检查当前空闲块是否满足请求大小。 - Step 2: 若满足,切割分配并更新
ptr
到下一个空闲块。 - Step 3: 若遍历到链尾仍未找到,则循环回链首继续查找。
- Step 4: 若全链无满足块,返回失败。
- Step 1: 从
优点 | 缺点 |
---|---|
减少低址碎片:避免集中切割低址部分。 | 大块缺乏:分布均匀但可能无连续大空间。 |
查找效率更高:跳过低址已碎片化区域。 | 仍存在碎片:外部碎片问题未彻底解决。 |
3、最佳适应(best fit,BF)算法
改进目标 :避免“大材小用”,提高内存利用率。
核心思想 :分配内存时,总是选择能满足请求且最小的空闲分区【空闲分区按大小升序排列,便于快速查找最佳匹配】,以减少浪费。
算法流程
空闲分区组织
:所有空闲块按容量从小到大排序
。分配流程
:- Step 1: 扫描空闲分区链,找到第一个 ≥ 请求大小的分区。
- Step 2: 若找到,切割分配,剩余部分作为新空闲块,重新排序。
- Step 3: 若未找到满足块,返回分配失败。
优点 | 缺点 |
---|---|
减少内部碎片,避免大块浪费。 | 产生很多外部碎片(剩余部分极小)。 |
首次查找即最优 ,无需全链扫描。 | 维护排序开销大(插入/删除频繁)。 |
4、最坏适应(worst fit,WF)算法
目标 :避免产生极小碎片,使得剩余部分仍可被后续利用。
核心思想 :每次分配时选择最大的空闲分区【将所有的空闲分区,按其容量以从大到小的顺序形成一空闲分区链,查找时,只要看第一个分区能否满足作业要求即可】,并从中切割所需大小。
算法流程 :
-
空闲分区排序
:维护一个按容量从大到小排列
的空闲链表。 -
分配步骤
:检查链表第一个分区(最大块)是否 ≥ 请求大小。
- 满足 → 切割分配,剩余部分重新插入链表并排序。
- 不满足 → 直接分配失败(无足够大块)。
-
更新链表
:将剩余空闲区(若有)重新按大小插入链表。
优点 | 缺点 |
---|---|
减少微小碎片(剩余部分较大) | 缺乏大块内存(频繁切割最大块) |
查找效率高(只需检查首节点) | 分配大作业时可能失败(无连续大块) |
适合中小型作业为主的场景 | 维护降序链表开销较大(插入排序慢) |
5、四种算法总结对比
算法 | 分配策略 | 分区排列顺序 | 碎片特点 | 适用场景 |
---|---|---|---|---|
FF | 首次满足 | 空闲分区以地址递增次序排列 | 低址碎片集中 | 需保留高址大块的情况 |
NF | 从上次位置继续查找 | 空闲分区以地址递增次序排列(可排列成循环链表) | 碎片分散均匀 | 避免低址碎片化 |
BF | 最小满足块 | 空闲分区以容量递增次序排列 | 外部碎片极多 | 小内存频繁分配 |
WF | 最大满足块 | 空闲分区以容量递减次序排列 | 内部碎片少 | 中小作业为主,避免碎片 |
4.3.5 基于索引搜索的动态分区分配算法
当系统很大时,系统中的内存分区可能会很多,相应的空闲分区链就可能很长,这时采用顺序搜索分区方法可能会很慢。为了提高搜索空闲分区的速度
,在 大、中型系统 中往往会采用基于索引搜索的动态分区分配算法,目前常用的三种算法。
1、快速适应(quick fit)算法
核心思想 :又称分类搜索法,将空闲分区按固定大小分类,每类维护独立的链表,通过索引表快速定位。
目标:
- 高效查找(
直接定位最小满足块
)。 - 避免分割碎片(
分区不切割
,减少外部碎片)。 - 保留大分区(大块不会被切割,满足大作业需求)。
数据结构 :
- 空闲分区链表
按容量分类:每类对应一个固定大小(如 2KB、4KB、8KB……)。【例:请求 3KB → 分配 4KB 链表中的块(向上取整)。】
-
管理索引表
哈希表或数组,存储每类链表的头指针
索引表: | 2KB | → [2KB空块1] → [2KB空块2] → NULL | 4KB | → [4KB空块1] → NULL | 8KB | → [8KB空块1] → [8KB空块2] → [8KB空块3] → NULL | ... |
算法流程 :
- Step 1:
查找最小满足链表
- 根据请求大小,从索引表中找到最小的 ≥ 需求容量的链表。
- 例:请求 5KB → 选择 8KB 链表(若 4KB 不满足)。
- Step 2:
分配首节点
- 直接取出链表中第一个空闲块分配给进程(无需分割)。
优缺点 :
-
优点
-
查找效率极高
:通过索引表直接定位链表,避免全局扫描。 -
无外部碎片
:分区整块分配,不切割。 -
保留大分区
:大块不会被切碎,适合大作业需求。
-
-
缺点
内部碎片不可避免
:例:请求 3KB 分配 4KB,浪费 1KB(以空间换时间)。合并复杂度高
:释放内存时需合并相邻空闲分区,需维护跨链表的合并逻辑(算法复杂)。分区固定化
:若分类粒度不合理(如缺少 6KB 类),可能加剧浪费。
2、伙伴系统(buddy system)
在伙伴系统中,无论是占用块或空闲块,其大小均为 2的k 次幂(k 为某个正整数)例如;当用户申请n个字的内存区时,分配的占用块大小为 2k 个字(2k-1 < n ≤ 2k)
伙伴系统的优点是算法简单、速度快;缺点是由于只归并伙伴而容易产生碎片。
可利用空间表的结构
将所有大小相同的空闲块建于一张子表中。 每个子表是一个双重链表,双重链表中的结点结构:
head 为结点头部,是一个由 4 个域组成的记录,
-
llink 域
和rlink 域
分别指向同一链表中的前驱和后继结点; -
tag 域
为值 取"0"、"1"的标志域; -
kval 域
的值为 2 的幂次 k; -
space
是一个大小为 2k-1 个字的连续内存空间(仍假设 head 占一个字的空间)。
# define m 16
typedef struct WORD_b {struct WORD_b* llink; // 指向前驱结点int tag;// 块标志,0:空闲,l: 占用。int kval;// 块大小,值为 2 的幂次 Kstruct WORD_b* rlink;// 头部域,指向后继结点OtherType other; // 字的其他部分
}WORD_b, head; // WORD : 内存字类型,结点的第一个字也称为 headtypedef struct HeadNode {int nodesize; // 该链表的空闲块的大小WORD_b* first; // 该链表的表头指针
}FreeList[m+1]; // 表头向量类型
分配算法
在分配时经常需要将一个大的空闲块
分裂成两个大小相等 的存储区
,这两个 由同一大块分裂出来的小块就称之“互为伙伴"
例题:
时间 | 事件 | 请求大小 | 分配情况 | 空闲情况 |
---|---|---|---|---|
Start | 初始状态 | - | - | 210 |
T1 | A请求 | 150B | 28(A) | 29, 28 |
T2 | B请求 | 100B | 28(A), 27(B) | 29, 27 |
T3 | C请求 | 50B | 28(A), 27(B), 26© | 29, 26 |
T4 | B释放 | - | 28(A), 26© | 29, 27, 26 |
T5 | C释放 | - | 28(A) | 29, 28 |
T6 | A释放 | - | - | 210 |
思路:
初始状态为210大小的空闲块
- 为
A进程
分配150B (27 < 150 ≤ 28 ==> 2k-1 < 150 ≤ 2k, k = 8)分区时,首先计算应该分配在哪个k区
k=8 - 接着,在2k
对应的空闲分区链中查找
找到
,将空闲分区分配
给进程A未找到
,表示大小为2k的空闲分区已耗尽,需要 在大小为2k+1的空闲分区链中继续查找。- 若
存在
大小为2k+1的空闲分区,则将其等分为两个分区
,这两个分区称为一对伙伴,其中一个用于分配,另一个加入大小为2k的空闲分区链。 - 若
不存在
大小为2k+1的空闲分区,则 继续查找2k+2的空闲分区链,- 若
找到
则需要进行两次分割
,分割称两个2k+1的分区,一个2k+1大小的分区加入到对应的空闲分区链中; 另一个分区继续分割,分割成两个2k的分区,一个加入对应分区链中,一个分给进程。 - 若仍然
不存在
,则继续查找
,以此类推
- 若
- 若
回收算法
首先判别其伙伴是否为空闲块
,
- 若
否
,则只要将释放的空闲块简单插入在相应子表
中即可; - 若
是
,则需在相应子表中找到其伙伴并删除之
,然后再判别合并后的空闲块的伙伴是否是空闲块
。依此重复,直到归并所得空闲块的伙伴不是空闲块时,再插入到相应的子表中去。
例如,当大小为28,起始地址为 512 的伙伴块的起始地址的计算方式为:
由于 512 取余 29=0,所以,512+ 28=768,即如果该存储块回收时,只需要查看起始地址为 768 的存储块的状态,如果是空闲块则两者合并,反之直接将回收的释放块链接到大小为 28 的链表中。
特点 | 优点 | 缺点 |
---|---|---|
分配/释放速度 | 快速查找 | 递归分裂/合并 可能带来额外 CPU 开销 |
碎片控制 | 几乎无外部碎片(自动合并空闲伙伴块) | 内部碎片严重 |
大块内存管理 | 保留大块能力(合并机制避免大块被永久拆分) | 不灵活适配非2k请求(如申请3KB须分配4KB) |
实现复杂度 | 数据结构简单(仅需链表数组维护不同大小的块) | 频繁合并检查(释放时需递归检查伙伴块状态,影响性能) |
适用场景 | 适合大块连续内存分配 | 不适合高频小内存分配 |
3、哈希算法
核心思想:用 哈希表
替代顺序索引表,将块大小作为关键字
,直接映射到对应的空闲链表。
分配流程:
-
哈希表代替顺序索引表
传统方式
:按固定块大小(如 4KB, 8KB,…)维护索引表,查找时按顺序比对大小。哈希优化
:将块大小作为 键(key),通过哈希函数 直接计算出链表位置,无需遍历。
-
哈希函数设计
冲突处理:若两个块大小映射到同一哈希桶(如 3KB 和 4KB 均映射到 4KB),可通过:
链表法
:在同一个桶内维护多个大小的链表。向上取整
:强制分配更大块。
传统方式:
1. 请求 5KB → 遍历索引表找到 ≥5KB 的最小块(如 8KB)。
2. 访问 8KB 链表分配。哈希优化:
1. 请求 5KB → hash(5) 计算得到索引 5(或向上取整到 8)。
2. 直接访问哈希表[5] 或哈希表[8] 的链表,分配匹配块。
4.3.6 动态可重定位分区分配
1、紧凑
连续分配
- 基本要求:程序和系统必须加载到连续的物理内存中。
- 问题:随着程序不断分配和释放内存,内存会被分割成许多离散的小分区(碎片化),即便总空闲空间足够,也可能无法满足大程序的连续分配需求。
碎片问题(内存零头)
外部碎片
:内存中存在多个不连续的小空闲分区,无法合并使用
,即使它们的总和足够容纳新程序。
示例:
- 内存中有 4 个空闲分区:10KB, 30KB, 14KB, 26KB(总计 80KB)。
- 新作业要求 40KB 连续空间,由于没有单块 ≥40KB 的空闲区,无法分配。
解决方案:紧凑/拼接:通过移动内存中作业的位置
,把原来多个分散的小分区拼接成一个大分区
。
紧凑带来的问题
- 地址改变:进程被移动后,其
内存基址(起始地址)改变
,若不修改指令和数据的地址,将导致执行错误。 - 频繁开销:内存碎片化快,需
频繁紧凑
,每次都要对移动了的程序或数据的地址进行修改
,降低系统效率。
改进方案:动态重定位:程序在运行时不直接使用物理地址,而是通过基址寄存器(Bounds Register)动态计算
2、动态重定位
动态重定位(Dynamic Relocation)
,即在 运行时自动调整程序访问的物理地址
,而 无需修改程序代码本身
。
重定位寄存器(Relocation Register)
:用它来存放程序(数据)在内存中的起始地址
。
硬件(MMU)自动完成 逻辑地址 → 物理地址的转换:物理地址 = 逻辑地址 + 重定位寄存器(基址)
3、动态重定位分区分配算法
动态重定位分区分配算法与动态分区分配算法基本上相同
,差别仅在于:在动态重定位分区分配算法中,增加了紧凑的功能
。
当该算法不能找到一个足够大的空闲分区以满足用户需求时
,如果所有的小的空闲分区的容量总和大于用户的要求,这时便须对内存进行“紧凑”,将经“紧凑”后所得到的大空闲分区分配给用户。
4.4 对换(Swapping)
4.4.1 多道程序环境下的对换技术
“对换”:
- 是指把
内存中暂时不能运行的进程
或者暂时不用的
程序和数据换出
到外存上,以便腾出足够的内存空间; - 再把
已具备运行条件的进程
或进程所需要的
程序和数据换入
对换的类型
在每次对换时,都是将一定数量的程序或数据换入或换出内存。按兑换数量换分为两类:
整体对换
。- 以整个
进程为单位
的,故称之为“进程对换
”或“整体对换
” 处理机中级调度
实际上就是存储器的对换功能,其目的是用来解决内存紧张问题
,并可进一步提高内存的利用率和系统的吞吐量。
- 以整个
页面(分段)对换
。- 以
进程的一个“页面”或“分段”为单位
进行对换的,则分别称之为“页面对换
”或“分段对换
”,又统称为“部分对换
”。 - 目的是为了支持虚拟存储系统。
- 以
4.4.2 对换空间的管理
1、对换空间管理的主要目标
在具有对换功能的 OS
中,通常把磁盘空间分为文件区和对换区两部分
。对换区的I/O速度比文件区的更快
。
- 对
文件区管理
的主要目标- 文件区
占用磁盘空间的大部分
,用于存放各类文件
。 - 通常的文件都是
较长时间地驻留在外存上
,对它访问的频率是较低
的。 - 对文件区管理的
主要目标
是提高文件存储空间的利用率
,然后才是提高对文件的访问速度
。 - 对文件区空间的管理采取
离散分配
方式。
- 文件区
- 对
对换空间管理
的主要目标- 对换空间只
占用磁盘空间的小部分
,用于存放从内存换出的进程
。 - 这些进程在对换区中
驻留的时间是短暂
的,而对换操作的频率却较高
。 - 对对换空间管理的
主要目标
,是提高进程换入和换出的速度
,然后才是提高文件存储空间的利用率
。 - 对对换区空间的管理采取
连续分配
方式,较少考虑外存中的碎片问题
。
- 对换空间只
2、对换区空闲盘块管理中的数据结构
- 用于
记录
外存对换区中
的空闲盘块的使用情况
。 - 可以用
空闲分区表
或空闲分区链
。 - 在空闲分区表的每个表目中应包含两项:
对换区的首址
及其大小
,分别用盘块号
和盘块数
表示。
3、对换空间的分配与回收
由于对换分区的分配采用的是连续分配方式
,因而对换空间的分配与回收与动态分区方式时的内存分配与回收方法雷同
。
4.4.3 进程的换出与换入
1、进程的换出
进程换出(Swap Out)是内存管理的重要手段,当内存不足时,系统会将部分进程从内存调出至交换区(Swap Area),以腾出空间供其他进程运行。
选择被换出的进程
换出策略遵循**优先级+状态匹配
**原则:
优先级顺序 | 说明 |
---|---|
阻塞/睡眠状态进程 | 优先换出,因为它们当前不占用CPU |
优先级最低的阻塞进程 | 若多个阻塞进程存在,选优先级最低的 |
驻留时间较长的低优先级进程 | 避免“刚换入就换出”的抖动问题 |
优先级最低的就绪进程 | 如果无阻塞进程可选,换出就绪但低优先级的进程 |
📌 关键原则:
- 非运行状态优先(阻塞 > 就绪)
- 低优先级优先
- 考虑驻留时间(防止频繁换入换出)
进程换出流程
换出时需确保 共享数据不被破坏
,步骤如下:
- 检查可换出内容
只能换出非共享的程序段和数据段
。共享内存/代码段(如动态链接库)不可换出
。
- 申请交换区空间 :向磁盘交换区
申请足够空间
,若失败则终止换出
操作。 - 数据写入磁盘 :启动磁盘I/O,将进程的**
程序和数据从内存复制到交换区
**。 - 释放内存资源(传输成功)
回收
该进程占用的物理内存页
。更新相关数据结构
:- 进程控制块(PCB):标记为“换出”状态。
- 内存分配表:记录释放的内存区域。
- 重复换出(可选) :若内存仍不足,继续选择下一个可换出进程,直到无阻塞进程或内存足够。
2、进程的换入
进程换入是对换机制的另一关键操作,当内存有空闲时,系统会将外存(交换区 / Swap Area)中符合条件的进程重新调入内存,使其恢复执行。
选择被换入的进程
换入策略遵循 就绪状态 + 换出时间
原则:
筛选条件 | 说明 |
---|---|
状态为“就绪且换出” | 只有就绪态的换出进程才可能被换入 |
换出时间最久(>阈值,如2s) | 优先换入长时间等待的进程,避免“饥饿” |
无法换入时的处理 | 若内存不足,先执行换出腾出空间,再换入 |
📌 关键原则:
- 公平性:防止某些进程因优先级低而长时间滞留外存。
- 资源优化:避免频繁换入换出(抖动)。
进程换入流程
-
检查进程状态:扫描PCB集合,筛选所有
“就绪且换出”
的进程。 -
选择目标进程:从候选进程中,选择**
换出时间最长
(>2s)**的进程。 -
申请内存空间
尝试为进程分配内存:
- 成功:直接
从外存加载到内存
。 - 失败:
先
触发换出操作
腾出空间,再换入
。
- 成功:直接
-
更新数据结构
- PCB:标记为“就绪”状态,记录换入时间。
- 内存分配表:记录新占用的物理页。
-
重复换入(可选)
若内存仍有余量,继续换入其他“就绪且换出”进程,直到:无此类进程或内存不足。
4.5 非连续分配 - 分页存储管理方式
可将离散分配
分为以下三种:
- 分页存储管理方式。
- 将
用户程序的地址空间分为若干个固定大小的区域
,称为“页”或“页面”
。典型的页面大小为1KB。 - 相应地,也将
内存空间分为若干个物理块或页框(frame)
,页和块的大小相同
。 - 这样
可将用户程序的任一页放入任一物理块中
,实现了离散分配。
- 将
- 分段存储管理方式。
- 它把
用户程序的地址空间分为若干个大小不同的段
,每段可定义一组相对完整的信息
。 - 在存储器分配时,以段为单位,这些
段在内存中可以不相邻接
,所以也同样实现了离散分配。
- 它把
- 段页式存储管理方式。这是
分页和分段两种存储管理方式相结合
的产物。它同时具有两者的优点,是目前应用较广泛的一种存储管理方式。
4.5.1 分页存储管理的基本方法
1、【页、页面】和【物理块、页框、页帧、物理页(面)、内存块】
页面和物理块。
- 将
进程的逻辑地址
空间分成若干个页
,并为各页加以编号
,从0开始(如第0页)。 - 把
内存的物理地址
空间分成若干个块
,并为各块加以编号
,从0开始(如0#块)。 - 在为进程分配内存时,
以块为单位
,将进程中的若干个页
分别装入到多个可以不相邻接
的物理块
中。 - 由于进程的最后一页经常装不满块,而形成了不可利用的碎片,称之为“
页内碎片
”
页面大小。页面的大小应选择适中,且页面大小应是2的幂,通常为1KB~8KB
- 选择
过小
的页面大小:- 减小内存碎片,从而减少内存碎片总空间的作用,有利于内存利用率的提高
- 造成每个进程占用较多的页面,从而导致进程的页表过长,占用大量内存。
- 降低页面换进换出的效率。
- 选择
过大
的页面大小:- 减少页表的长度,提高页面换进换出的速度,但却又会使页内碎片增大。
2、地址结构
两部分内容: 前一部分为页号P
,后一部分为位(偏)移量W,即页内地址d
。
逻辑地址A对应的物理地址 = P号页面在内存中的起始地址 + 页内偏移量W
若给定一个
逻辑地址
空间中的地址为A
,页面的大小
为L
,则页号P
和页内地址d
可按下式求得:
P = I N T [ A L ] = A / L (取除法的整数部分) d = [ A ] M O D L = A % L (取除法的余数部分) 其中 I N T 是整除函数 , M O D 是取余函数。 P = INT[\frac{A}{L}] = A/L(取除法的整数部分) \quad d = [A]\,MOD\, L = A\%L(取除法的余数部分)\\其中 \,INT是整除函数,MOD是取余函数。 P=INT[LA]=A/L(取除法的整数部分)d=[A]MODL=A%L(取除法的余数部分)其中INT是整除函数,MOD是取余函数。
例如,其系统的页面大小为1KB, 设A=2170B,则由上式可以求得P=2,d=122。
如果有 K 位表示“页内偏移量”,则说明该系统中一个页面的大小是 2K 个内存单元
如果有 M 位表示“页号”,则说明在该系统中,一个进程最多允许有 2M 个页面
3、页表
页表的作用是实现从页号到物理块号的地址映射
:
在进程地址空间内的所有页(0~n),依次在页表中有一页表项,其中记录了相应页在内存中对应的物理块号。在配置了页表后,进程执行时,通过查找该表,即可找到每页在内存中的物理块号。
根据页号可以查询页表,而页表中记录的只是内存块号,而不是内存块的起始地址! J号内存块的起始地址 =J *内存块大小
4.5.2 地址变换机构
基本任务:将 用户地址空间中的逻辑地址转换
为内存空间中的物理地址
。实际上是借助页表将逻辑地址中的页号转换为内存中的物理块号。
1、基本的地址变换机构
页表寄存器 PTR (Page-Table Register)
存储内容
:
页表在内存的起始地址F
【页表大多驻留在内存中】页表的长度M
内容来源
:
- 进程
未运行
时:页表基址和长度保存在PCB中
。 - 进程
被调度
时:从PCB加载到PTR
。
步骤详解:
-
逻辑地址拆分(硬件自动完成):逻辑地址 = 页号(P) + 页内偏移量(W)
-
越界检查(安全性):比较
页号(P)
和页表长度M
(PTR中存储):- 若
P ≥ 页表长度 → 地址越界中断
(非法访问)。【页号是从0开始的,而页表长度至少是1,因此 P=M 时也会越界】 - 若
P< 页表长度 → 未越界
- 计算页表项位置:
页表项地址 = 页表始址(F) + 页号(P) × 页表长度(M)
。 - 获取物理块号:从内存中读取页表项,得到物理块号。
- 合成物理地址:
物理地址 = 物理块号 × 页大小 + 页内偏移量(W)
。
- 计算页表项位置:
- 若
2、具有快表的地址变换机构 - 是基本地址变换机构的改进版本
背景:由于页表是存放在内存中的,这使CPU在每存取一个数据时,都要两次访问内存。
- 第一次是访问内存中的页表,从中找到指定页的物理块号,再将块号与页内偏移量 W拼接,以形成物理地址。
- 第二次访问内存时,是通过刚刚所得的物理地址获得所需数据(或向此地址中写入数据)。
因此,采用这种方式将使计算机的处理速度降低近1/2。
快表:又称联想寄存器(TLB, Translation Look aside Buffer)
,是一种访问速度比内存快很多的高速缓存
(TLB不是内存!),用来存放最近访问的页表项的副本
,可以加速地址变换的速度。与此对应,内存中的页表常称为慢表。快表不可能做得很大,通常只存放16~512 个页表项
。
地址变换过程:
在CPU给出有效地址(不越界)后,由地址变换机构自动地将页号P送入TLB
,将此页号与TLB中所有页号进行比较
,
-
若在快表中
找到有与此相匹配的页号
,便表示所要访问的页表项在快表中。于是,可直接从快表中读出
该页所对应的物理块号,并送到物理地址寄存器中
。 -
若在快表中
未找到对应的页表项
,则还须再访问内存中的页表
,找到后把从页表项中读出的物理块号送往地址寄存器
;
同时,再将此页表项存入快表
的一个寄存器单元中——重新修改快表。如果TLB已满,则OS必须找到一个
老的且已被认为是不再需要的页表项
,将它换出。
局部性定理:
时间局部性
:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行; 如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
空间局部性
:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(很多数据在内存中都是连续存放的)基本地址变换机构中,每访问一个逻辑地址,都需要查询一次内存中的页表。由于局部性原理,可能连续很多次查到的都是同一个页表项。将慢表中的页表项复制到快表中,访问的速度就会大幅提升。
4.5.3 访问内存的有效时间
内存的有效访问时间(Effective Access Time, EAT):从进程发出指定逻辑地址的访问请求,经过地址变换,到在内存中找到对应的实际物理地址单元并取出数据,所需要花费的总时间。
快表命中,只需一次访存;快表未命中,需要两次访存
4.5.4 两级和多级页表
单级页表的问题:
问题一:页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框。
问题二:没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面。
解决问题一:两级和多级页表
将页表进行分页的方法,使每个页面的大小与内存物理块的大小相同,并为它们进行编号,然后离散地将各个页面分别存放在不同的物理块中。同样,也要为离散分配的页表再建立一张页表,称为外层页表(Outer Page Table)/页目录表/顶层页表
,在每个页表项中记录了页表页面的物理块号。
注意:
解决问题二:
- 可以在
需要访问页面
时才把页面调入内存
(虚拟存储技术)。 - 可以在
页表项中增加一个标志位
,用于表示该页面是否已经调入内存。如果没被调入内存中,则产生缺页中断(内中断),然后将目标页面从外存调入内存
4.5.5 反置页表(Inverted Page Table)
反置页表 则是为每一个物理块设置一个页表项
,并将它们按物理块的编号排序
,其中的内容则是页号和其所隶属进程的标识符
。
-
全系统共享一个页表
-
条目数=物理页框数 【条目数与物理内存相关】
物理页框号(PFN) → (进程ID + 页号)
地址转换流程:
- 根据
进程标识符和页号
,去遍历检索
反置页表。- 如果
检索到
与之匹配的页表项,则该页表项(中)的序号便是该页所在的物理块号
,可用该块号与页内地址一起构成物理地址送内存地址寄存器。 - 如果检索了整个反置页表仍
未找到匹配
的页表项,则表明此页尚未装入内存
。- 对于
不具有请求调页功能
的存储器管理系统,此时则表示地址出错
。 - 对于
具有请求调页功能
的存储器管理系统,此时应产生请求调页中断
,系统将把此页调入内存
。
- 对于
- 如果
检索优化
利用 Hash 算法来进行检索
,这样可以很快地找到在反置页表中的相应页表项。
不过在采用 Hash 算法时,可能会出现所谓的“地址冲突”
,即有多个逻辑地址被映射到同一个Hash 表项上,必须妥善解决这一问题。
4.6 非连续分配 - 分段存储管理方式
4.6.1 分段存储管理方式的引入
1. 方便编程
- 逻辑分段:程序员可
将程序划分为多个逻辑段
(如代码段、数据段、堆栈段等)。 - 地址结构:逻辑地址由
段号+段内偏移
组成,直观易读。 - 优点:
- 符合程序员的自然思维(函数、数组等逻辑单位独立编址)。
- 调试和维护更直观(如直接定位到某个函数所在的段)。
2. 信息共享
- 共享粒度:以段为逻辑单位共享(如共享一个函数库或数据文件)。
- 对比分页:
页是物理单位
,共享一个函数可能需要多个不连续的页,管理复杂。段是逻辑单位
,共享时只需标记整个段为可共享。
3. 信息保护
- 保护粒度:
按段设置权限
(如代码段只执行,数据段读写)。 - 对比分页:
页可能混合不同权限的内容
(如一个页同时包含代码和数据),无法统一保护。段是逻辑整体
,保护标志直接作用于整个段(如段X:只读
)。
- 用途:
防止越权访问
(如恶意修改代码段)。
4. 动态增长
- 需求场景:
数据段
(如堆、数组)运行时可能动态扩展
。 - 分段方案:预留空间或动态分配新物理段(操作系统调整段表)。
- 对比分页/连续分配:
- 分页需预分配固定大小,扩展需分配新页表,可能引发碎片。
- 连续分配(如动态分区)难以应对大小不确定的请求。
5. 动态链接
- 核心思想:程序运行时
按需加载和链接目标段
(如调用函数时才加载)。 - 分段支持:
以段为链接单位
(如一个动态库对应一个段),延迟加载减少内存占用
。 - 对比静态链接:避免一次性加载所有代码,提升内存利用率。
4.6.2 分段系统的基本原理
1. 分段
作业
的地址空间被划分为若干个段
,每个段
定义了一组逻辑信息且在内存中占据连续空间
,但各段之间可以不相邻
。例如,有主程序段 MAIN、子程序段X、数据段D及栈段S等。
每个段都有自己的名字
。为了实现简单起见,通常可用一个段号来代替段名【编译程序会将段名转换为段号】
,每个段都从0开始编址,并采用一段连续的地址空间
。
段的长度由相应的逻辑信息组的长度决定,因此各段的长度并不相等
。
逻辑地址
由段号(段名)和段内地址所组成
。段号的位数决定了每个进程最多可以分几个段;段内地址位数决定了每个段的最大长度是多少
一维地址(如分页系统):
整个程序的内存地址是连续编号
的,比如从0
到1000
,像一个长长的纸条。
→ 只需要指明“逻辑地址 = 第500号位置
”,系统就能直接找到数据。二维地址(分段系统):
程序的内存地址被分成多个段,每段有独立的编号和内部地址。
→ 需要指明:“逻辑地址 = <段号2, 偏移200> = 段号是2的第200号位置
”,系统才会定位到数据。
【先指定段(一维),再指定段内偏移(二维)】。
2. 段表
段表:用于实现从逻辑段到物理内存区的映射
的。
段表:每个段在表中占有一个表项
,其中记录了该段在内存中的起始地址(又称为“基址”)
和段的长度
。
段表可以存放在一组寄存器
中,以利于提高地址转换速度。但更常见的方法是将段表放在内存中
。
3. 地址变换机构
系统中设置了段表寄存器,用于存放段表始址和段表长度TL
。
流程:
- 在进行地址变换时,系统将逻辑地址中的
段号S与段表长度M, 进行比较
。- 若
S ≥ M
,表示段号太大,是访问越界
,于是产生越界中断
信号。 - 若
S < M
,表示未越界
, 则- 根据段表的
始址F
和 该段的段号S
,计算出该段对应段表项的存放地址
。 - 再检查
段内地址W
是否超过该段的段长C
。- 若超过,即
W ≥ C
,同样发出越界中断
信号。 - 若未越界,即
W < C
,则将该段的基址b 与 段内地址W 相加
,即可得到
要访问的内存物理地址
。
- 若超过,即
- 根据段表的
- 若
访问内存
分页(单级页表)
:第一次访存–查内存中的页表
,第二次访存–访问目标内存单元
。总共两次访存
分段
:第一次访存–查内存中的段表
,第二次访存–访问目标内存单元
。总共两次访存
- 与分页系统类似,分段系统中也可以
引入快表机构
,将近期访问过的段表项放到快表中
,这样可以少一次访问,加快地址变换速度。
4. 分页和分段的主要区别
特性 | 分段 | 分页 |
---|---|---|
划分单位 | 逻辑段(变长) | 物理页(固定大小-系统决定) |
地址转换 | 段表(基址 + 段内地址) | 页表(页框号) |
碎片问题 | 外部碎片(需紧凑) | 内部碎片(页内未用空间) |
用户可见性 | 用户程序可感知(需指定段号) | 对用户透明(系统自动管理) |
硬件成本 | 段表寄存器 | 页表寄存器 |
逻辑地址 | 二维(段号 + 段内地址) | 一维 |
4.6.3 信息共享
分段比分页更容易实现信息的共享和保护。
4.7 非连续分配 - 段页式存储管理方式
1. 基本原理
段页式系统的基本原理是分段和分页原理的结合
,即先将用户程序分成若干个段
,再一个作业地址空间把每个段分成若干个页(面)
,并为每一个段赋予一个段名
。
优点 | 缺点 | |
---|---|---|
分页管理 | 内存空间利用率高,不会产生外部碎片,只会有少量的页内碎片 | 不方便按照逻辑模块实现信息的共享和保护 |
分段管理 | 很方便按照逻辑模块实现信息的共享和保护 | 如果段长过大,为其分配很大的连续空间会很不方便。另外,段式管理会产生外部碎片 |
2. 段表、页表
- 一个进程对应一个段表,每个段表项对应一个页表 => 一个进程对应多个页表
段表
:每个段
对应一个段表项
,每个段表项
由段号
、页表长度
、页表存放块号(页表起始地址)
组成。每个段表项长度相等,段号是隐含的
。页表
:每个页面
对应一个页表项
,每个页表项
由页号
、页面存放的内存块号
组成。每个页表项长度相等,页号是隐含的
。
3. 地址变换过程
过程
-
由逻辑地址得到
段号
、页号
、页内偏移量
-
段号
与段表寄存器中的段长度比较
,检查段号是否越界
-
由
段表始址、段号
找到对应段表项
-
根据段表中记录的页表长度,检查
页号是否越界
-
由
段表中的页表地址、页号
得到查询页表,找到相应页表项
-
由页面存放的
内存块号、页内偏移量
得到最终的物理地址
-
访问目标单元
共三次访存
。可引入快表机构,用段号和页号作为查询快表的关键字
。老若快表命中则仅需一次访存。
参考:
教材:
计算机操作系统(第四版) (汤小丹) (Z-Library).pdf
视频:
王道计算机考研 操作系统