3.1 内存管理概念

1.内存管理的基本原理和要求

内存管理的四大主要功能:


1. 内存分配与回收 (Memory Allocation & Deallocation)

这是内存管理最基本的功能。操作系统需要记录内存的使用状态(哪些块是空闲的,哪些已被占用),并根据进程的需求进行操作。

  • 分配: 当一个程序启动或请求更多空间时,OS 为其划分内存块。
  • 回收: 当程序运行结束或不再需要某块内存时,OS 必须及时将其收回,以便供其他程序使用。
  • 碎片管理: 解决在频繁分配和回收过程中产生的“空闲碎片”问题(包括外部碎片和内部碎片)。

2. 地址变换 (Address Mapping / Relocation)

程序员编写程序时通常使用逻辑地址(虚拟地址),而数据在内存中存储的实际位置是物理地址

  • 重定位: 内存管理负责在程序执行时,将逻辑地址转换为对应的物理地址。
  • 灵活性: 这种机制允许程序在内存中被移动(例如由于内存紧缩),而不影响其正常运行。

3. 内存保护 (Memory Protection)

为了保证系统的安全性,内存管理必须确保每个进程都在分配给它的内存空间内运行,防止进程之间互相干扰。

  • 防止越界: 确保进程 A 不会无意或恶意地读写进程 B 或操作系统的内存区域。
  • 读写权限控制: 设定某些区域为“只读”(如代码段),某些为“可读写”(如数据段)。

4. 内存扩充 (Memory Expansion / Virtual Memory)

通过虚拟内存技术,内存管理可以使用户感觉内存容量比实际的硬件内存大得多。

  • 请求调入: 只将程序当前需要的部分调入内存,其余部分留在磁盘上。
  • 置换功能: 当物理内存不足时,将暂时不用的数据置换到磁盘的交换区(Swap Space)中。
  • 效果: 使得大型程序能够在较小的物理内存中运行,同时也支持更多的并发进程。

核心功能对比表

功能目标解决的问题
分配与回收资源管理“谁能用内存?用多少?”
地址变换透明化“程序看哪?”与“数据在哪?”的统一
内存保护安全性“禁止乱串门”,防止系统崩溃
内存扩充逻辑扩展“小内存跑大程序”

提示: 在现代系统中,这些功能通常通过硬件(如 MMU,内存管理单元)和操作系统的协同工作来实现。

2.逻辑地址和物理地址

1. 什么是逻辑地址 (Logical Address)?

逻辑地址(也称为虚拟地址)是 CPU 在程序执行期间生成的地址。

  • 视角: 它是从用户程序的角度看到的地址。
  • 特性: 程序在编写、编译和链接时,通常都使用逻辑地址。它给每个进程一种错觉,仿佛它拥有一个连续且独立的完整内存空间。
  • 集合: 一个进程产生的所有逻辑地址的集合被称为该进程的逻辑地址空间

2. 什么是物理地址 (Physical Address)?

物理地址内存单元(即 RAM 硬件)中真实存在的地址。

  • 视角: 它是从硬件存储器的角度看到的地址。
  • 特性: 这是数据被存入内存条或从内存条读出时,地址总线上显示的真实数字。
  • 集合: 内存中所有可用的物理单元对应的地址集合被称为物理地址空间

3. 核心差异对比

特性逻辑地址 (Logical Address)物理地址 (Physical Address)
生成者由 CPU 在执行程序时生成由内存管理单元 (MMU) 计算得出
可见性用户和程序员可见用户和程序不可见(对硬件透明)
别名虚拟地址 (Virtual Address)实地址 (Real Address)
存储位置并不物理存在于硬件中真实存在于内存条(RAM)中
一致性相同程序在不同时间运行,其逻辑地址通常一致物理位置可能因内存分配情况而异

4. 地址转换:MMU 的作用

为了让 CPU 生成的“虚假”逻辑地址能找到“真实”的物理内存,硬件中有一个专门的部件叫作 MMU(Memory Management Unit,内存管理单元)

当 CPU 想要访问逻辑地址 A 时:

  1. CPU 将逻辑地址 A 发送给 MMU
  2. MMU 根据当前进程的页表段表(记录了逻辑地址与物理地址的映射关系)进行计算。
  3. MMU 输出对应的物理地址 B。
  4. 内存控制器根据物理地址 B 在 RAM 中读写数据。

5. 为什么要区分这两个地址?

这种“地址分离”的设计主要为了解决以下核心问题:

  1. 多任务并发: 多个程序可以同时运行。即使两个程序都使用了相同的逻辑地址(如 0x0040),MMU 也会将它们映射到 RAM 中不同的物理位置,互不干扰。
  2. 内存保护: 操作系统可以通过 MMU 限制进程只能访问属于它的物理区域,防止程序 A 意外修改程序 B 的数据。
  3. 内存扩充(虚拟内存): 允许逻辑地址空间大于实际的物理内存。暂时不用的数据可以换出到磁盘,当需要时再调入物理内存并更新映射。
  4. 动态重定位: 程序在运行过程中可以被移动到物理内存的其他位置,而不需要修改程序内部的地址指令。

3.装入模块装入内存的三种装入模式

在操作系统内存管理中,根据地址转换(重定位)发生的时机不同,主要分为以下三种装入模式。


1. 绝对装入模式 (Absolute Loading)

在这种模式下,用户程序在编译或链接时就已经知道程序将驻留在内存的哪个物理位置。

  • 工作原理: 编译程序直接产生绝对地址(物理地址)的目标代码。装入程序按照代码中的地址直接将程序和数据装入内存,不需做任何地址修改。
  • 适用场景: 只适用于单道程序环境(如早期的 DOS 系统或简单的嵌入式系统)。
  • 优缺点:
    • 优点: 装入过程非常简单、迅速。
    • 缺点: 极其不灵活。如果程序在内存中的位置改变,必须重新编译。此外,无法在多任务环境下运行(因为不同程序的地址可能冲突)。

2. 可重定位装入模式 (Relocatable Loading)

也称为静态重定位 (Static Relocation)。这是为了支持多道程序环境而设计的。

  • 工作原理: 1. 编译和链接产生的装入模块使用从 0 开始的逻辑地址。
    1. 在装入时,装入程序根据内存的当前空闲状态,将程序中的逻辑地址一次性转换为物理地址。
    2. 计算公式: 物理地址 = 逻辑地址 + 内存起始地址。
  • 特点: 地址转换是在装入阶段一次性完成的,程序一旦进入内存,其地址就固定了。
  • 优缺点:
    • 优点: 支持多道程序,程序不再绑定在死板的物理位置。
    • 缺点: 程序在内存中不能移动。如果想在运行时调整内存(如内存紧缩),这种模式做不到;且必须分配连续的内存空间。

3. 动态运行时装入模式 (Dynamic Run-time Loading)

也称为动态重定位 (Dynamic Relocation)。这是现代操作系统(如 Windows, Linux, macOS)最普遍采用的模式。

  • 工作原理: 1. 装入程序将模块装入内存后,内存中保留的依然是逻辑地址。
    1. 真正的地址转换推迟到程序真正执行某条指令时才进行。
    2. 硬件支持: 依靠 CPU 内部的 MMU(内存管理单元) 和 重定位寄存器(Base Register)。执行指令时,硬件自动将逻辑地址与重定位寄存器中的起始地址相加。
  • 优缺点:
    • 优点: * 程序可以在内存中任意移动(只需修改重定位寄存器的值)。
      • 便于实现虚拟存储器(程序可以只装入一部分)。
      • 支持代码共享(多个进程映射到同一块物理内存)。
    • 缺点: 需要专门的硬件(MMU)支持。

三种模式对比表

特性绝对装入可重定位装入 (静态)动态运行时装入
地址转换时间编译或链接时程序装入内存时程序执行时
地址类型(内存中)物理地址物理地址逻辑地址
是否支持移动不支持不支持支持
主要应用环境单道程序系统早期多道程序系统现代通用操作系统
硬件需求需要 MMU

4.链接时间不同的三种链接方式

1. 静态链接 (Static Linking)

发生时间: 程序运行前(编译后的链接阶段)。

  • 工作原理: 在程序执行之前,链接程序将各个目标模块(.obj 或 .o)以及所需的库函数,完整地整合成一个单独的可执行文件(如 .exe)。
  • 特点:
    • 独立性: 运行代码时不需要外部库支持,所有代码都在一个文件里。
    • 空间冗余: 如果多个程序都用了同一个库,每个程序的可执行文件里都会包含该库的一份副本,浪费磁盘和内存空间。
    • 更新不便: 如果库函数更新了,整个程序必须重新链接。

2. 装入时动态链接 (Load-time Dynamic Linking)

发生时间: 程序装入内存时

  • 工作原理: 编译后得到的一组目标模块在装入内存时,边装入边链接。当装入程序发现某个模块引用了外部模块时,会去磁盘中寻找该模块并将其一并装入内存。
  • 特点:
    • 易于更新: 只要替换磁盘上的目标模块,下次装入程序时就会自动链接新版本。
    • 效率折中: 相比静态链接,它减少了磁盘空间的占用,但在装入时会有额外的性能开销。

3. 运行时动态链接 (Run-time Dynamic Linking)

发生时间: 程序执行过程中(按需链接)。

  • 工作原理: 这是现代操作系统最主流的方式。在程序运行过程中,如果发现某个目标模块尚未调入内存,才由操作系统去寻找该模块并将其链接到调用者中。
  • 特点:
    • 内存利用率最高: 那些在运行过程中没被用到的模块(例如某些不常用的报错处理逻辑)根本不会被装入内存。
    • 支持共享: 多个进程可以共享内存中的同一个动态链接库(如 Windows 的 .dll 或 Linux 的 .so),极大地节省了物理内存。
    • 灵活性: 允许程序在运行时动态加载插件或扩展。

三种链接方式对比表

特性静态链接装入时动态链接运行时动态链接
链接时机程序运行前程序装入内存时程序执行过程中
可执行文件大小最大(包含所有库)较小最小
内存占用较高(重复副本多)较低(支持共享)最低(按需加载)
更新便捷性差(需重新链接)较好最好
代表技术静态库 (.lib, .a)早期加载技术动态链接库 (.dll, .so)

总结与联系

这三种链接方式的演进过程,实际上是内存和空间利用率不断优化的过程:

  • 静态链接 解决了“能跑起来”的问题。
  • 装入时动态链接 解决了“不用重复存储”的问题。
  • 运行时动态链接 解决了“不用全部塞进内存”的问题。

5.进程的内存映像

1. 进程内存映像的结构

① 代码段 (Text Segment / Code Segment)

  • 内容: 存储 CPU 执行的机器指令。
  • 特性: 通常是只读的,以防止程序意外修改自己的指令。
  • 共享: 多个运行相同程序的进程可以共享同一个代码段的物理副本以节省空间。

