Skip to content

内存管理基本概念

2026 大纲 三(一)1 内存管理的基本概念:逻辑地址空间与物理地址空间、地址变换、内存共享、内存保护、内存分配与回收。

内存只有一块,进程却有一堆

进程管理那一章解决的是"CPU 只有一个,进程却有一堆"——办法是在时间上轮流。 内存这一章面对的是同一个矛盾的另一半:内存只有一块,进程却有一堆

但内存不能靠"轮流"解决。CPU 可以这一毫秒给你、下一毫秒给他,因为切换时把寄存器 存起来就行;内存里装着进程全部的代码和数据,动辄几十上百兆,没法每次切换都倒腾一遍。 所以内存必须在空间上同时分给多个进程——它们真的同时待在里面。

一旦"同时待在里面",五个问题立刻全冒出来,而且它们全部源自这同一句话:

编译时谁也不知道自己会被放到哪 ⇒ 程序只能用从 0 开始的地址,得有人翻译(地址变换); 谁占了哪一块得有人记账(分配与回收);一个进程写错地址就可能踩坏别人, 得有人拦(内存保护);可库代码、通信缓冲区又应该被多个进程一起看到, 拦不能一刀切死(内存共享);所有进程加起来终究超过物理内存, 得在内外存之间倒腾(内存扩充)。

这五条就是本章的全部内容,而整章是被一条因果链推着走的

先把程序整块整块地连着放(连续分配)→ 装装卸卸内存被切碎,大进程无处安放(碎片)→ 那就别连着放,切成小块散着装(分页、分段)→ 可散着装之后地址怎么翻译成了新问题(地址变换、多级页表、TLB)→ 翻译解决了,但内存终究还是不够用(虚拟内存)→ 不够就只能把暂时不用的踢出去(页面置换算法)→ 踢得太勤,系统就光顾着搬页面不干活了(抖动、工作集、页框分配)。

每一步都是上一步留下的问题逼出来的。 这一节先把五项功能的地基打好—— 尤其是地址变换和内存保护那两套寄存器,后面分页、分段用的都是它们的直系后代。

一、五项功能从哪来

顺着"多道程序共存"这一句就能推出全部五项:编译时不可能知道自己被放到哪里 ⇒ 程序只能用从 0 开始的逻辑地址 ⇒ 必须有人翻译成物理地址(地址变换);谁占了哪块必须有人记账(分配与回收);一个进程写错地址就可能踩坏别人(内存保护);但库代码、通信缓冲区应该被多个进程同时看到,保护不能一刀切死(内存共享);所有进程加起来超过物理内存,得在内外存之间倒腾(内存扩充)。

功能做什么详见
地址变换(重定位)把逻辑地址翻译成物理地址本篇二
内存分配与回收记录空闲/已分配情况,按算法切分与合并本篇四、连续分配
内存保护限定进程只能访问自己的地址范围本篇三
内存共享让指定的内存区被多个进程同时访问本篇五
内存扩充覆盖、对换、虚拟内存本篇六、虚拟内存

二、地址重定位

方式转换时机谁来做装入后能否移动适用场合
绝对装入编译/汇编时编译程序(或程序员直接写绝对地址)不可只能单道程序
可重定位装入(静态重定位)装入时装入程序,一次性改写全部地址不可多道程序,但进程位置固定
动态运行时装入(动态重定位)每次访存时硬件(重定位寄存器 + 加法器)可以多道程序 + 对换 + 紧凑

三、内存保护

两套硬件方案的差别只在拿什么去比

方案寄存器比什么
上下限寄存器上限=进程占用区域的最大物理地址;下限=最小物理地址变换后的物理地址:下限 ≤ 物理地址 ≤ 上限
基址 + 限长寄存器基址(重定位寄存器)=进程起始物理地址;限长=逻辑地址空间长度变换前的逻辑地址:逻辑地址 < 限长,再加基址

方案二更常用,因为它把"越界检查"和"地址变换"合并进同一套硬件——限长比较与基址加法可以并行做。后面分页的"页号 < 页表长度"、分段的"段号 < 段表长度",都是它的直系后代。

四、内存分配与回收

组成内容典型形式
数据结构记录哪些区域空闲、哪些已分配、各自的始址与大小空闲分区表 / 空闲分区链 / 位示图
分配算法有多块空闲区可选时,选哪一块首次适应、最佳适应、最坏适应、邻近适应
回收处理进程释放后,把归还的区域并回空闲结构与前后相邻空闲区合并,避免越回收越碎

三样里最容易被漏掉的是回收:只登记不合并,内存会被切得越来越碎,最终大进程无处安放。完整算法与四种邻接情况见连续分配

