基本分页存储管理
连续分配的死结是外部碎片:内存里明明还有很多空间,却因为不连续而放不下作业。紧凑能缓解,但移动作业的开销很大。
分页的思路很直接:既然”必须连续”是问题的根源,那就不要求连续。 把内存切成固定大小的小块,进程需要多少块给多少块,放在哪儿无所谓。
机制
三个基本概念
页框(页帧、物理块):把物理内存等分成的固定大小的块,从 0 开始编号。
页(页面):把进程的逻辑地址空间按同样的大小分成的块,也从 0 开始编号。
页表:记录每个页放在哪个页框里的对照表。每个进程一张。
页和页框大小必须相同——正因如此,任意一页都能装进任意一个页框,摆放才彻底自由。
页表项通常只需要记录页框号:页号是隐含的(第
地址结构
逻辑地址被拆成两段:
页内偏移的位数由页面大小决定:页面大小为
这个拆分不需要做除法——页面大小是 2 的幂,所以只要按位截断即可,硬件做起来极快。这正是页面大小必须取 2 的幂的原因。
地址变换过程
flowchart LR LA["逻辑地址<br/><b>页号 P</b> | 页内偏移 W"]:::la PTR["页表寄存器<br/>(存页表始址+长度)"]:::reg CHK{"P ≥ 页表长度?"}:::chk ERR["越界中断"]:::err PT["查页表<br/>得页框号 F"]:::pt PA["物理地址<br/><b>页框号 F</b> | 页内偏移 W"]:::pa LA --> CHK PTR --> CHK CHK -->|"是"| ERR CHK -->|"否"| PT PT --> PA LA -.->|"W 原样拼接"| PA classDef la fill:#dbeafe,stroke:#2563eb,color:#1e3a5f classDef pa fill:#dcfce7,stroke:#16a34a,color:#14532d classDef reg fill:#fef3c7,stroke:#d97706,color:#78350f classDef pt fill:#fef3c7,stroke:#d97706,color:#78350f classDef chk fill:#f1f5f9,stroke:#94a3b8,color:#334155 classDef err fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
有两个细节必须记牢:
① 页内偏移 W 原样照抄,不参与任何计算。 因为页和页框大小相同,某个字节在页内是第几个,在页框内也是第几个。只有页号需要被翻译。
② 这个过程要访问两次内存:一次读页表(页表在内存里),一次读真正的数据。这就是分页的性能代价,也是引入快表的动机。
疑问点:页表始址存放在何处,查页表由谁完成
在页式存储管理中,页表的始地址存放在( )中。 A. 物理内存 B. 页表 C. 快表(TLB) D. 页表寄存器
在页式存储管理中,当 CPU 形成一个有效地址时,查找页表的工作是由( )实现的。 A. 操作系统 B. 页表查询程序 C. 硬件 D. 存储管理进程
第 31 题答案 D:页表寄存器。
要区分两件事:页表本身存放在内存中(这是 A 的迷惑之处),但页表的起始地址存放在 CPU 的页表寄存器里。
道理很简单:如果连”页表在哪儿”都要去内存里查,那就成了先有鸡还是先有蛋。必须有一个 CPU 能直接读到的地方来存这个入口,那就是寄存器。
页表寄存器通常同时存放页表始址和页表长度,后者用于越界检查(见上图)。
进程切换时,页表寄存器的内容要作为进程上下文的一部分被保存和恢复——这正是进程切换比模式切换昂贵的原因之一。
第 32 题答案 C:硬件。
关键在于频率:每一次访存都要做一次地址变换。如果由操作系统的软件来做,意味着每取一个数就要陷入内核跑一段程序,开销将高到无法接受。
因此地址变换必须由硬件(地址变换机构 / MMU)完成,全程不需要操作系统介入。操作系统只负责在进程切换时设置好页表寄存器,剩下的交给硬件。
排除 B、D 也是同样的道理:任何”程序”或”进程”级别的实现都太慢了。
快表(TLB)
为了消除”访存两次”的代价,引入快表——一个高速缓存,存放最近用过的页表项。
TLB 由相联存储器实现,可以并行比较所有表项,一次查找就知道命中与否。
有 TLB 之后的流程:
- TLB 命中:直接得到页框号 → 访存 1 次(只取数据)
- TLB 未命中:查内存中的页表得页框号 → 再取数据 → 访存 2 次,同时把该表项装入 TLB
TLB 之所以有效,靠的是程序访问的局部性原理——程序倾向于在一段时间内反复访问相邻的地址,因此命中率通常能达到 90% 以上。
注意 TLB 中存的是”页号→页框号”的完整映射,这一点在多级页表下极其重要(见下方边界段)。
多级页表
单级页表有个致命问题:页表本身太大,而且要求连续存放。
以 32 位系统、4KB 页面为例:页号占 20 位,共
解决办法是把页表本身也分页,再用一张”页表的页表”来索引它——这就是二级页表。逻辑地址随之拆成三段:
- 页表不必连续存放,各级页表自己也按页存储,散落在内存各处
- 可以只装入需要的部分,进程没用到的地址区域,对应的下级页表根本不必建立
代价是访存次数增加:
计算题模板一:需要几级页表
疑问点:多级页表级数的计算方法
- 在采用页式存储管理的系统中,逻辑地址空间大小为 256TB,页表项大小为 8B,页面大小为 4KB, 则该系统中的页表应该采用( )级页表。 A. 2 B. 3 C. 4 D. 5
这类题应当如何着手?
答案 C:4 级。
这类题的唯一原理是:每一级页表必须恰好装进一个页面(这是多级页表的设计目标——让页表本身也能按页管理)。因此每一级能覆盖的位数是固定的,用总页号位数除一下即可。
四步法:
第一步:算页内偏移位数。 页面大小
第二步:算页号总位数。 逻辑地址空间
计算题模板二:平均访存次数
疑问点:带 TLB 的多级页表平均访存次数计算
- 在配置了 TLB 的页式虚拟存储管理的系统中,假设 TLB 的命中率约为 75%, 忽略访问 TLB 的时间,并且使用二级页表,则每次存取的平均访问次数是( )。 A. 1.25 B. 1.5 C. 1.75 D. 2
答案 B:1.5。
三步法:
第一步:分别算出”命中”和”未命中”两条路径各要访存几次。
- TLB 命中:直接拿到页框号,不需要查任何一级页表 → 只需 1 次访存去取数据
- TLB 未命中:要查完 2 级页表(2 次访存),再取数据(1 次)→ 共 3 次
第二步:按概率加权。
通用公式(
边界
TLB 缓存的是最终映射,不是某一级
疑问点:多级页表下 TLB 命中率是否需要按级数相乘
在二级页表下,TLB 是否可能”只命中某一级”?两级都命中的概率是否应为
?
不需要相乘,也不存在”只命中某一级”。这是理解 TLB 最关键的一点。
原因在于 TLB 中存放的是”页号 → 页框号”的完整映射,而不是各级页表的中间项。
换句话说,TLB 的查找以完整的页号为键,一次查询要么直接给出最终的页框号,要么什么都没有。它把整个多级查表过程当作一个黑盒缓存了起来。
所以:
- 命中:整条路径被跳过,无论有几级页表都不必查 → 1 次访存
- 未命中:整条路径都得走一遍 →
次查表 + 1 次取数
“两级都命中”这个概念本身不成立——不存在”第一级命中而第二级未命中”的中间状态。75% 就是拿到完整翻译结果的概率,不需要再做任何复合运算。
这也解释了为什么多级页表下 TLB 的价值更大:级数越多,未命中的代价越高(
分页有内部碎片,没有外部碎片
外部碎片消失了,因为任何一个空闲页框都能被使用——不存在”太小以致用不上”的空闲块。这正是分页取代连续分配的根本理由。
但引入了内部碎片:进程的最后一页通常装不满。平均而言每个进程浪费半个页面。
由此可推出页面大小的权衡:
- 页面太大 → 内部碎片大
- 页面太小 → 页表项数量剧增,页表本身太大
页表项里存的是页框号,不是物理地址
页表项存的是页框号(块号),不是完整的物理地址。物理地址要由页框号拼接页内偏移得到。
计算页表项大小时常犯的错是按物理地址位数去算。正确做法是按页框号位数(= 物理内存大小的位数 − 页内偏移位数)向上取整到字节。
分页提供的物理地址空间
疑问点:采用分页或分段后用户可用的物理地址空间
- 采用分页或分段管理后,提供给用户的物理地址空间( )。 A. 分页支持更大的物理地址空间 B. 分段支持更大的物理地址空间 C. 不能确定 D. 一样大
答案 C:不能确定。
命题人的意图是:页表和段表本身也要占用内存。因此
这道题的措辞确实偏松——严格说”提供给用户的物理地址空间”应指物理内存的寻址范围,那是由地址总线宽度决定的,与采用分页还是分段无关。命题人想问的其实是”可供用户程序使用的物理内存量”。
遇到这类题的稳妥做法:先看选项。当出现”不能确定”且题干确实缺少关键条件时,命题人多半就是想考”管理机构本身也要占空间”这个点。答题按命题意图走,但心里要清楚它问得不严谨。
对照速查
| 概念 | 属于 | 说明 |
|---|---|---|
| 页框(物理块) | 物理内存 | 内存被等分成的固定大小块 |
| 页(页面) | 逻辑地址空间 | 进程被等分成的块,与页框等大 |
| 页表 | 每进程一张,存于内存 | 记录页 → 页框的映射 |
| 页表寄存器 | CPU 中 | 存页表始址和长度 |
| 快表 TLB | 相联存储器 | 缓存完整的页号→页框号映射 |
| 情形 | 访存次数 |
|---|---|
| 无 TLB,单级页表 | 2 |
| 无 TLB, | |
| TLB 命中 | 1 |
| TLB 未命中, | |
| 平均(命中率 |
| 计算模板 | 步骤 |
|---|---|
| 几级页表 | ① 页内偏移位数 ② 页号总位数 ③ 一页装几个表项 = 每级消化位数 ④ 向上取整相除 |
| 平均访存 | ① 命中路径 1 次、未命中 |
考点
- 页内偏移原样照抄,只有页号需要翻译
- 页表始址存于页表寄存器(页表本身在内存)
- 地址变换由硬件完成,操作系统不介入
- TLB 缓存完整映射,不存在”只命中某一级”,命中率不需相乘
- 多级页表的设计目标:每级页表恰好装进一个页面
- 分页有内部碎片、无外部碎片;页面大小是碎片与页表大小的权衡
- 页表项存页框号,不是物理地址
链接
- 🏠 返回总览:操作系统第 3 章:内存管理总览
- ⬅️ 上一节:3.1.2 连续分配管理方式
- ➡️ 下一节:3.1.4 基本分段存储管理
- 📖 名词库:第 3 章名词库