② 数据段 (Data Segment)

  • 初始化数据段 (Initialized Data): 存储程序中已明确赋值的全局变量和静态变量。
  • 未初始化数据段 (BSS Segment): 存储未赋初值的全局变量。在程序开始执行前,内核会将此区域初始化为 0。

③ 堆 (Heap)

  • 内容: 用于动态内存分配(如 C 语言的 malloc 或 C++ 的 new)。
  • 特性: 堆的大小不固定,在运行时由低地址向高地址方向增长

④ 共享库 (Shared Libraries)

  • 内容: 用于映射动态链接库(如 .so.dll)的物理内存区域。通常位于堆和栈之间的空隙中。

⑤ 栈 (Stack)

  • 内容: 存储局部变量、函数参数、返回地址以及函数调用的上下文。
  • 特性: 每次函数调用都会压入一个“栈帧”。它由高地址向低地址方向增长

⑥ 内核空间 (Kernel Space)

  • 内容: 进程映像的最顶部通常预留给操作系统内核。用户程序不能直接访问这部分区域,只有通过系统调用进入内核态时才能使用。

2. 结构汇总对比表

区域名称存储内容增长方向读写权限
栈 (Stack)局部变量、函数调用链向下 (低地址)读/写
堆 (Heap)动态分配的内存向上 (高地址)读/写
数据段 (Data/BSS)全局变量、静态变量固定读/写
代码段 (Text)程序指令固定只读/执行

3. 为什么需要这种布局?

  1. 保护机制: 将代码段设为只读,防止程序通过指针错误覆盖指令,提高系统稳定性。
  2. 空间利用率: 堆和栈从两端向中间增长,可以最大限度地利用两者之间的空闲地址空间。
  3. 多任务支持: 不同的进程拥有独立的内存映像(通过 MMU 映射到不同的物理地址),保证了进程间的独立性安全性

4. 动态视角:从“装入”到“映像”

回顾我们之前聊到的装入

  • 装入程序读取磁盘上的装入模块
  • 它把代码放在代码段,把全局变量放在数据段。
  • 它为该程序预留好堆和栈的初始空间。
  • 最后,CPU 的 PC 寄存器指向代码段的入口,进程就此“活”了起来。

6.内存保护

内存保护 (Memory Protection) 是操作系统内存管理中的一项关键安全机制。它的核心目标是:确保每个进程只能访问被合法分配给它的内存区域,防止进程无意或恶意地破坏其他进程的数据或操作系统内核。

如果没有内存保护,一个程序中的 Bug(如野指针)可能会导致整个系统崩溃。


1. 内存保护的主要目标

  • 隔离进程: 确保进程 A 不能读取或修改进程 B 的内存。
  • 保护内核: 严禁用户程序访问操作系统的核心区域。
  • 权限控制: 限制程序对特定内存区域的操作(例如:代码段只能读和执行,不能修改)。

2. 硬件实现机制

内存保护必须依靠硬件来完成,因为软件检查速度太慢,无法跟上 CPU 指令的执行节奏。

A. 界限寄存器 (Limit & Base Registers)

这是最简单的保护方式。CPU 中有两个特殊的硬件寄存器:

  1. 基址寄存器 (Base Register): 存储该进程物理内存的起始地址。
  2. 界限寄存器 (Limit Register): 存储该进程内存区域的大小。

检查过程:

CPU 产生的每一个地址都会经过硬件自动检查:

Base \le 物理地址 < Base + Limit

如果地址超出了这个范围,硬件会触发一个陷阱 (Trap),由操作系统接管并终止报错程序。

B. 存储保护键 (Protection Keys)

为每个内存块(Page 或 Segment)分配一个数字代码(键),并为每个进程分配一个匹配的键。只有键值匹配时,进程才能访问该内存块。

C. 分页/分段保护位 (Protection Bits)

在现代操作系统中,地址映射表(页表)中不仅存储物理地址,还包含若干控制位

  • R (Read): 是否可读。
  • W (Write): 是否可写。
  • X (Execute): 是否可执行。
  • V (Valid): 该地址是否有效(是否已分配给该进程)。

3. 不同区域的保护权限示例

结合我们之前聊过的进程内存映像,操作系统会为不同段设置不同的保护属性:

内存区域常见权限保护目的
代码段 (Text)Read + Execute防止程序在运行时意外或恶意修改自己的指令。
数据段/堆/栈Read + Write允许程序存储和读取数据,但禁止执行(防止某些注入攻击)。
内核空间无访问权限用户进程完全不可见,防止直接干预硬件或系统状态。

4. 违规后果:段错误 (Segmentation Fault)

当程序试图违反内存保护规则时,硬件会立即向操作系统发出信号。操作系统通常会采取“铁腕手段”:

  1. 中断执行: 立即停止该程序的运行。
  2. 抛出错误: 在 Linux 下通常表现为 Segmentation fault (core dumped),在 Windows 下则是“访问违规”。
  3. 释放资源: 回收该进程占用的所有内存,防止其继续危害系统。

5. 特权指令与模式切换

内存保护还依赖于 CPU 的执行模式

  • 用户态 (User Mode): 只能执行非特权指令,受内存保护机制的严格约束。
  • 内核态 (Kernel Mode): 操作系统运行的模式,可以修改界限寄存器、页表等敏感硬件配置。

关键点: 用户程序绝对无法通过指令修改自己的“界限寄存器”或“页表”,因为这些指令是特权指令,只能由内核执行。


总结:

内存保护就像是给每个进程住的房间安上了锁,并安排了一个硬件(MMU)作为保安,时刻检查每一把钥匙是否匹配。这不仅保证了数据的隐私,更确保了整个计算机系统的健壮性。

7.内存共享

内存共享 (Memory Sharing) 是操作系统提供的一种机制,允许两个或多个进程访问同一块物理内存区域。

虽然我们之前讨论过内存保护是为了让进程相互隔离,但内存共享则是为了在确保安全的前提下,提高系统的性能和资源利用率。


1. 核心实现原理:多对一映射

内存共享的核心在于地址转换机制。通过 MMU(内存管理单元)页表,操作系统可以将不同进程的逻辑地址映射到同一个物理内存页上。

  • 进程 A 的逻辑地址 0x4000 \rightarrow 映射到 \rightarrow 物理地址 0x8000
  • 进程 B 的逻辑地址 0x7000 \rightarrow 映射到 \rightarrow 物理地址 0x8000

此时,A 和 B 看到的逻辑地址不同,但它们实际操作的是内存条上的同一块空间。


2. 内存共享的常见场景

① 共享库 (Shared Libraries)

这是最常见的共享形式。

  • 例子: 几乎所有运行中的 Linux 程序都会用到 libc.so
  • 做法: 操作系统只在物理内存中保留一份 libc.so 的代码段,然后将其映射到所有需要它的进程的地址空间中。这样可以节省数百 MB 甚至 GB 的内存。

② 进程间通信 (IPC – Shared Memory)

当两个进程需要高速交换大量数据时,共享内存是最快的方法。

  • 对比: 管道(Pipe)或消息队列需要将数据从进程 A 拷贝到内核,再从内核拷贝到进程 B。
  • 优势: 共享内存直接读写,无需数据拷贝过程,速度极快。

③ 写时复制 (Copy-on-Write, COW)

这是 fork() 系统调用的核心优化。

  • 过程: 当父进程创建子进程时,它们起初共享全部物理内存。
  • 触发: 只要大家只是“读”数据,就一直共享。直到某个进程尝试“写”数据时,操作系统才会为该进程复制一个私有的物理副本。这极大减少了创建进程的开销。

3. 内存共享与保护的冲突与协调

既然共享了内存,如何防止其中一个进程把数据改乱呢?

  1. 只读共享: 对于代码段(如共享库),页表中的权限位会被设为 Read-Only。任何尝试写入的操作都会触发“段错误”。
  2. 读写共享的同步: 对于 IPC 形式的共享内存,操作系统本身通常不提供自动保护。程序员必须使用信号量 (Semaphores)互斥锁 (Mutexes) 来确保同一时间只有一个进程在修改数据,防止竞态条件 (Race Condition)

4. 内存共享的优缺点

优点缺点
节省物理内存: 避免了重复存储相同的指令和静态数据。增加编程复杂性: 需要处理进程间的同步问题。
极高的通信效率: 避免了内核态和用户态之间的数据拷贝。安全性风险: 如果同步处理不好,一个进程的错误可能污染另一个进程的数据。
加速进程创建: 配合 COW 技术,创建子进程几乎是瞬间完成的。

总结:隔离与共享的辩证法

  • 隔离(保护) 是为了稳定性:防止进程互相干扰。
  • 共享 是为了效率:避免资源浪费,加速数据交换。

现代操作系统通过精细的页表控制,完美地平衡了这两点:该隔离的(栈、堆、私有数据)严密保护,该共享的(动态库、共享内存区)高效映射。

8.内存分配与回收

内存分配与回收是内存管理最核心的职能。操作系统必须像一位精明的管家,既要满足各个进程对空间的即时需求,又要确保内存资源不被浪费。

内存分配主要分为连续分配离散分配两大类,我们先从最基础的动态分区分配聊起。


1. 动态分区分配 (Dynamic Partitioning)

这种方式不预先划分内存,而是在进程装入时,根据进程的大小动态地建立分区。

常见的分区分配算法:

为了在空闲内存列表中找到最合适的“坑”,操作系统通常采用以下四种算法:

算法名称分配策略优点缺点
首次适应 (First Fit)从头开始找,遇到第一个够大的就分。简单、高地址空闲块多。低地址容易留下很多细小的碎片。
循环首次适应 (Next Fit)从上次找到的位置开始继续往下找。空闲块分布更均匀。缺乏大的连续空闲块。
最佳适应 (Best Fit)找遍所有空闲块,挑一个能放下且最小的。尽量保留大的空闲块。会产生大量极小的难以利用的碎片。
最坏适应 (Worst Fit)找遍所有空闲块,挑一个最大的分。剩下的碎片比较大,还能继续用。大的空闲块很快被耗尽。

2. 内存碎片问题 (Fragmentation)

在频繁的分配与回收过程中,内存会变得“支离破碎”,这被称为碎片:

  • 内部碎片 (Internal Fragmentation): 已经分配给进程,但进程没用完的那部分(常见于固定分区或分页管理)。
  • 外部碎片 (External Fragmentation): 内存中存在的、由于太小而无法分配给任何进程的空闲块(常见于动态分区管理)。

解决办法:紧缩技术 (Compaction)

操作系统通过移动内存中的进程,使它们连成一片,从而将分散的小空闲块合并成一个大的空闲块。

注意: 这要求系统支持动态重定位(即我们之前聊过的 MMU 硬件支持),否则进程移动后地址就全乱了。


3. 内存回收 (Memory Deallocation)

当一个进程运行结束释放内存时,回收程序不仅要收回空间,还要检查相邻状态