离散分配下这项工作会变形但不消失:分页时分配的是页框,回收只需把位示图对应位清零,天然不需要合并。"要不要合并"取决于分配单位是否等大。

五、内存共享

保护要求"互相看不见"、共享要求"能一起看见",操作系统靠三件事同时满足:

  1. 粒度可控——共享以逻辑上完整的单位进行。分段系统中共享一个函数只需在两个进程的段表里各放一个指向同一段的表项;分页系统中同一个函数可能横跨多个页面、还可能与别的内容挤在同一页里,共享就麻烦得多。共享是否方便,取决于划分依据是逻辑的还是物理的。
  2. 权限分级——段表/页表项里的存取控制字段给每个进程单独规定读、写、执行权限,文件主可读写、其他进程只读甚至只执行。
  3. 可重入代码(纯代码)——被共享的代码在执行中绝对不允许被修改。真实代码难免有变量、指针、计数器,做法是把这些会变的部分拷贝一份到各进程私有的局部数据区,共享的那份只读不写。

六、内存扩充:覆盖与交换

覆盖(Overlay)交换 / 对换(Swapping)
作用范围同一个进程内部的若干程序段进程之间,以整个进程为单位换进换出
谁来划分程序员,编程时就要指明哪些段互斥、共用哪块覆盖区操作系统(对换进程)
对用户不透明,属于一种程序设计技术透明,用户程序无感
换到哪里内存中预留的覆盖区,段来自外存外存上专门的对换区(连续分配,读写速度优先)

覆盖把程序划成一个常驻的固定区和若干覆盖区,不会同时执行的模块共用同一块覆盖区。内存需求被压到

固定区大小+每个覆盖区(该区内最大那个模块的大小)

🔴 不是"固定区 + 最大的那一个覆盖区"——不同覆盖区里的模块之间并不互斥、可能同时驻留,所以每个覆盖区都要按它自己那组互斥模块中最大的那个留够空间。代价是程序员必须清楚模块间的调用关系,这也是它被虚拟内存取代的原因。

交换由操作系统的对换进程执行,属进程调度里的中级调度

  • 换出选谁——先在驻留内存的进程里找阻塞或睡眠状态的;有多个时选优先级最低的;有的系统还要看在内存中已驻留多久,避免低优先级进程刚调入又立刻被换出
  • 换入选谁——在"就绪但已换出"的进程里,选在磁盘上待得最久的换回来

两条规则背后是同一个判据:换出要挑"眼下最不可能用 CPU 的",换入要挑"已经等得最委屈的"。

三点边界:换出的进程并不释放 PCB,PCB 常驻内存(否则系统就找不到它了);对换区采用连续分配,分配与回收方法和内存的动态分区完全一样(同样有四种邻接情况的合并);交换以整个进程为单位,而虚拟内存以页/段为单位——后者才是"部分装入",前者仍要求进程全部在内存才能运行。

七、内碎片与外碎片

类型说明产生原因出现在
内碎片已分配给进程但未被使用的空间分配单位大于实际需要固定分区、分页、段页式
外碎片内存中空闲但太小无法利用的区域多次分配回收后被切碎动态分区、分段

判据是"这块空间是否已经分出去了":已分出去而没用满 ⇒ 内碎片;还没分出去但小到分不出去 ⇒ 外碎片。

八、逻辑地址是在哪一步形成的

一个地址从源代码走到内存单元要经过四个阶段,各阶段的产物不同,题目会挑其中一步来问:

阶段这一步产出什么
编辑源程序文本,此时地址还是符号名(变量名、函数名)
编译目标模块,模块内部是各自从 0 开始的相对地址——还不是完整的逻辑地址
链接装入模块,各模块被拼成一条统一的地址线,完整的逻辑地址在这一步形成
装入内存映像,逻辑地址被翻译(或运行时由硬件翻译)成物理地址

容易答成"编译",因为编译确实产生了地址。但编译时产生的是局部的相对地址—— 模块 B 里的 300 只在 B 内部有意义,把 B 拼到 A 后面之后它就变了。 只有链接把所有模块摆成一条地址线之后,"逻辑地址"这个说法才成立, 地址变换机构要变换的正是这个地址。详见程序的链接与装入