当回收区 R 被释放时,存在四种情况:

  1. 上邻空闲: 与上方的空闲块合并。
  2. 下邻空闲: 与下方的空闲块合并。
  3. 上下均空闲: 三块合并成一个大块。
  4. 上下均不空闲: 作为一个新的空闲表项插入空闲表。

4. 离散分配方式 (Non-continuous Allocation)

由于连续分配容易产生碎片且要求空间必须连续,现代操作系统大多采用离散分配。这种方式允许将一个进程分散地装入到不相邻的内存分区中。

  • 分页存储管理 (Paging): 将内存分为固定大小的“页框”(如 4KB),进程也按 4KB 分页。这样基本消除了外部碎片,但会有极小的内部碎片。
  • 分段存储管理 (Segmentation): 按程序的逻辑结构(代码段、数据段)划分,更符合程序员的思维,便于共享和保护。
  • 段页式管理: 结合了两者的优点,是目前主流的方案。

总结

  • 分配:决定把哪块空闲地皮划给进程(算法选择)。
  • 回收:进程走后,把地皮收回并看看能不能和邻居拼成更大的地(合并空闲块)。
  • 目标:提高内存利用率,减少碎片。

2.连续分配管理方式

1.单一连续分配

单一连续分配 (Single Continuous Allocation) 是最简单的一种内存分配方式。在早期的单用户、单任务操作系统(如 MS-DOS)中,这种方式被广泛采用。

其核心思想是:整个内存的用户区被划分为一个连续的区域,且在任何时刻只能由一个用户进程占用。


1. 内存布局结构

在单一连续分配中,物理内存通常被划分为两个部分:

  • 系统区 (System Area): 通常位于内存的低地址部分,专门用于存放操作系统的内核及其相关数据。
  • 用户区 (User Area): 内存的其余部分,专门用于存放当前正在运行的一个用户进程。

2. 运行机制

  1. 分配: 当一个程序请求运行时,操作系统检查用户区的总大小是否足够容纳该程序。如果够大,就将整个程序装入用户区。
  2. 回收: 当程序运行结束,操作系统直接清空用户区,或者简单地标记该区域为空闲,等待下一个程序。
  3. 保护: 这种模式下的内存保护非常简单,通常只需要一个界限寄存器来防止用户进程非法访问系统区(内核代码)。

3. 内部碎片 (Internal Fragmentation)

这是单一连续分配的一个显著特征。

如果用户区有 100MB,而当前运行的程序只需要 30MB,那么剩下的 70MB 空间会被浪费掉,且这部分空间无法分配给其他程序使用。这部分被浪费在分配分区内部的空间就叫作内部碎片。


4. 优缺点评价

优点:

  • 实现简单: 操作系统不需要复杂的数据结构(如空闲分区表)来管理内存。
  • 开销极小: 不需要复杂的地址变换或分配算法,运行效率高。
  • 无需高级硬件: 对 CPU 的 MMU 功能要求极低。

缺点:

  • CPU 利用率低: 由于内存中只能有一个程序,一旦该程序在等待 I/O(如等待键盘输入),CPU 就会闲置。
  • 内存浪费严重: 容易产生较大的内部碎片。
  • 无法并发: 不支持多道程序设计,用户不能边听歌边写文档。

5. 适用场景

  • 早期操作系统: 如 CP/M, MS-DOS。
  • 简单的嵌入式系统: 在一些功能极其单一、资源非常有限的单片机应用中,依然可能采用这种模式。
  • 现代特例: 在某些极高性能需求的实时系统中,为了消除动态分配带来的不确定性(抖动),可能会为特定任务预留固定的大块连续内存。

总结对照

特性单一连续分配
并发性不支持(单任务)
分区个数1 个用户分区
碎片类型内部碎片
内存利用率

随着计算机技术的发展,为了克服这种方式无法运行多个程序的缺陷,后来演进出了固定分区分配动态分区分配

2.固定分区分配

固定分区分配 (Fixed Partitioning) 是最早出现的、可用于多道程序系统的存储管理方式。它是从“单一连续分配”向“动态分区分配”演进的过渡技术。

其核心思想是:将内存的用户空间划分为若干个固定大小的分区,每个分区只装入一个作业。


1. 划分分区的方式

根据分区大小是否相同,可以分为两种情况:

① 分区大小相等

  • 做法: 所有练习区的大小都一样(例如每个都是 8MB)。
  • 缺点: 缺乏灵活性。如果程序很大,放不下;如果程序很小(如 1KB),会造成严重的内存浪费。
  • 适用场景: 适用于控制多个相同对象的场合(如工业控制中完全相同的传感器处理程序)。

② 分区大小不等

  • 做法: 划分多个小分区、适量的中分区和少量的大分区。
  • 优点: 增加了灵活性,可以根据程序的大小将其分配到最合适的“坑”里,提高了内存利用率。

2. 内存管理数据结构:分区说明表

为了管理这些分区,操作系统维护一张分区说明表 (Partition Explanation Table)。表中记录了每个分区的状态。

分区号大小 (MB)起始地址状态
1420k已分配
2824k空闲
31632k空闲
43248k已分配

3. 分配与回收过程

  • 分配: 当一个作业准备装入时,查询分区说明表,找出一个空闲大小足够的分区分配给它,并将状态修改为“已分配”。
  • 回收: 当作业运行结束释放内存时,只需将对应分区号的状态改回“空闲”即可。

4. 优缺点分析

优点:

  1. 支持多道程序: 内存中可以同时运行多个任务,显著提高了 CPU 利用率。
  2. 实现简单: 管理逻辑非常直接,不需要复杂的搜索算法(如首次适应算法)。
  3. 无外部碎片: 所有的空闲空间都是整块的分区,不存在因为太小而无法利用的空闲块。

缺点:

  1. 内部碎片 (Internal Fragmentation) 严重: 只要作业大小小于分区大小,分区内剩下的空间就无法被利用。这是该模式最大的弊端。
  2. 作业大小受限: 如果一个超大型作业的大小超过了内存中最大的那个分区,该作业就无法运行。
  3. 并发数固定: 分区的个数在系统初始化时就定死了,这限制了系统同时运行的进程总数。

5. 总结:与单一连续分配的区别

特性单一连续分配固定分区分配
支持任务数1个多个 (N个)
内存利用率极低中等 (受限于内部碎片)
灵活性极差一般
适用环境单用户系统早期多道程序系统

演进方向:

为了解决固定分区带来的“内部碎片”和“灵活性差”的问题,操作系统随后发展出了动态分区分配(按需划分大小),以及后来更高级的分页存储管理。

动态分区分配 (Dynamic Partitioning) 是多道程序环境下一种非常灵活的内存管理方案。它的基本原理可以用一句话概括:不预先划定分区,而是在进程装入内存时,根据进程的大小动态地建立分区。

这种方式使得分区的大小正好等于进程的大小,分区的数据和位置也会随着进程的进入和退出而不断变化。


3.动态分区分配的基本原理

1. 核心运行机制

动态分区分配的运行过程遵循“按需切割”的原则:

  1. 初始状态: 除去操作系统占据的区域外,整个用户内存空间是一个巨大的空闲块。
  2. 分配过程: 当一个新进程请求 S 大小的内存时,系统在空闲区域中寻找一个大于或等于 S 的块。
    • 如果找到的块正好等于 S,则全部分配。
    • 如果块大于 S,则将其切割成两部分:一部分分配给进程,另一部分仍保留为空闲块。
  3. 回收过程: 当进程运行结束释放内存时,系统将其占用的空间收回,并检查是否可以与相邻的空闲空间合并。

2. 实现原理的三大要素

要实现这种动态分配,操作系统必须解决以下三个核心问题:

① 数据结构:如何记录空闲空间?

系统必须记录哪些内存是空闲的,通常使用以下两种结构:

  • 空闲分区表: 记录每个空闲分区的序号、起始地址、长度及状态。
  • 空闲分区链: 在每个空闲分区的起始位置设置控制信息,并通过指针将所有空闲块连成一个双向链表。

② 分配算法:选哪块地?

当有多个空闲块满足需求时,系统需要通过算法决定分配哪一块。正如我们之前讨论的,常见的算法包括:

  • 首次适应 (First Fit):求快,找第一个。
  • 最佳适应 (Best Fit):求省,找最接近需求大小的(会产生小碎片)。
  • 最坏适应 (Worst Fit):求大,找最大的分(剩下的还能用)。

③ 回收逻辑:如何合并邻居?

为了防止内存碎片化,回收时必须执行合并 (Coalescing)。当一个分区 P 被释放时,系统会检查:

  • P 的上方是否空闲? 若是,合并。
  • P 的下方是否空闲? 若是,合并。
  • P的上下方是否空闲?若是,合并。
  • P的上下方是否空闲?若不是,插入空闲分区表这确保了内存中不会出现两个相邻的空闲分区,从而尽可能保持空闲空间的大尺寸。

3. 动态分区分配的致命伤:外部碎片

虽然动态分区分配消除了“内部碎片”(分配给进程的空间全都被用上了),但它产生了一个新的问题:外部碎片 (External Fragmentation)

  • 定义: 随着进程频繁地进入和退出,内存中会产生许多细小的、不连续的空闲空间。
  • 后果: 虽然这些小碎片的总和可能很大,但因为它们不连续,无法容纳任何新的大进程。

解决原理:紧缩 (Compaction)

为了解决外部碎片,操作系统需要执行“拼图”操作。

  • 原理: 移动内存中所有已分配的进程,使它们向内存的一端靠拢,从而将原本分散的小碎片拼接成一个巨大的连续空闲区。
  • 前提: 必须拥有动态重定位(MMU 硬件支持)的技术背景,因为进程在内存中的物理地址在紧缩后发生了改变。

4. 动态分区分配小结

维度特性描述
分区建立时间程序装入时动态建立
分区大小等于进程大小
优点无内部碎片,利用率比固定分区高
缺点存在外部碎片,分配算法有开销
核心挑战如何在分配速度与内存利用率之间取得平衡

思考:

动态分区分配虽然比固定分区进步了很多,但它依然要求进程必须占用连续的内存空间。

想必您已经察觉到了,如果能让一个进程的数据“散落”在内存的各个角落(不连续存放),那么外部碎片的问题是不是就彻底解决了?这正是分页存储管理 (Paging) 的由来。

4.基于顺序搜索的分配算法

基于顺序搜索的分配算法是动态分区分配(Dynamic Partitioning)中用于寻找合适空闲内存块的策略。

这类算法的核心逻辑是:系统维护一个空闲分区链(或空闲分区表),当有进程请求内存时,系统按照某种顺序搜索该链表,找到一个满足大小要求的空闲分区并进行分配。

常见的基于顺序搜索的分配算法有四种:


1. 首次适应算法 (First Fit, FF)

  • 基本思想: 每次分配内存时,都从空闲分区链的起始端开始顺序查找,直到找到第一个大小能满足要求的空闲分区为止。
  • 特点:
    • 排序要求: 空闲分区通常按地址递增的顺序排列。
    • 优点: 分配速度快,且能尽可能地保留高地址的大空间。
    • 缺点: 内存的低地址部分会留下许多难以利用的小空闲块(碎片的堆积),增加了后续查找的开销。

2. 循环首次适应算法 (Next Fit, NF)

  • 基本思想: 由首次适应算法演变而来。它不再从头查找,而是从上一次查找结束的位置开始继续往下搜索。
  • 特点:
    • 结构支持: 通常将空闲分区链组织成一个循环链表
    • 优点: 使空闲分区的分布更加均匀,减少了查找空闲分区的平均时间。
    • 缺点: 容易导致整个内存空间都变得支离破碎,缺乏较大的连续空闲分区。

3. 最佳适应算法 (Best Fit, BF)

  • 基本思想: 搜索整个空闲分区链,找到能满足要求且与进程大小最接近(最小)的空闲分区进行分配。
  • 特点:
    • 排序要求: 空闲分区按容量从小到大排列。
    • 优点: 宏观上保留了内存中较大的空闲块,以便应对未来大进程的需求。
    • 缺点: 每次分配后剩下的那部分空间往往非常小,容易在系统中产生大量无法利用的外部碎片

4. 最坏适应算法 (Worst Fit, WF)

  • 基本思想: 与最佳适应算法相反。它总是扫描整个链表,选择最大的空闲分区分配给进程。
  • 特点:
    • 排序要求: 空闲分区按容量从大到小排列。
    • 优点: 剩下的空闲分区通常仍然很大,足以装入其他进程,从而减少了极小碎片的产生。
    • 缺点: 会迅速耗尽系统中最大的那些连续分区,导致后续出现的大进程无法运行。

算法综合对比表

算法名称搜索起始点排序依据核心优势核心缺陷
首次适应 (FF)链首地址递增简单、高地址保留大块低地址碎片多
循环首次适应 (NF)上次位置地址递增/循环空间分布均匀缺乏大连续空间
最佳适应 (BF)全链搜索容量递增节省大块内存产生极小碎片
最坏适应 (WF)链首 (最快)容量递减剩余块大,好利用大块很快被拆散

实际应用中的选择

在实际的操作系统实现中:

  • 首次适应算法 (FF) 通常被认为表现最好,因为它在查找速度、碎片分布和保留大块空间之间取得了较好的平衡。
  • 最佳适应算法 (BF) 听起来很好,但实际应用中产生的极细碎“垃圾”空间最难处理。

提示: 所有的顺序搜索算法在处理大型复杂内存请求时,由于需要遍历链表,效率都会随分区数量增加而下降。为此,现代系统有时会采用索引搜索算法(如哈希算法或伙伴系统)。

5.基于索引搜索的分配算法

基于索引搜索的分配算法(Index-based Search Allocation Algorithms)是为了解决“顺序搜索算法”在处理大量空闲分区时效率低下的问题而设计的。

在顺序搜索(如首次适应、最佳适应)中,随着系统运行,空闲分区链会变得很长,每次分配内存都需要从头遍历,时间复杂度接近 O(n)。而索引搜索通过建立索引表分类链表,实现了更快的查找速度。

以下是三种最典型的索引搜索算法:


1. 快速适应算法 (Quick Fit)

又称为分类搜索算法。它将空闲分区按其容量大小进行分类,每种长度的空闲分区各单独设立一个空闲分区链表。

  • 实现原理: 系统维护一张管理索引表,表中的每个条目对应一种长度的分区链表头。
    • 例如:条目1指向所有 4KB 的空闲块链表;条目2指向所有 8KB 的块。
  • 分配过程: 当进程请求 X 大小的内存时,系统直接去索引表中找到大于或等于 X 的最小分类。从该链表中取下第一块分配即可。
  • 特点:
    • 优点: 搜索速度极快(接近 O(1)),分配时不需要分割大块,也不需要遍历。
    • 缺点: * 在回收时,将分区归还到对应分类链表的开销较大。
      • 容易产生内部碎片(如果请求的大小不在分类中,可能分配稍大的块)。

2. 伙伴系统 (Buddy System)

这是 Linux 内核等高性能操作系统中最著名的内存管理算法,它在分配速度和避免碎片之间取得了极佳的平衡。

  • 基本原理: 1. 所有空闲分区的大小均为 2^k 次幂(如 4KB, 8KB, 16KB…)。
    1. 如果请求的大小为 S,且满足 2^{k-1} < S \le 2^k,系统就在大小为 2^k 的空闲分区链中查找。
    2. 拆分: 如果该链表为空,就去 2^{k+1} 的链表中找一个块,将其平分为二。这两个大小相等的块被称为“伙伴” (Buddies)。其中一半分配出去,另一半挂入 2^k 链表。
    3. 合并: 当内存回收时,系统检查其“伙伴”块是否也空闲。如果是,则将两者合并成一个 2^{k+1} 的大块。
  • 特点:
    • 优点: 查找和释放都非常快(时间复杂度为 O(\log n));合并逻辑简单,能有效防止外部碎片。
    • 缺点: 仍然存在内部碎片(因为必须分配 2 的幂次大小,例如程序要 33KB,必须给 64KB)。

3. 哈希算法 (Hash Algorithm)

利用哈希表来加速对空闲分区的查找。

  • 实现原理: 根据空闲分区的大小作为键值(Key),通过哈希函数计算出其在哈希表中的位置。
  • 分配过程: 进程请求内存时,通过哈希运算直接定位到对应大小的空闲分区链表入口。
  • 特点:
    • 优点: 具有极高的搜索效率。
    • 适用场景: 当系统存在大量不同大小的空闲分区时,哈希算法能提供比简单分类(Quick Fit)更灵活的映射。

索引搜索算法对比表

算法名称核心数据结构搜索效率主要优点主要缺点
快速适应 (Quick Fit)分类索引表极高分配速度最快回收开销大、有内部碎片
伙伴系统 (Buddy System)二叉树/位图合并方便,平衡性好内部碎片(2的幂次限制)
哈希算法 (Hash)哈希表极高查找精准算法实现复杂

3.基于分页存储管理

1. 分页的基本概念

分页的核心是将内存空间和进程逻辑地址空间都打碎成等大的小块。

  • 物理端——页框 (Page Frame): 将内存空间分为若干个固定大小(如 4KB)的分区,也称为页帧或物理块。
  • 逻辑端——页 (Page): 将进程的逻辑地址空间也分为与页框大小相等的小区域,称为页或页面。

分配逻辑: 操作系统以页框为单位为各个进程分配内存空间。进程的每一页可以装入内存中任意一个不一定连续的页框中。


2. 分页 vs. 固定分区

图片提到,分页从形式上看类似于“块大小相等”的固定分区技术,但两者有本质区别:

特性固定分区分配分页存储管理
块的大小通常较大很小(如 4KB)
进程划分进程作为一个整体放入分区进程也按块大小划分为
连续性必须占用连续空间离散分配,页可以散落在内存各处
碎片情况产生大量内部碎片不产生外部碎片,只产生极小的内部碎片

3. 页内碎片 (Internal Fragmentation)

分页管理彻底消除了外部碎片,但由于进程的大小不一定是页面大小的整数倍,依然会产生内部碎片(也称页内碎片)。

  • 产生位置: 只会在进程的最后一个不完整的块申请主存空间时产生。
  • 碎片大小: 相对于整个进程来说,这种碎片是非常小的。
  • 数学期望: 平均每个进程只产生半个块大小的内部碎片,即:\text{平均页内碎片} = \frac{1}{2} \times \text{页面大小}例如,页面为 4KB 时,平均每个进程仅浪费 2KB 内存。

4. 总结:为什么要分页?

分页技术实现了离散分配,它打破了“程序必须占用连续物理内存”的限制。这使得内存的利用率大幅提升,因为只要内存中有任何空闲的页框,就可以分配给需要的进程,而不需要一整块庞大的连续空间。

1.分页存储的几个基本概念

在分页存储管理中,页面页的大小是决定系统性能与内存利用率的核心参数。根据您提供的资料,我们可以从以下几个维度来深入理解:

1. 基本定义

  • 页面 (Page): 进程的逻辑地址空间被划分为若干个固定大小的区域,这些区域被称为“页”或“页面”。
  • 页框 (Page Frame): 物理内存空间被划分为若干个与页大小相等的分区,称为“页框”、“页帧”或“物理块”。
  • 核心原则: 为了方便离散分配,页的大小必须与页框的大小完全相等

2. 页的大小 (Page Size)

页的大小通常由硬件决定,且为了方便地址转换,其大小一般选定为 2 的幂(例如 512B、4KB 或 8KB)。

为什么通常选 4KB?

图片中提到了以 4KB 为例的常见配置。选择页的大小时需要进行权衡:

页面大小内部碎片 (页内碎片)页表长度与内存占用I/O 效率
较小减少。每个进程平均产生的碎片仅为半个页大小。增加。由于页数增多,存放映射关系的“页表”会变得很大,占用更多内存。较低。频繁的页面调入调出(换页)会增加磁盘 I/O 开销。
较大增加。最后一个不完整的块产生的空闲空间会变大。减少。页数减少,页表占用的内存空间也随之缩小。较高。一次可以传输更多数据,减少了寻道时间和传输次数。

3. 页内碎片 (Internal Fragmentation)

分页管理虽然彻底消除了外部碎片,但会产生内部碎片(也称页内碎片):

  • 产生原因: 进程的大小往往不是页面大小的整数倍,导致最后一个页面可能无法填满。
  • 严重程度: 这种碎片相对进程整体大小来说是非常小的。
  • 数学期望: 在分页管理中,每个进程平均只产生半个块(页)大小的内部碎片

4. 总结对比

根据资料显示,分页思想的引入是为了尽量避免碎片的产生,提高内存利用率。它通过将程序“打碎”成小的页面,使得这些页面可以离散地存放在主存中任何可用的页框中。

核心结论:

  • 页与块大小相等是实现离散分配的前提。
  • 页内碎片可控,平均每个进程仅浪费 1/2 个页面的空间。

2.地址结构


1. 逻辑地址的两部分组成

一个逻辑地址 A 在分页系统中由以下两部分组成:

  • 页号 (Page Number, P): 决定了该地址属于进程的哪一页。它作为索引,用于查表(页表)以找到对应的物理块号。
  • 页内偏移量 (Page Offset, W 或 d): 也称为页内地址,表示该地址相对于所在页面起始地址的距离。

2. 地址结构的位权分配

在现代计算机(通常为 32 位或 64 位系统)中,地址结构通常如下所示(以 32 位地址为例):

位 (Bits)31 ………………………. 1211 ………………………. 0
含义页号 (P)页内偏移量 (d)
  • 页内偏移量: 如果页面大小为 4KB(即 2^{12} B),则偏移量部分占用 12 位。
  • 页号: 剩余的高位部分(如 32 – 12 = 20 位)用于表示页号。这意味着该系统最多支持 2^{20} 个页面。

3. 地址计算公式