考点速记

  1. 五项功能同源于"多道程序共存"。逻辑地址空间大小由地址位数决定、物理地址空间大小由实际安装的内存容量决定,两者互不决定——正因不相等才必须有映射层,简单平移是不够的。
  2. 三种装入方式是同一件事被推迟得越来越晚(编译时 → 装入时 → 每次访存)。只有动态重定位允许进程搬家,判据是代码里留下的是逻辑地址还是物理地址;它进而解锁紧凑、对换、动态可重定位分区分配三件事,代价是必须有硬件支持
  3. 保护两套方案的差别只在拿什么去比:上下限寄存器比变换后的物理地址,基址 + 限长比变换前的逻辑地址。后者更常用,必须先限长后基址——先加基址的话,越界地址会变成一个看似合法的物理地址。分区分配方式的主要保护措施就是界地址保护
  4. 保护检查必须每次访存都做,抽查等于不查;也因此只能交给硬件——比较与加法可并行,不增加延迟。
  5. 描述内存布局的寄存器(重定位、限长、页表、段表)一律平时存在 PCB 里、上下文切换时装入
  6. 共享的是内容不是地址:同一共享段在不同进程中可以有不同的段号。共享是保护的特例——保护的本质是"按权限访问"而非"禁止访问",靠段表/页表项里的存取控制字段表达;被共享的代码必须是可重入(纯)代码
  7. 覆盖 vs 交换看谁做决策:覆盖是程序员编排、作用在一个进程内部、不透明、属程序设计技术;交换是操作系统决定、作用在进程之间、透明、属中级调度。覆盖的内存需求是 固定区+(各覆盖区中最大模块)不是"固定区 + 最大的那一个覆盖区"
  8. 碎片判据是"这块空间是否已经分出去了":已分出去而没用满是内碎片,还没分出去但小到分不出去是外碎片。⚠️段页式仍有内碎片(段内分页、末页填不满),它消除的只是外碎片。
  9. 完整的逻辑地址在链接阶段形成,不是编译阶段。

这一节在真题里被考过的形式

memory-management-basics 这个标签下挂了 7 道题,但其中 5 道实际考的是分页、 虚存或置换算法(2014-28 TLB、2018-45 与 2019-31 二级页表、2021-28 改进型 Clock、 2023-28 虚拟地址空间),已分别在基本分页CLOCK 算法虚拟内存基本概念里讲。 所以本页下方的练习区渲染的题比本节内容宽,属正常。 真正落在本节的只有两道:

  • 问分区分配内存管理方式的主要保护措施是什么(2009-26)。答界地址保护。四个选项里另三个(程序代码保护、数据保护、栈保护)都不是内存管理层面的机制——分区分配下 OS 能做的保护只有一件事:用一对寄存器把进程圈在自己那段地址范围里。⚠️ 这道题考的就是速记第三条,答案就是那两个寄存器的名字。
  • 问逻辑地址是在哪个阶段形成的(2011-30)。答链接。⚠️ 极易误选"编译"——编译产生的是各模块内部从 0 开始的相对地址,还不是完整逻辑地址;只有链接把各模块拼成一条地址线之后,地址变换机构要变换的那个"逻辑地址"才成立。

复习优先级本节是整章的地基,必考但不单独重考。速记第二、三条(动态重定位与 两套保护方案)会在分页、分段里以"页号 < 页表长度""段号 < 段表长度"的形式反复出现, 在这里理解透,后面就不用再记。覆盖与交换、内外碎片属于容易设错项的地方, 把第七、八条的判据背清楚即可。

易错:认为逻辑地址在编译阶段形成。编译产生的是模块内部的相对地址,链接才形成完整的逻辑地址。

易错:把越界检查放在加基址之后。必须先比限长再加基址,否则越界地址会变成一个看似合法的物理地址。

易错:认为覆盖所需内存是"固定区 + 最大的那一个覆盖区"。不同覆盖区可能同时驻留,要各覆盖区分别取自己那组的最大值再求和

易错:把覆盖和交换的决策者搞反。覆盖是程序员编排的程序设计技术,交换是 OS 做的中级调度

易错:认为段页式既无内碎片又无外碎片。段内还要分页、末页填不满,内碎片仍在

易错:认为共享段在各进程中的段号必须相同。共享的是内容不是地址,段号可以不同。

易错:认为静态重定位也能配合紧凑或对换。只有动态重定位才允许进程搬家。

教材出处
  • 汤小丹《计算机操作系统》4.2.1 程序的装入(绝对装入、可重定位装入与静态重定位、动态运行时装入),p123–p124
  • 同上 4.3.6 动态可重定位分区分配(紧凑必须配合动态重定位、重定位寄存器),p133–p134
  • 同上 4.4 对换(对换的引入、整体对换与页面/分段对换、对换区的分配与回收、进程的换出与换入),p135–p138
  • 孙钟秀、费翔林《操作系统教程》(第 6 版)3.1 存储管理基础(主存空间的分配与去配、共享、存储保护、主存空间的扩充:对换技术与虚拟技术;段覆盖技术是一种程序设计技术),p83、p85–p86

相关知识

连续分配程序的链接与装入(三种链接时机;动态链接库在内存中只存一份,是内存共享的典型用例)|运行时内存映像与地址空间虚拟内存

真题练习