如果已知页面大小为 L,逻辑地址为 A,则:

  • 页号 P = A / L(整除)
  • 页内偏移量 d = A \pmod L(取余)

为什么页大小通常是 2 的幂?

如资料所述,页的大小通常选定为 2 的幂。这样做最大的好处是,地址转换不需要进行复杂的算术运算。计算机只需要通过简单的“位截取”操作即可分离出页号和偏移量:

  • 低地址的 n 位(其中 2^n = L)直接就是偏移量。
  • 高地址部分直接就是页号。

4. 物理地址的合成

一旦通过页表找到了该页对应的“物理块号(页框号)”,系统就会将物理块号与原有的页内偏移量拼接在一起,形成最终的物理地址。


5. 总结

根据您的资料,分页地址结构的设计目标是实现离散分配并减少碎片:

  • 页号实现了进程逻辑空间到物理空间的映射。
  • 页内偏移量保证了在同一页面内的访问逻辑与连续分配一致。
  • 页大小(如 4KB)决定了偏移量的位数,并影响平均产生的页内碎片大小(平均 1/2 个块)。

3.页表

在分页存储管理系统中,页表 (Page Table) 是实现从逻辑页到物理页框映射的核心数据结构。它是操作系统为了管理进程内存,实现离散分配而建立的一张映射表。


1. 页表的作用

由于进程的“页”可以离散地装入内存中任何不一定连续的“页框”(物理块)中,操作系统必须有一种机制记录每一页在内存中的实际位置。页表的作用就是:记录进程逻辑页面与内存物理页框之间的对应关系。

2. 页表的结构

页表通常存储在主存中。一个典型的页表由多个页表项 (Page Table Entry, PTE) 组成:

页号 (Index)物理块号 (Frame Number)
03
18
21
  • 页号 (Page Number): 对应进程逻辑地址空间的划分区域。在实际存储中,页号通常作为页表的索引(下标),不占用实际的存储空间。
  • 物理块号 (Frame Number): 记录该页对应的内存物理地址起始块(页框号)。这是地址转换时最关键的数据。

3. 地址变换过程

当 CPU 尝试访问某个逻辑地址时,地址变换机构(硬件)会执行以下步骤:

  1. 分离地址: 将逻辑地址拆分为“页号”和“页内偏移量”。
  2. 查表: 以页号为索引,去该进程的页表中查找对应的“物理块号”。
  3. 合成地址: 将找到的“物理块号”与原有的“页内偏移量”拼接,得到最终的物理地址。

4. 页表带来的管理特性

  • 离散存储: 允许进程的各个页面分散存储在内存的任何可用页框中,不需要连续的大块空间。
  • 消除外部碎片: 分页管理不会产生外部碎片,因为任何一个空闲页框都可以被利用。
  • 内存浪费极小: 虽然会产生“页内碎片”(即进程最后一个不完整的块产生的浪费),但每个进程平均仅产生半个页面大小的内部碎片。

5. 存储与性能挑战

  • 存储开销: 随着进程逻辑地址空间的增大(如 64 位系统),页表本身会变得非常庞大。为了解决这个问题,引入了多级页表
  • 访问速度: 由于页表存放在内存中,每次访问数据都需要先查页表(访问一次内存),再取数据(访问第二次内存)。为了加速这一过程,硬件引入了 TLB (快表/转换后备缓冲器),用于缓存最近使用的页表项。

总结:

页表是将“连续的逻辑空间”映射到“离散的物理空间”的关键纽带。它使得操作系统能以极小的内部碎片代价,高效地管理和利用有限的物理内存。

4.基本地址变换机构

1. 核心组件

在地址变换过程中,硬件需要用到两个关键的寄存器:

  • 页表始址寄存器 (PTBR): 存放页表在内存中的起始物理地址。
  • 页表长度寄存器 (PTLR): 存放页表项的总数,用于地址越界检查。

2. 地址变换的具体流程

当进程要访问某个逻辑地址 A 时,地址变换机构会执行以下步骤:

  1. 自动拆分: 硬件根据页的大小(通常为 2 的幂,如 4KB),自动将逻辑地址 A 拆分为页号 P页内偏移量 d
  2. 越界检查: 将页号 P 与页表长度 PTLR 进行比较。如果 P \ge PTLR,说明地址越界,硬件会产生地址越界中断(Trap)。
  3. 定位页表项: 根据页表始址 PTBR 和页号 P,计算出该页对应的页表项在内存中的位置:\text{页表项物理地址} = PTBR + P \times \text{页表项大小}
  4. 读取物理块号: 从内存中读取该页表项,获取其对应的物理块号(页框号) b。
  5. 合成物理地址: 将物理块号 b 与页内偏移量 d 拼接,形成最终的物理地址。如果页面大小为 L,则物理地址 E = b \times L + d。
  6. 访问内存: CPU 根据计算出的物理地址 E 去内存中读取或写入数据。

3. 与“页内碎片”的关系

地址变换机构执行的是离散分配的映射逻辑。由于这种机制允许将逻辑上连续的页面映射到物理上离散的页框中,它彻底消除了外部碎片。

然而,正如您提供的资料所强调的:

  • 这种分配方式以页框(物理块)为单位。
  • 当进程的最后一部分数据不足一个完整块时,依然会产生页内碎片
  • 得益于 4KB 左右的小页面设计,每个进程平均产生的页内碎片仅为半个块大小。

4. 性能瓶颈:两次访存

在基本地址变换机构中,CPU 每次存取一个数据实际上需要两次访问内存

  1. 第一次: 访问内存中的页表,获取物理块号。
  2. 第二次: 根据计算出的物理地址,访问内存中的目标数据。

这种设计使得系统的处理速度几乎下降了一半。

5.页表存储管理

1. 分页存储管理的引入背景与特点

  • 解决碎片问题:固定分区会产生内部碎片,动态分区会产生外部碎片,这两种技术的内存利用率都较低。引入分页的思想是为了尽量避免碎片的产生。
  • 划分方式
    • 物理端:将内存空间分为若干个固定大小(如 4KB)的分区,称为页框、页帧或物理块。
    • 逻辑端:进程的逻辑地址空间也被分为与块大小相等的若干区域,称为或页面。
  • 分配单位:操作系统以页框为单位为各个进程分配内存空间。
  • 与固定分区的区别:虽然分页看起来像块大小相等的固定分区,但其块的大小相对分区要小得多,且进程运行按块申请主存可用空间。

2. 内存利用率与碎片

  • 消除外部碎片:分页管理不会产生外部碎片。
  • 页内碎片:进程只会在为最后一个不完整的块申请主存空间时产生内部碎片(也称页内碎片)。
  • 碎片大小:这种碎片相对进程而言很小,每个进程平均只产生半个块大小的内部碎片。

3. 页式管理的地址结构

  • 一维地址空间:由于页面大小 L 是固定的,只需给出一个整数即可确定对应的物理地址,因此页式管理中的地址空间是一维的。

4. 页表项(PTE)大小的确定

页表项的作用是找到该页在内存中的位置,其大小受到地址空间范围的约束。以 32 位内存地址空间、按字节编址、一页 4KB 为例:

  • 第一步:大楼里一共有多少个房间?(页面总数)
    • 已知条件:整栋大楼(32 位地址空间)总容量是 2^{32} 个字节。
    • 划分方式:每个房间(页面)的大小固定为 4\text{KB},即 2^{12} 个字节。
    • 计算结果:房间总数 = \frac{\text{总容量}}{\text{单间大小}} = \frac{2^{32}}{2^{12}} = 2^{20} 个房间(页)。
    • 结论:这意味着我们的“地图”上必须能标记出 2^{20}(约 100 万)个不同的物理位置。
    第二步:标记这些房间需要多长的编号?(位数需求)
    • 逻辑:如果你有 100 万个房间,你的编号(索引)至少要能数到 100 万。
    • 计算:在二进制中,要表示 2^{20} 个不同的数字,至少需要 20 位(bit) 的二进制长度。
    • 结论:页表项里的核心内容——“物理块号”,至少得占 20 位,否则就没法指向某些靠后的房间了。
    第三步:20 位编号需要占用几个字节?(字节换算)
    • 转换:计算机存储的基本单位是字节(Byte),1\text{Byte} = 8\text{bit}。
    • 计算:20\text{位} \div 8\text{位/字节} = 2.5\text{字节}。
    • 规则:内存分配不能给半个字节,必须向上取整,所以至少需要 3 个字节(3B) 才能存下这 20 位编号。
    第四步:为什么最后实际取 4B 而不是 3B?资料提到,实际应用中通常选 4\text{B},主要基于以下两个专业考量:
    1. 对齐与计算方便
      • 一个页面是 4\text{KB}(4096\text{B})。
      • 如果页表项是 4\text{B},那么一个页面恰好能装下 4096 \div 4 = 1024 个项,是个整数,计算位置非常快。
      • 如果是 3\text{B},4096 \div 3 \approx 1365.33,会导致页表项跨越页面边界,硬件处理起来极其低效。
    2. 预留功能位
      • 除了房间号,页表项还需要存储一些“状态信息”,比如这个页是否在内存中是否允许写入最近是否被访问过等控制信息。多出的位(32 位减去 20 位 = 12 位)正好用来存这些信息。

5. 分页管理存在的主要问题

  1. 转换速度:每次访存操作都需要进行逻辑地址到物理地址的转换,该过程必须足够快,否则会降低访存速度。
  2. 内存开销:每个进程引入页表用于存储映射机制,页表不能太大,否则会降低内存利用率。

6.具有快表的地址变化机构

在操作系统的内存管理中,具有快表的地址变换机构是为了解决分页系统效率问题而设计的核心硬件机制。

由于页表通常存放在内存中,如果不使用快表,CPU 每存取一个数据都需要两次访问内存(第一次访页表,第二次访数据),这会使处理速度降低近 50%。


1. 什么是快表 (TLB)?

快表,全称 转换后备缓冲区(Translation Lookaside Buffer, TLB),是一种容量小、但访问速度极快的关联存储器(Associative Memory)。

  • 位置:集成在 CPU 的 MMU(内存管理单元)内部。
  • 内容:存放当前进程最常用的部分页表项。
  • 原理:基于局部性原理,即程序在一段时间内往往只访问少数几个页。

2. 地址变换的具体流程

当 CPU 产生一个逻辑地址(由页号 P 和页内偏移量 W 组成)时,硬件机制会按以下步骤进行转换:

  1. 并行检索快表:将页号 P 送入快表。由于快表是关联存储器,可以同时对比所有条目。
  2. 快表命中 (TLB Hit)
    • 如果页号 P 在快表中,直接读出对应的物理块号 f。
    • 将 f 与页内偏移量 W 拼接,形成物理地址。
  3. 快表缺失 (TLB Miss)
    • 如果快表中没有该页号,则必须访问内存中的页表
    • 从页表寄存器找到页表始址,查询该页号对应的物理块号 f。
    • 更新快表:将该页表项同时写入快表,以便下次使用。如果快表已满,则按算法(如 LRU)替换。
    • 将 f 与 W 拼接,形成物理地址。

3. 性能对比

特性纯页表访问 (无快表)具有快表的访问
访存次数2 次 (页表 + 实际数据)命中时 1 次;缺失时 2 次
查询时间较慢 (受内存频率限制)极快 (纳秒级,与 CPU 同频)
平均周期固定 2 \times tHit \times (t_{TLB} + t) + (1 – Hit) \times (t_{TLB} + 2t)

:现代计算机的 TLB 命中率通常可达 90% – 99% 以上,这使得平均访存速度非常接近于直接物理寻址的速度。


4. 关键硬件组件

  • 页表寄存器 (PTR):存放页表在内存中的起始地址和长度。
  • 关联寄存器 (TLB):实现快速并行匹配。
  • 地址拼接逻辑:负责将块号与偏移量合并。

7.二级页表

在操作系统内存管理中,两级页表(Two-Level Page Table)是为了解决单级页表在面对现代大容量逻辑地址空间时,占用内存过大且必须连续存放的问题而引入的一种层次化管理机制。

以下是对两级页表的详细解析:

1. 为什么需要两级页表?

在 32 位系统中,如果页面大小为 4KB(2^{12}),则逻辑地址空间共有 2^{20} 个页。

  • 单级页表的问题:若每个页表项占 4 字节,则整个页表需要 2^{20} \times 4B = 4MB 的连续内存空间。
  • 内存浪费:进程往往只使用了部分地址空间,但单级页表要求所有页表项都必须存在且连续,这不仅浪费内存,还难以找到如此大的连续物理块。
  • 解决方案:将页表本身进行分页。即“对页表建立页表”。

2. 逻辑地址的划分

在两级页表结构下,32 位逻辑地址通常被划分为三个部分(以 4KB 页面为例):

  1. 一级页号(外层页号/页目录索引):通常占 10 位。用于在一级页表(页目录)中查找二级页表的始址。
  2. 二级页号(页表索引):通常占 10 位。用于在二级页表中查找最终的物理块号。
  3. 页内偏移量:占 12 位(对应 4KB 页面)。

地址格式示例:

[ 一级页号 P1 (10位) | 二级页号 P2 (10位) | 页内偏移量 W (12位) ]


3. 地址变换的过程

当 CPU 访问一个逻辑地址时,硬件(MMU)执行以下步骤:

  1. 查找一级页表:根据一级页表基址寄存器(CR3)找到一级页表,利用 P1 作为索引,找到对应的二级页表的起始物理地址。
  2. 查找二级页表:根据第一步找到的二级页表始址,利用 P2 作为索引,从中读出该页所在的物理块号(Frame Number)。
  3. 形成物理地址:将物理块号与页内偏移量 W 拼接,得到最终的物理地址。

4. 两级页表的优势

  • 离散存储:二级页表可以离散地存放在内存中,只有一级页表(页目录)需要连续存放。一级页表仅占用 4KB(1024 项 \times 4B),非常容易分配。
  • 按需分配:对于进程未使用的地址空间,不需要为其创建对应的二级页表。这大大节省了存放页表所消耗的物理内存。

5. 存在的缺点与弥补

  • 性能开销:单级页表访存需要 2 次(1次查表 + 1次取数),两级页表则需要 3 次(1次查一级表 + 1次查二级表 + 1次取数)。
  • 弥补措施:配合 快表 (TLB)。如果 TLB 命中,可以直接获取物理块号,将访存次数减少到 1 次。

总结

两级页表通过引入“页表索引”的层次结构,实现了页表的离散化和稀疏分配,是现代计算机支持大内存地址空间的核心技术。在 64 位系统中,由于地址空间更广,通常会使用三级甚至四级页表。

4.基本分段式存储

1.分段

在操作系统中,分段存储管理(Memory Segmentation) 是一种符合程序员逻辑视图的内存管理技术。它不像分页(Paging)那样将内存强行切分为固定大小的块,而是根据程序的逻辑结构(如函数、对象、堆栈等)将内存划分为大小不等的“段”。

以下是对分段系统的详细解析:


1. 核心概念:逻辑视图

在分段系统中,程序员眼中的程序不是一段连续的内存,而是一组逻辑单位的集合。

  • 代码段(Code Segment): 存储程序的指令。
  • 数据段(Data Segment): 存储全局变量。
  • 堆栈段(Stack Segment): 存储局部变量和函数调用信息。每个段都有一个名称(或编号)和长度,段与段之间在物理内存中不必连续。

2. 地址结构与转换

在分段系统中,逻辑地址是由两部分组成的二维地址:

\text{逻辑地址} = (\text{段号 } S, \text{ 段内偏移 } d)

段表(Segment Table)

为了实现从逻辑地址到物理地址的映射,系统为每个进程维护一张段表。段表的每个条目包含两个关键信息:

  1. 基址(Base): 该段在物理内存中的起始地址。
  2. 限长(Limit): 该段的长度(用于安全检查,防止越界)。

转换流程:

  1. CPU 生成逻辑地址 (S, d)。
  2. 硬件根据 S 在段表中找到对应的条目。
  3. 安全性检查: 检查偏移量 d 是否超过了限长 Limit。如果超过,产生越界中断(Segment Fault)。
  4. 计算物理地址: 物理地址 = 基址 Base + d。

3. 分段系统的优缺点

特性优势 / 描述
逻辑性强符合程序员对程序的理解,方便模块化编程和链接。
共享与保护容易实现。例如,可以将“代码段”设为只读并让多个进程共享。
无内部碎片段的大小是按需分配的,不会像分页那样产生页内浪费。
外部碎片主要缺点。由于段的大小不等,内存频繁分配后会产生大量零散的空隙,需要“紧凑(Compaction)”技术。

4. 分段 vs. 分页:关键区别

维度分页 (Paging)分段 (Segmentation)
基本单位固定大小的页(如 4KB)变长的逻辑段
视角物理视图(对硬件友好)逻辑视图(对程序员友好)
地址空间一维(连续数字)二维(段号 + 偏移)
碎片有内部碎片,无外部碎片无内部碎片,有外部碎片

5. 现代应用:段页式存储

由于纯分段系统容易产生外部碎片,而纯分页系统逻辑性较差,现代操作系统(如 Linux, Windows)通常采用段页式存储管理(Paged Segmentation)

  • 做法: 先将程序按逻辑分成“段”,再将每个“段”进一步划分为固定大小的“页”。
  • 优势: 既保留了分段在逻辑保护和共享上的便利,又利用分页解决了外部碎片和内存分配难题。

2.段表

1. 段表的基本结构

段表由多个段表项(Segment Table Entry)组成,每个条目对应程序的一个逻辑段(如代码段、数据段、堆栈段)。一个标准的段表项通常包含以下核心字段:

字段名称作用
段号 (Segment Number)逻辑段的编号(通常作为访问段表的索引,不占物理存储空间)。
段长 (Limit/Length)该段的逻辑大小。用于检查地址是否越界。
基址 (Base Address)该段在物理内存中的起始地址。
存取控制 (Access Rights)权限标识(如:只读、读写、可执行)。
状态位 (Presence Bit)标识该段当前是否已加载到内存中(用于虚拟存储)。

2. 段表在地址转换中的作用

当 CPU 发出一个逻辑地址 (S, d)(S 为段号,d 为段内偏移)时,硬件(MMU)会通过以下步骤查表:

  1. 定位段表项: 根据段号 S,在内存的段表中找到对应的第 S 个条目。
  2. 越界检查(重要):
    • 系统会将段内偏移 d 与段表项中的段长进行比较。
    • 如果 d \ge 段长,说明程序试图访问非法地址,触发越界中断(Segmentation Fault)。
  3. 计算物理地址:
    • 如果检查通过,物理地址 = 基址 + 段内偏移 d

3. 硬件支持:段表寄存器

为了加快访问速度,CPU 内部通常有一个段表控制寄存器

  • 段表始址寄存器(STBR): 保存当前进程段表在内存中的起始物理地址。
  • 段表长度寄存器(STLR): 保存当前进程段表的总条数(用于检查段号 S 是否合法)。

访问一次数据需要几次内存访问?

在基本的分段系统中,需要两次内存访问:

  1. 第一次:访问内存中的段表,获取基址。
  2. 第二次:根据计算出的物理地址,访问实际的数据/指令。(注:现代系统会使用 TLB(快表) 缓存常用的段表项,从而将大多数访问优化为接近一次。)

4. 段表的特点与优势

  • 变长存储: 段表中的每一项所指向的物理内存块大小是不相等的,这与分页(固定大小)有本质区别。
  • 易于共享: 如果两个进程要共享某段代码(如库函数),只需让它们的段表项指向同一物理基址,并设为“只读”即可。
  • 逻辑隔离: 段表可以方便地为不同性质的数据设置不同的保护级别(例如,代码段禁止写入,数据段允许读写)。

5. 段表 vs 页表 (简要对比)

  • 段表: 二维地址,反映逻辑结构,长度不固定,存在外部碎片。
  • 页表: 一维地址,反映物理结构,长度固定,存在内部碎片(页内碎片)。

3.地址变换机构

在分段存储管理系统中,地址变换机构是负责将程序员使用的“二维”逻辑地址转换为内存物理地址的核心硬件逻辑(通常集成在 MMU,即内存管理单元中)。

其主要任务是根据段号找到对应的段描述符,并进行合规性检查,最后通过偏移量计算出实际地址。


1. 核心硬件组件

地址变换机构的运行依赖于 CPU 中的两个关键寄存器:

  • 段表始址寄存器 (STBR): 存放当前进程段表在内存中的起始物理地址
  • 段表长度寄存器 (STLR): 存放当前进程段表的总段数,用于判断段号是否非法。

2. 地址变换的具体流程

假设 CPU 产生了一个逻辑地址 (S, d),其中 S 是段号,d 是段内偏移。地址变换机构会按以下步骤操作:

  1. 段号合法性检查:
    • 将逻辑地址中的段号 S 与段表长度 STLR 进行比较。
    • 如果 S \ge STLR,说明段号越界,产生地址越界中断
  2. 查找段表项:
    • 根据段表始址 STBR 和段号 S,计算出目标段表项在内存中的位置。
    • \text{段表项地址} = STBR + S \times \text{段表项长度}
  3. 从内存读取段表项:
    • 获取该段的物理基址 (Base)段长 (Limit)
  4. 段内偏移合法性检查:
    • 将逻辑地址中的段内偏移 d 与该段的段长 Limit 比较。
    • 如果 d \ge Limit,说明偏移量超过了该逻辑单元的大小,产生越界中断(即常见的 Segmentation Fault)。
  5. 形成物理地址:
    • 检查通过后,将段基址与段内偏移量相加,得到最终的物理内存地址。
    • \text{物理地址} = \text{基址 (Base)} + d

3. 特点与优化

  • 两次内存访问: 在基本机构中,访问一次数据需要访问两次内存(第一次查段表,第二次取数据)。
  • 引入快表 (TLB): 为了提高速度,硬件通常会设置一个高速缓冲寄存器(TLB),用来存放最近使用的段表项。如果命中 TLB,则无需访问内存中的段表,速度大幅提升。
  • 动态增长: 相比于分页系统,分段系统的地址变换更具逻辑性,允许某些段(如堆栈段)在运行期间根据需要动态改变长度。

4. 常见异常情况

在地址变换过程中,机构会持续监控:

  • 越界错误: 访问了不属于该进程的段,或访问了段以外的地址。
  • 存取违规: 例如尝试向一个“只读”的代码段写入数据,地址变换机构会根据段表项中的保护位拦截此类操作。

4.分页和分段对比

在操作系统内存管理中,分页(Paging)分段(Segmentation)是两种最基础且重要的非连续内存分配技术。虽然它们都解决了“内存不连续分配”的问题,但出发点和实现细节有本质区别。

以下是两者的详细对比分析:


1. 核心差异对比表

特性分页 (Paging)分段 (Segmentation)
基本单位固定大小的页(Page)变长的逻辑段(Segment)
视角物理视角:旨在提高内存利用率逻辑视角:符合程序员对程序的理解
地址空间一维:地址是连续的数字二维:(段号, 段内偏移)
大小设定硬件决定(如 4KB)程序员/编译器决定(按功能分)
碎片问题无外部碎片,但有内部碎片无内部碎片,但有外部碎片
可见性对用户/程序员透明(不可见)对用户/程序员可见
共享与保护较难(因为页没有逻辑意义)容易(以逻辑单位进行权限控制)

2. 深入理解:逻辑 vs 物理

  • 分页的本质是“切碎”:它把物理内存看作一堆大小相等的“抽屉”(页框),把程序也切成同样大小的块塞进去。这主要是为了解决内存分配困难,避免因为找不到足够大的连续空间而导致内存浪费。
  • 分段的本质是“分类”:它按照程序的逻辑功能(如:主程序、子程序、全局变量区、堆栈)进行划分。程序员知道哪个段是代码,哪个段是数据。

3. 地址转换机构的区别

  • 分页系统:
    • 逻辑地址被硬件自动分为:页号 + 页内偏移
    • 通过页表查找该页对应的物理块号。
    • 物理地址 = 块号 \times 页面大小 + 偏移。
  • 分段系统:
    • 逻辑地址由用户显式给出:段号 + 段内偏移
    • 通过段表查找该段的基址限长
    • 安全性检查:偏移量不能超过限长。
    • 物理地址 = 基址 + 偏移。

4. 优缺点总结

分页 (Paging)

  • 优点: 内存空间利用率高,不会产生外部碎片;离散分配极大简化了内存管理。
  • 缺点: 页面没有逻辑意义,不便于实现数据的保护与共享。

分段 (Segmentation)

  • 优点: 方便共享代码和保护数据;段长可以根据动态增长的需求进行扩充。
  • 缺点: 容易产生外部碎片(即内存中存在很多很小、无法利用的空隙)。

5. 现代系统的折中方案:段页式存储

为了结合两者的优点,现代操作系统(如 Linux, Windows)普遍采用段页式存储管理

  1. 先分段:按逻辑意义将程序划分为若干段,方便共享和保护。
  2. 再分页:对每个段进行分页,解决物理内存分配产生的外部碎片问题

5.段的共享和保护

在分段存储管理中,共享保护是其相对于分页管理最突出的优点之一。因为“段”是逻辑单位(如一个函数、一个库或一个数据区),这使得对特定功能模块的操作变得非常直观。

以下是关于段的共享与保护的详细说明:


一、 段的共享 (Segment Sharing)

在多用户或多任务环境下,多个进程经常需要调用相同的程序代码(如共享库、编译器或公共插件)。

1. 实现机制

  • 原理:让不同进程的段表项指向物理内存中同一个基地址
  • 配置:虽然物理地址相同,但每个进程在自己的逻辑空间中可以有不同的“段号”。
  • 例子:如果有 10 个用户都在使用同一个文本编辑器,内存中只需保留一份编辑器的“代码段”,而每个用户拥有独立且私有的“数据段”(用于存放各自编辑的文档内容)。

2. 必要条件:纯代码 (Reentrant Code)

为了保证共享安全,被共享的段必须是可重入代码(也叫纯代码):

  • 这种代码在执行过程中不会修改自身
  • 所有的本地变量和数据都保存在寄存器或进程私有的栈中。
  • 这样,多个进程并发执行该段代码时,互不干扰。

二、 段的保护 (Segment Protection)

由于段具有明确的逻辑边界,操作系统可以非常精确地控制每个段的访问权限。

1. 越界检查 (Boundary Protection)

这是最基本的安全机制,由硬件(MMU)自动完成:

  • 段表长度检查:检查逻辑地址中的“段号”是否超出了段表的大小。
  • 段长检查:在地址转换时,比较“段内偏移 d”与段表项中的“段长 (Limit)”。如果 d > Limit,系统立即抛出 Segmentation Fault(段错误)并终止进程。

2. 存取控制检查 (Access Control)

每个段表项中都有一组保护位(Protection Bits),定义了该段允许的操作:

  • 只读 (Read-only):常用于常量段。
  • 读/写 (Read/Write):用于数据段或堆栈段。
  • 只执行 (Execute-only):用于代码段(防止代码被恶意篡改)。

如果一个进程试图向“只执行”的代码段写入数据,硬件会拦截该操作并触发保护异常。

3. 环位保护 (Privilege Levels)

在更复杂的架构(如 x86)中,还会引入特权级(Ring 0 – Ring 3)

  • 内核段处于高特权级(Ring 0),普通应用程序(Ring 3)无法直接访问或跳转到高特权级的段中,必须通过“门(Gate)”机制受控访问。

三、 为什么分段比分页更易于实现共享与保护?

  • 分页的局限:页是物理单位,大小固定且没有逻辑意义。一个函数可能跨越两个页面,而这两个页面可能还包含其他不相关的代码。如果要设置“只读”,可能会误伤到不该设为只读的部分。
  • 分段的优势:段是逻辑单位。一个段就是一个完整的函数或模块。给一个段设置“共享”或“只读”,正好对应整个逻辑实体,不会出现“误伤”情况。

总结:

  • 共享是通过多个映射指向同一物理块实现的,节省了大量内存。
  • 保护是通过硬件检查“段长”和“权限位”实现的,确保了进程间的隔离和系统安全。

3.段页式存储管理

段页式存储管理(Paged Segmentation) 是分段和分页两种内存管理技术的结合。它既保留了分段系统便于逻辑保护、共享和动态增长的优点,又利用分页系统解决了内存外部碎片的问题。

这是现代操作系统(如 Linux 和 Windows)实际应用最广泛的一种存储管理方式。


1. 基本思想

段页式管理的原则是:“先分段,再分页”

  1. 作业按段划分:首先将程序按照逻辑功能划分为若干个(如代码段、数据段、堆栈段)。
  2. 段内按页划分:对于每一个段,不再要求它在内存中连续存放,而是将其进一步划分为固定大小的
  3. 内存分配:物理内存被划分为与页面大小相同的页框(Frame),以页为单位离散地存储各段内容。

2. 地址结构

在段页式系统中,逻辑地址由三部分组成(三维地址):

\text{逻辑地址} = (\text{段号 } S, \text{ 页号 } P, \text{ 页内偏移 } d)

  • 段号 S:决定访问哪个段。
  • 页号 P:决定在该段内的哪一页。
  • 页内偏移 d:决定在页内的具体字节位置。

3. 数据结构:段表与页表

为了实现地址转换,系统需要维护两级表:

  1. 段表(Segment Table)
    • 每个进程只有一个段表。
    • 内容:每个段表项不再包含基址,而是包含该段对应的页表始址页表长度(即该段有多少页)。
  2. 页表(Page Table)
    • 每个段都有一个属于自己的页表
    • 内容:页面存放的物理块号。

4. 地址变换过程

当 CPU 给出逻辑地址 (S, P, d) 时,地址变换机构的操作流程如下:

  1. 段号校验:比较段号 S 与段表长度。若越界,触发中断。
  2. 查询段表:根据 S 找到对应的段表项,获取该段的页表起始地址
  3. 页号校验:将地址中的页号 P 与该段的页表长度比较。若越界(即试图访问段外空间),触发越界中断。
  4. 查询页表:根据页表起始地址和页号 P,找到该页对应的物理块号 b
  5. 形成物理地址:物理地址 = 物理块号 b \times 页面大小 + 页内偏移 d。

5. 性能问题与优化

三次访存(Memory Access)问题:

在段页式系统中,获取一个数据需要访问三次内存:

  • 第一次:访问内存中的段表,获取页表地址。
  • 第二次:访问内存中的页表,获取物理块号。
  • 第三次:根据物理地址访问真正的数据。

优化方案:

为了弥补速度损失,硬件通常使用 快表 (TLB)。TLB 会缓存常用的 (段号, 页号) -> 物理块号 的映射。如果命中 TLB,仅需一次访存即可完成,大大提高了效率。


6. 段页式管理的优缺点

特性描述
解决碎片彻底消除了分段系统产生的外部碎片,内存利用率高。
逻辑性维持了段的逻辑特征,极大地方便了代码共享存储保护
灵活性段可以根据需要动态增长,而无需寻找大片连续空间。
复杂性硬件实现更复杂(需要管理多级表),管理开销稍大。

总结:

段页式存储管理是内存管理的“集大成者”。它用分段的形式满足了程序员的逻辑需求(方便共享、保护和编程),用分页的形式满足了硬件的物理需求(解决外部碎片、方便分配)。

4.补充

  1. A. 存储保护的目的是限制内存的分配(错误)
    • 解析: 存储保护(Memory Protection)的主要目的是确保进程之间互不干扰,防止一个进程访问或破坏其他进程或操作系统的内存空间。它关乎的是安全性与隔离,而不是限制分配的总量。
    B. 在内存为 M、有 N 个用户的分时系统中,每个用户占用 M/N 的内存空间(错误)
    • 解析: 这种说法过于绝对。在分时系统中,每个用户进程的大小通常是不等的。此外,操作系统本身也需要占用一部分内存空间,实际分配是根据进程的需求动态进行的。
    C. 在虚拟内存系统中,只要磁盘空间无限大,作业就能拥有任意大的编址空间(错误)
    • 解析: 虚拟地址空间的大小主要受计算机架构(字长)的限制。例如,在一个 32 位的系统中,最大编址空间只能是 2^{32} = 4\text{GB};即便磁盘有无限大,CPU 也无法寻址超过其位数限制的范围。
    D. 实现虚拟内存管理必须有相应硬件的支持(正确)
    • 解析: 虚拟内存的实现依赖于硬件机构,主要是 MMU(内存管理单元)。硬件需要负责逻辑地址到物理地址的快速转换、缺页中断的产生以及存储保护位的检查。如果没有硬件支持,仅靠软件来实现这种高频的地址转换,效率将低到无法接受。
  2. 实现虚拟内存通常需要以下三大硬件/软件支撑:一定容量的内存和外存:提供存储基础。页表/段表机制:实现逻辑地址到物理地址的映射。中断机构:当访问的页面不在内存时,产生“缺页中断”来请求调入。
  3. 关于存储管理目标的说法错误答案:D
    • A. 为进程分配内存空间(正确目标)
      • 这是内存管理最基本的功能,即确定如何为新创建或新换入的进程分配物理内存。
    • B. 回收被进程释放的内存空间(正确目标)
      • 当进程运行结束或主动释放资源时,系统必须及时收回内存以供其他进程使用。
    • C. 提高内存的利用率(正确目标)
      • 通过分页、分段或紧凑(Compaction)等技术,尽量减少内部和外部碎片,让有限的物理内存承载更多的进程。
    • D. 提高内存的物理存取速度(错误目标)
      • 原因:内存的物理存取速度(即硬件的响应时间)是由硬件物理特性(如 DDR 颗粒的频率)决定的。操作系统管理软件可以优化管理效率,但无法改变硬件本身的物理读写速度
  4. 维度说明实施主体硬件(主) + 软件(辅)。硬件负责实时监控,软件负责设置标准。主要目标确保每个进程只在自己的地址空间内活动,保护操作系统不被用户程序破坏。触发结果一旦发生越界访问,硬件产生越界中断(内部异常),CPU 切换到内核态处理。
  5. 存储管理方式重定位方式是否需要硬件变换机构关键硬件单一连续分配静态重定位 / 绝对装入否-固定分区分配静态重定位否-动态分区分配动态重定位重定位寄存器页式存储管理动态重定位页表寄存器 / MMU页式虚拟存储动态重定位MMU / TLB
  6. A. 系统将程序装入内存后,程序在内存中的位置可能发生移动:
    • 正确。 动态重定位最大的优点就是支持内存紧凑(Compaction)*和*对换(Swapping)。因为地址转换是在运行时进行的,只要更新重定位寄存器的值,程序就可以在内存中移动位置而不会出错。
    B. 系统为每个进程分配一个重定位寄存器:
    • 错误(本题答案)。 重定位寄存器(也称基址寄存器)是 CPU 中的硬件资源。通常情况下,CPU 中只有一个(或一组)物理重定位寄存器。系统并不会为每个进程“分配”一个物理寄存器,而是在进行进程切换(Context Switch)时,由操作系统将当前运行进程的起始地址装入这个硬件寄存器中。
    C. 被访问单元的物理地址 = 逻辑地址 + 重定位寄存器的值:
    • 正确。 这是动态重定位地址转换的标准公式:物理地址 = 逻辑地址 + 基址(重定位寄存器的值)。
    D. 逻辑地址到物理地址的映射过程在进程执行时发生:
    • 正确。 这正是“动态”重定位的定义。它将地址绑定推迟到执行时(Execution-time),由硬件地址转换机构(MMU)自动完成。
  7. 题目解析动态重定位(Dynamic Relocation)是指在程序执行过程中,每当 CPU 访问内存时,才由硬件地址变换机构将逻辑地址转换为物理地址的过程。以下是各选项的详细分析:
    • II. 重定位寄存器 (Relocation Register):必备硬件。 在动态重定位中,系统会设置一个重定位寄存器(也称为基址寄存器),用来存放程序在内存中的起始物理地址。
    • III. 地址变换机构 (Address Translation Mechanism):必备机构。 这是由硬件实现的物理电路(通常是 MMU 的一部分),它在程序运行时自动将“逻辑地址”与“重定位寄存器中的值”相加,生成真实的“物理地址”。
    为什么不选 I 和 IV?
    • I. 可重定位装入程序 (Relocatable Loader): 这是静态重定位所依赖的工具。静态重定位是在程序装入内存时,由软件(装入程序)一次性修改程序中的所有地址。而动态重定位在装入时并不修改代码,地址转换推迟到了执行阶段。
    • IV. 目标程序 (Object Program): 目标程序是重定位的对象,而不是重定位过程所“依赖”的机制或工具。
  8. 装入方式地址转换时间进程在内存中是否可移动适用场景绝对装入编译/汇编时不可移动单道程序环境静态重定位装入时不可移动早期多道程序,无硬件支持动态重定位执行时可移动现代操作系统,支持紧凑技术
  9. 分类典型算法特点基于顺序搜索首次适应 (FF)、循环首次适应 (NF)、最佳适应 (BF)、最坏适应 (WF)结构简单,但当分区链很长时,搜索效率较低。基于索引搜索快速适应 (Quick Fit)伙伴系统 (Buddy System)哈希算法 (Hash)搜索速度快,适用于大、中型系统,但管理开销略大。
  10. 执行主体主要职责执行时机硬件 (MMU)查找页表、地址转换、权限检查指令执行期间(实时)操作系统 (OS)建立与维护页表、处理缺页异常进程创建、调度、异常时
  11. 可重入程序(Reentrant Program)**是一种纯代码程序,即它在运行过程中不会修改自身代码,其局部数据通常存储在调用者的寄存器或栈中。
    • 共享特性: 多个用户或进程可以同时调用同一个物理内存中的程序副本,而不需要为每个用户都加载一份拷贝。
    • 改善性能的逻辑:
      1. 由于多个进程共享同一份代码,系统占用的内存空间大大减少
      2. 内存压力减小后,系统就不需要频繁地将内存中的内容置换到外存(磁盘)上。
      3. 因此,对换(Swapping)动作的数量减少,降低了磁盘 I/O 开销,从而提高了系统整体性能。
  12. 1. 为什么选择 B(以字节或字为单位)?主存储器是计算机中存放程序和数据的核心部件。从硬件物理结构指令执行的角度来看:
    • 编址单位: 现代计算机的主存通常是按字节(Byte)编址的,或者是按字(Word)编址。
    • 访问方式: 当 CPU 执行一条访存指令(如 Load 或 Store)时,它通过地址总线送出的是一个具体的内存地址。这个地址指向的是主存中的一个特定存储单元,这个单元的大小通常就是一个字节或一个字。
    • 精确性: 为了能处理各种大小的数据(如字符、整数等),硬件必须支持细粒度的随机访问,因此“字节”或“字”是最基本的访问单位。
    2. 为什么其他选项是错误的?
    • A. 以块(页)或段为单位
      • 错误原因: “块”或“页”通常是操作系统在进行内存管理(如分页存储管理)时使用的逻辑单位,或者是主存与磁盘(辅存)之间进行信息交换的单位。在物理硬件层面上,CPU 访问内存依然是细化到字节或字的。
    • C. 随存储器的管理方案不同而异
      • 错误原因: 无论操作系统采用哪种管理方案(如固定分区、分段、分页),底层的硬件访存机制是不变的。管理方案改变的是地址转换的方式(逻辑地址转物理地址),而不是主存物理访问的基本单位。
    • D. 以用户的逻辑记录为单位
      • 错误原因: “逻辑记录”是文件系统或数据库中的概念,属于软件应用层的数据组织方式。硬件内存完全不理解什么是“记录”,它只识别地址和二进制位。
  13. 在段页式存储管理中,系统结合了“分段”和“分页”两种技术的优点。它的地址转换层级如下:1. 结构层次
    • 进程级别: 为了管理一个进程的所有段,系统为每个进程分配一张段表
    • 段级别: 在段页式系统中,每一个“段”不再是连续的物理内存,而是被进一步划分为若干个固定大小的“页”。因此,每一个段都需要一张对应的页表来记录该段内各页对应的物理块号。
    2. 地址映射过程当 CPU 给出逻辑地址时,地址结构通常分为三部分:段号 (S)页号 (P)页内偏移量 (W)
    1. 首先根据段号查找该进程的段表,找到该段对应的页表起始地址
    2. 然后根据页号查找该段所属的页表,找到对应的物理块号
    3. 最后将物理块号与页内偏移量拼接,得到最终的物理地址。
    4. 存储管理方式关键映射表访存次数(不含快表 TLB)分页管理每个进程一张页表2次(页表 + 实际数据)分段管理每个进程一张段表2次(段表 + 实际数据)段页式管理每个进程一物理段表 + 每个段一页表3次(段表 + 页表 + 实际数据)
  14. 在操作系统存储管理中,地址结构的“维度”通常是指程序员或用户在编写程序时感知的逻辑地址空间结构。
    1. 分页存储管理(Paging):一维(线性)
      • 逻辑地址虽然在内部被划分为页号和页内偏移量,但对用户来说,整个地址空间是连续的线性增长,只需提供一个地址即可。
    2. 分段存储管理(Segmentation):二维
      • 用户需要通过“段号”和“段内偏移量”两个维度来定位数据。每个段的长度可以不同,且各段之间在逻辑上是独立的。
    3. 段页式存储管理(Segmented-paging):二维
      • 原理: 它结合了段式和页式的优点。首先将用户程序分成若干个逻辑(如代码段、数据段),然后再将每个段分成固定大小的
      • 为什么是二维: 尽管在地址转换过程中涉及到“段号、页号、页内偏移”三个部分,但从程序员的角度来看,访问一个数据仍然只需要给出 [段号, 段内偏移]
      • 系统会自动将“段内偏移”拆分为“页号”和“页内地址”。因此,其逻辑地址结构在本质上与分段式一样,仍然是二维的。
    结构对比表存储管理方式逻辑地址维度用户感知的地址组成分页一维 (线性)[绝对地址]分段二维[段号, 段内偏移]段页式二维[段号, 段内偏移] (内部自动拆分)
  15. 分段管理(用分段方法管理用户地址空间):
    • 面向用户/程序员: 它将程序按照逻辑功能划分为段(如主程序段、子程序段、数据段、堆栈段等)。
    • 优点: 方便编程、方便共享和保护、支持动态链接。它反映了程序的逻辑结构,因此用于管理用户逻辑地址空间
  16. 分页管理(用分页方法管理物理存储空间):
    • 面向系统/硬件: 它将物理内存和逻辑内存划分为固定大小的页框(Frames)和页面(Pages)。
    • 优点: 离散分配内存,彻底解决了“外部碎片”问题,提高了主存的利用率。因此,它常用于管理物理存储空间

段页式管理的结合点

段页式管理正是结合了上述两者的长处:

  • 在逻辑上(对用户): 程序仍然按照“段”来划分,用户看到的还是由多个段组成的逻辑空间。
    • 在物理上(对内存): 每一个“段”不再是连续存放的,而是被进一步划分为若干个固定的“页”。系统以“页”为单位为这些段分配物理内存。

总结:

  • 分段负责解决程序的逻辑组织和共享保护问题(对应用户地址空间)。
    • 分页负责解决内存分配和碎片减少问题(对应物理存储空间)。

发表评论