城西物流园里,仓库管理员小张最近愁得睡不着。仓库明明还有不少空位,可新来的大件货物怎么也塞不进去——之前堆放的货东一块西一块,留下的缝隙都是“鸡肋”空间。操作系统的内存管理,碰到的正是跟小张一模一样的难题:程序启动时申请内存,运行中释放内存,来来回回之后,物理内存里布满了用不上的小空洞。 小张想了两套对策。第一套是“标准干货箱”:不管什么货,全部分切成统一尺寸的箱子,一片一片塞进仓库的固定格里,缝隙再也不是问题——这就是分页。第二套是“分类储物区”:把货物按照“类型”整块划分专区,代码归代码、数据归数据,看着顺眼、取用方便——这就是分段。两套方案各有代价:标准箱总让最后一箱装不满,分类区之间又会留下挤不进货物的缝。聪明的操作系统干脆把两招合体:专区内再放标准箱,把两类碎片的麻烦都压到最小。 这篇文章,就带你看懂这两个“整理方案”是怎么运作的,以及现代的电脑仓库,到底选了哪张图纸。

岗位关联

掌握分页与分段是理解操作系统内存管理的基石;对 AI 从业者而言,大模型推理的显存优化(如 vLLM)、训练框架的显存复用,本质上都是这两个概念的工程变体。

目录
  1. 可视化演示
  2. 内存分配困境
  3. 分页机制
  4. 分段机制
  5. 碎片对比
  6. 地址转换
  7. 共享与保护
  8. 段页式结合
  9. 权衡与选型
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

分步展示分页、分段及段页式内存管理的地址转换机制,对比碎片类型与共享粒度差异,交互切换理解三种方案的本质。

本节配有交互动效(进入视口后自动加载,约 3KB)。

分页机制:固定大小页框映射 虚拟地址分为页号和页内偏移,页表将虚拟页号映射到物理页框号 物理地址 = 页框号 × 页大小 + 偏移 分页消除外部碎片,但存在页内内部碎片。

内存分配困境

小张的仓库里,货物都是“整块整车”地卸下来堆放在一起。今天的货从墙角开始摆,明天的货紧挨着摆。程序加载到内存也是这个道理:早期的操作系统要求一个进程必须占据一块连续的存储区,从某个地址一直到另一个地址,中间不允许“跳空”。这种方案叫连续分配,思路直白,但会把可用空间越切越碎。

老客户的货越堆越多,中途又有几批货临时运走,仓库里出现了一个个又窄又短的缝隙。新来的大货柜需要一整片空地,可怎么比划都塞不下——明明总空间是够的,可就是凑不出一块大的。这种现象,正是操作系统的“内存碎片化”:作业之间留下的零碎洞叫外部碎片,分配出去却发现用不完的部分叫内部碎片。连续分配下,外部碎片是主要矛盾。由此计算机科学家想出一个破局思路:既然程序不必连续,为什么不把内存切成更小的单位,让程序像拼图一样“离散”地放进去?同时,虚拟内存为每个进程画了一张独立的“仓库图纸”(虚拟地址空间),物理内存只是一排排真实的货架。思路一变,后面的分页、分段才有了舞台。

小张明白了,问题不是仓库太小,而是摆放方式不对。要么把货切碎(分页),要么按类划区(分段),出路就在这两套方案里。

U=EM\displaystyle U = \frac{E}{M}
U 表示内存利用率,E 为进程实际使用的空间,M 为系统总内存。U 越接近 1,说明空间浪费越少。
  1. 分子 E程序中真正运行需要的有用字节数,相当于货物本身占的体积。
    E=进程实际占用空间\displaystyle E = \text{进程实际占用空间}
  2. 分母 M物理内存总容量,相当于仓库里全部货架的总容积。
    M=物理内存总大小\displaystyle M = \text{物理内存总大小}
  3. 比值 U内存被有效利用的程度,余下的部分就是碎片与空闲。
    U=EM,0<U≤1\displaystyle U = \frac{E}{M}, \quad 0 < U \le 1

分页机制

小张的第一套方案:买回一批统一尺寸的标准干货箱,不管什么货,通通拆开按箱装。仓库的货架也预先划成一个个同样大小的格子。任何一箱货放到任何格子都行,货箱之间没有宽度差异,自然也就没有塞不进的缝。

对应到操作系统:把虚拟内存空间切成固定大小的块,每一块叫一页;把物理内存切成同样大小的块,每一块叫页框。页大小常见 4KB。进程的页可以放进任意空闲页框,不需要连续。由此虚拟地址与物理地址之间多了一张映射表——页表,它记录了每一页放到哪个页框里。每个进程一张页表,页表基址寄存器用来定位这张表;页表项里除了物理页框号,还有存在位、读写权限位等控制信息。

技术细节上,虚拟地址被拆成页号和页内偏移两部分。地址转换时,CPU 先取页号查页表,得到物理页框号,再与偏移拼接出物理地址。

标准箱方案让小张惊喜:无论货怎么来,只要格子还有空的,就一定能放下。但小张也注意到,最后一箱常常只装了半箱货——那空着的半箱,就是“内部碎片”。

物理地址=F×S+d\displaystyle \text{物理地址} = F \times S + d
F 为页框号,S 为页大小,d 为页内偏移。地址转换本质就是“换一个页框号、保留同一偏移”。
  1. 提取页号 P虚拟地址按页大小整除即可得到所在页号,相当于看箱子上印的“第几箱”。
    P=⌊AS⌋\displaystyle P = \left\lfloor \frac{A}{S} \right\rfloor
  2. 提取偏移 d虚拟地址取余得到页内偏移,相当于这箱货在箱内的第几格。
    d=A mod S\displaystyle d = A \bmod S
  3. 查页表得页框号 F用 P 查页表,得到货箱实际所在货架格子的编号 F。
    F=页表[P]\displaystyle F = \text{页表}[P]
  4. 合成物理地址用 F 乘页大小再加上偏移 d,得到真实的内存地址。
    物理地址=F×S+d\displaystyle \text{物理地址} = F \times S + d

分段机制

小张的第二套方案:不开箱,按“类别”划区。把仓库分成几个大区——办公货区、建材货区、生鲜冷库,每个区一整块连着的空间。货物完整地码进去,想取哪箱不用翻半天,想给某个区加装门禁也容易。

操作系统中的对应物:一个程序并不是铁板一块,它天然包含代码段、数据段、栈段、堆段。分段机制尊重这种结构,把虚拟内存按逻辑单位切成若干段,每段大小不等,段号从 0 开始编号。物理内存中,为每段分配一块连续区域,各段之间不必相邻。段表记录每段的基址和段长;栈段这类会随运行伸缩的段,还可以在段长字段上做动态增长的支持,只要物理内存中还有空闲空间,段长就能扩展。

技术细节上,虚拟地址被拆成段号 s 与段内偏移 d。查段表得到该段在物理内存中的基址 B,于是物理地址 = 基址 + 偏移。段表还记录了段长 L,访问前要做越界检查:如果 d 超出了 L,就拒绝这次访问并抛出异常。

分类区的优势是整齐、好管理、好保护,但小张皱着眉头发现:区与区之间总有一些不规则的过道缝隙——这些就是外部碎片,时间一长,还是会浪费不少空间。

物理地址=B+d\displaystyle \text{物理地址} = B + d
B 是段的物理起始地址(基址),d 是段内偏移。偏移不能越过段长 L,否则视为非法访问。
  1. 取段号 s虚拟地址的高位是段号,用来查段表,相当于报出“我要去建材区”。
    s=虚拟地址高若干位\displaystyle s = \text{虚拟地址高若干位}
  2. 查段表得基址 B 与段长 L段表给出该段在物理内存中的起点和长度。
    (B,L)=段表[s]\displaystyle (B, L) = \text{段表}[s]
  3. 越界检查如果偏移 d 大于等于段长 L,说明越界,触发异常。
    d<L\displaystyle d < L
  4. 合成物理地址基址加偏移得到最终地址。
    物理地址=B+d\displaystyle \text{物理地址} = B + d

碎片对比

标准箱方案里,箱子里总有装不满的空位;分类区方案里,区与区之间总有挤不进东西的过道。一个在“盒子内部”,一个在“区域外部”,操作系统把这分别命名为内部碎片与外部碎片。分页消除了外部碎片,但页是固定大小的,进程最后一页用不完,这部分就是内部碎片,平均约等于半页。分段消除了内部碎片(每段按需分配),但段与段之间的空隙可能积累成无法使用的外部碎片。

对付外部碎片的经典招数是“紧凑”:把正在运行的进程整体搬到相邻位置,把散落的小空闲区合并成大空间。但紧凑代价不低——它需要动态重定位,搬运期间进程不能执行。另一个思路是“最佳适配/最先适配”这类放置策略,只能缓解,无法根除。于是有一个直观的量化指标衡量浪费:碎片率。

小张把两套方案的账本摊开对比:分页牺牲的是“箱内的零头”(内部碎片),分段牺牲的是“区间的过道”(外部碎片)。没有绝对免费的选择,只有适合场景的取舍。

碎片率=碎片空间总空间×100%\displaystyle \text{碎片率} = \frac{\text{碎片空间}}{\text{总空间}} \times 100\%
碎片率越低,内存管理水平越高。分页侧重控制外部碎片,分段侧重控制内部碎片。
  1. 碎片空间内部碎片或外部碎片的字节数,相当于空箱容积加过道面积。
    碎片空间=总空间−有效使用空间\displaystyle \text{碎片空间} = \text{总空间} - \text{有效使用空间}
  2. 总空间 M当前物理内存或某分区的容量。
    M\displaystyle M
  3. 比例碎片占总体空间的比重,越低越好。
    0≤碎片率<100%\displaystyle 0 \le \text{碎片率} < 100\%

地址转换

分页和分段都离不开账本。分页的账本按页记录“第几箱→第几格”(页表);分段的账本按区记录“几号区→仓库哪个位置+区多大”(段表)。CPU 的 MMU(内存管理单元)就像一个拿着账本的调度员,每次访问内存都要先翻账本。页表基址寄存器指向当前进程的页表,段表基址寄存器指向当前进程的段表。

查一次页表就是一次内存访问,再访问目标数据又是一次内存访问,所以无快表时一次取数需 2 次访存,段页式则需 3 次。为了提速,CPU 加入 TLB(快表,即转译后备缓冲器),缓存最近用过的页表项——命中时只需 1 次访存。此外,单级页表可能非常庞大,于是引入多级页表:只在需要时才分配下一级页表,用空间换时间中的“空间”被大幅压缩。

账本翻得勤不勤、记得牢不牢,决定了仓库出货速度。TLB 就是小张脑子里那本“热货记忆册”,总在用的箱子位置不用每次翻账本。

EAT=h×t+(1−h)×2t\displaystyle EAT = h \times t + (1-h) \times 2t
EAT 为有效访问时间;h 为 TLB 命中率;t 为一次内存访问耗时。命中率越高,平均访存时间越接近 t。
  1. h 为命中率TLB 中能找到目标映射的概率,相当于调度员凭记忆直接报出位置。
    h=命中次数访问总次数\displaystyle h = \frac{\text{命中次数}}{\text{访问总次数}}
  2. t 为一次访存时间访问内存中一个字的耗时。
    t\displaystyle t
  3. 命中时TLB 直接给出页框号,只访问 1 次内存。
    h×t\displaystyle h \times t
  4. 未命中时需要查内存中的页表再取数据,共 2 次访存。
    (1−h)×2t\displaystyle (1-h) \times 2t
  5. 加权期望两部分相加得到平均访存时间,h 接近 1 时 EAT 接近 t。
    EAT=h×t+(1−h)×2t\displaystyle EAT = h \times t + (1-h) \times 2t

共享与保护

仓库里有一台咖啡机是几个团队共用的茶歇区。分类区方案里,这个茶歇区是一个整“区”,想共享就指给多个团队,想上锁就在区门口设个门禁卡。而标准箱方案里,每个箱子只是货物的一小片,要把连续几个箱子共享出来,得一格一格地分,麻烦得多。

共享与保护是内存管理的两大重点。分段以逻辑单位为共享粒度,很适合共享代码段、动态链接库;段表项中也能设置读、写、执行权限位,越界和越权都会触发异常。分页虽然也能共享,但按页共享粒度太细,容易破坏整体性;页表项同样有保护位,只是粒度以页为单位。另外,被多个进程共享的代码段要求“可重入”:代码本身只读,各进程把私有数据放在自己的数据段中,才能安全地同时运行。

茶歇区加门禁卡,这套思路让共享代码库既能被所有进程“共用一套”,又不会被人乱改。分段在这件事上天生比页级更顺手。

d<L且权限位允许\displaystyle d < L \quad \text{且} \quad \text{权限位允许}
一次合法访问必须同时满足“偏移在段内”和“权限位放行”两个条件。
  1. 越界检查偏移 d 须小于段长 L,否则该访问非法。
    d<L\displaystyle d < L
  2. 权限检查段表项中的权限位决定本段可读、可写或可执行。
    权限位∈{读,写,执行}\displaystyle \text{权限位} \in \{\text{读}, \text{写}, \text{执行}\}
  3. 两者同时满足访问才被允许,否则触发保护异常。
    允许访问  ⟺  (d<L)∧权限位通过\displaystyle \text{允许访问} \iff (d < L) \land \text{权限位通过}

段页式结合

小张终于想通了:先按类别划出几个大区(分段),每个大区里再全部用标准箱(分页)。这样既保留了区好管理、好设门禁的优点,又消灭了区与区之间塞不下的过道。分类和标准化的好处全占上。

段页式存储管理以段为外层、页为内层:进程先按逻辑分段,段内再按固定大小分页。地址转换分三步:段号查段表,得到该段的页表起始地址;段内页号查页表,得到页框号;页框号加偏移得到物理地址。无 TLB 时一次访问需 3 次访存,这是它付出的时间代价;空间上也同时维护段表和页表两张表,表项开销高于纯分页。

小张的“专区内标准箱”方案让仓库重获秩序。代价是搬运时要翻两本账(大区账+箱格账),但总比货堆满地来得强。

T访存=t段表+t页表+t数据=3t\displaystyle T_{\text{访存}} = t_{\text{段表}} + t_{\text{页表}} + t_{\text{数据}} = 3t
无快表时,段页式一次取数要查两次表、取一次数据,共 3 次访存;TLB 可将时间压到接近 1 次。
  1. 查段表用段号 s 找到该段对应的页表起始地址。
    t段表=t\displaystyle t_{\text{段表}} = t
  2. 查页表用段内页号 p 找到物理页框号 F。
    t页表=t\displaystyle t_{\text{页表}} = t
  3. 访问数据合成物理地址后取目标数据。
    t数据=t\displaystyle t_{\text{数据}} = t
  4. 合计三次访存相加;开启 TLB 后可接近一次。
    T访存=3t→TLB命中T≈t\displaystyle T_{\text{访存}} = 3t \quad \xrightarrow{\text{TLB命中}} \quad T \approx t

权衡与选型

图纸要落地,就得看现实条件:仓库的硬件、物流的习惯、管理员的能力。操作系统的选择也一样,性能和成本要一起算。实际系统里,x86 CPU 从硬件上同时提供分段与分页,主流操作系统的做法是“绕过分段、专注分页”:Linux 把段基址设为 0、段长设为最大,让分段失去隔离作用,只依赖分页。这样共享保护全部交给页表权限位完成,模型简单统一。

页大小的选择也讲究:4KB 小页灵活但页表大,2MB/1GB 大页能减少页表项、提高 TLB 命中率,但内部碎片更多。现代数据库和大模型推理框架主动申请“大页”,就是希望在页表开销与碎片浪费之间找自己的平衡点。理解这些取舍,是阅读系统源码和做性能优化时的重要背景。

小张最后在图纸上写下结论:分段适合做“逻辑世界的框架”,分页适合做“物理世界的摆放”,而现代系统大多让分页承担主角,把分段优化到近乎透明。理解这两套方案的权衡,就能看懂系统设计和 AI 框架的许多底层决策。

---

页表占用=VS×e\displaystyle \text{页表占用} = \frac{V}{S} \times e
V 为进程虚拟地址空间大小,S 为页大小,e 为每个页表项的字节数。页越大,页表越小,但内部碎片越多。
  1. 页表项数虚拟空间按页切分的数量,等于 V 除以 S。
    VS\displaystyle \frac{V}{S}
  2. 每项大小 e一个页表项通常为 4 字节或 8 字节。
    e=4B∼8B\displaystyle e = 4\text{B} \sim 8\text{B}
  3. 总占用页表项数乘每项大小,就是页表占用的内存。
    VS×e\displaystyle \frac{V}{S} \times e

AI 应用场景

vLLM 的 PagedAttention:给 KV Cache 装标准箱

阶段:大模型推理引擎的显存管理

解决的问题:传统推理框架为 KV Cache 申请连续显存,请求长短不一、动态生成,显存很快碎片化且无法按需增长,导致显存利用率低、吞吐量上不去。vLLM 借鉴分页思想,把 KV Cache 切成固定大小的逻辑块,映射到物理块上,按需分配,几乎消除了碎片化浪费。

具体案例:部署 Llama-70B 时,vLLM 的 PagedAttention 把显存利用率从传统方案的 40%-60% 提升到 90% 以上,吞吐量提高数倍。每个请求的 KV Cache 像一箱箱标准货,散落在显存各页框中,用完即还。

PyTorch Caching Allocator:显存里的分类储物区

阶段:深度学习训练的张量内存分配与复用

解决的问题:训练中反复创建、销毁张量,如果每次都向 CUDA 申请和释放显存,会产生大量碎片和性能抖动。PyTorch 的缓存分配器预申请大块显存,再按大小分桶复用,类似分段“按逻辑分类”的思路,显著降低显存碎片和 OOM 概率。

具体案例:训练 GPT-3 规模模型时,Caching Allocator 让显存峰值降低约 30%,训练速度提升约 20%。框架在后台扮演小张:货(张量)按大小分类入库,释放后留在桶里供下次复用。

CUDA Unified Memory:CPU 与 GPU 的共享账本

阶段:异构计算的统一内存管理

解决的问题:传统 CPU 与 GPU 显存分离,程序员要手动拷贝数据,容易出错且难以优化。CUDA 的 UVM(统一虚拟内存)让 CPU 与 GPU 共享一个虚拟地址空间,以页为粒度自动迁移数据,类似分页的换入换出。

具体案例:在科学计算或大模型微调中,程序员像访问普通内存一样读写 GPU 数据,硬件驱动按页搬运;只有真正用到的页才被迁移,省去大量手动拷贝。仓库比喻:小张让主仓库和移动仓库共用一套账本,哪边要货就从哪边调箱。

Python 演示

python60 行
"""分页与分段: 碎片化内存下谁更能装得下进程""" import math PAGE_SIZE = 4096  # 页大小 4KB, 一页对应一个页框  def paging_load(process_size, free_frame_count):    """    分页装载: 进程被切成等长页, 可散落在任意空闲页框。    返回 (是否成功, 内部碎片字节数)。    """    need_frames = math.ceil(process_size / PAGE_SIZE)    if need_frames > free_frame_count:        return False, 0    internal_frag = need_frames * PAGE_SIZE - process_size    return True, internal_frag  def segment_load(seg_size, free_blocks):    """    分段装载: 进程按逻辑段申请一整块连续空间。    free_blocks 是可用空闲块列表 [(起始地址, 大小)]。    返回 (是否成功, 该块剩余碎片)。    """    for start, size in free_blocks:        if size >= seg_size:            return True, size - seg_size    return False, 0  def main():    process_size = 16 * 1024 + 500  # 16.5KB 的"AI 推理小进程"    free_frames = 6                 # 内存里还剩 6 个空闲页框(24KB)     ok, internal = paging_load(process_size, free_frames)    need = math.ceil(process_size / PAGE_SIZE)    print(f"[分页] 进程 {process_size}B 需要 {need} 个页框, "          f"空闲 {free_frames} 个, 装载{'成功' if ok else '失败'}")    print(f"[分页] 内部碎片 = {internal}B")     # 系统运行一段时间后, 256KB 内存被拆成若干大小不一的空闲区    free_blocks = [        (0x0000, 8 * 1024),        (0x3000, 4 * 1024),        (0x8000, 6 * 1024),        (0xB000, 2 * 1024),        (0xE000, 3 * 1024),    ]    total_free = sum(sz for _, sz in free_blocks)    max_free = max(sz for _, sz in free_blocks)    print(f"[分段] 空闲总量 {total_free}B, 最大连续 {max_free}B")     ok, external = segment_load(process_size, free_blocks)    print(f"[分段] 进程需要 {process_size}B 连续空间, 装载"          f"{'成功' if ok else '失败 - 外部碎片太多, 拼不出一整块!'}")  if __name__ == "__main__":    main()
目的:在同样碎片化的内存里分别用分页与分段装载 16.5KB 进程,直观对比“内部碎片”与“外部碎片”对装载能力的影响。
  1. need_frames = math.ceil(process_size / PAGE_SIZE)向上取整算出需要几个页框,像数“标准箱”要几箱
  2. if need_frames > free_frame_count页框不够就失败,但页框可以零散分布
  3. internal_frag = need_frames * PAGE_SIZE - process_size最后一页用不满的字节数就是内部碎片
  4. for start, size in free_blocks:遍历空闲块,寻找能塞下整个段的空间
  5. if size >= seg_size只看单块连续空间够不够大,这是分段的关键约束
  6. total_free = sum(sz for _, sz in free_blocks)空闲总量看似够,却救不了分段

常见误区

误区分页把内存切成固定大小的页框,每页都能填进去,所以分页没有任何内存浪费。

正解分页消除的是“外部碎片”,但“内部碎片”依然存在——进程最后一页往往装不满,这部分按页框整体分配却用不掉的字节就是内部碎片。仓库比喻:标准箱解决了货架间塞不进裂缝的问题,但每箱总有一截空位。页越小内部碎片越少,但页表会变大,二者需要权衡。

为何常见:初学者常把“消除外部碎片”理解成“消除全部碎片”,忽略了固定大小划分天然带来的尾部浪费。

误区现代系统都用分页,分段是过时的古董,已经不存在了。

正解x86 从硬件上仍同时支持分段与分页,Linux 这类主流系统只是把段基址设为 0、段长设为整个地址空间,让分段“透明化”,从而只依赖分页。在实模式或某些嵌入式场景,分段依然重要。仓库比喻:分类区并没有拆掉,只是被标记成“所有区都从仓库入口开始”,等于没分区。

为何常见:普通开发者只在书上看过分段,日常写代码接触不到,就误以为它消失了。

误区只要进程建立,页表就记录了该进程所有虚拟页的映射关系,因此页表总是占很大空间。

正解页表项有“存在位”,未分配的虚拟页在页表中是无效项,不必占用实际资源;多级页表更是按需建立,只有实际用到的页目录和页表才被分配。仓库比喻:账本不会把所有可能的箱子都提前画好,而是货真的来了才记账。

为何常见:把“地址空间大小”与“实际分配空间”混为一谈,忽略了按需分配机制。

误区把页设得大一点,页表就小、TLB 命中率也高,所以页越大越划算。

正解大页确实减少了页表项和 TLB 缺失,但代价是最后一页的内部碎片更多。比如 2MB 大页装一个"2MB+1B"的进程,会浪费约 2MB 空间。所以系统提供多种页大小(4KB、2MB、1GB),按场景选择。仓库比喻:大箱子装大货省纸板,但装小货时空得更多。

为何常见:只看到页表省空间的收益,忽略了内部碎片增长的成本。

面试题

Q1. 分页存储管理主要消除了连续分配中的哪一类内存问题?

单选题 · easy

  • A.内部碎片
  • B.外部碎片← 正确答案
  • C.内存泄漏
  • D.磁盘坏道

答案:B。分页把内存切成等大的页框,任何页都能放进任何空闲页框,不需要连续空间,因此连续分配中“碎片化空洞”(外部碎片)被消除。仓库比喻:标准箱能塞进任意空格,过道问题消失。但注意它并不消除内部碎片,因此 A 不正确;内存泄漏与磁盘坏道与分页无关。

Q2. 分段机制划分内存空间的依据是什么?

单选题 · easy

  • A.固定大小
  • B.程序的逻辑单位← 正确答案
  • C.用户随机指定
  • D.物理页框大小

答案:B。分段尊重程序结构,按代码段、数据段、栈段、堆段等逻辑单位划分,每段大小不等。仓库比喻:按货物类别分区。A 是分页思路,D 完全无关。

Q3. 页表的核心作用是?

单选题 · mid

  • A.记录页的大小
  • B.将虚拟页号映射为物理页框号← 正确答案
  • C.存储程序的源代码
  • D.管理磁盘空闲块

答案:B。页表是虚拟页到物理页框的映射表,MMU 通过它完成地址转换。仓库比喻:账本记录第几箱货放到第几号格子。页大小是系统定死的,不靠页表记录,A 错误。

Q4. 不启用 TLB 快表时,段页式存储管理访问一次数据需要几次内存访问?

单选题 · mid

  • A.1 次
  • B.2 次
  • C.3 次← 正确答案
  • D.4 次

答案:C。段页式需要先查段表得到页表起始地址(1 次),再查页表得到页框号(1 次),最后访问目标数据(1 次),共 3 次。仓库比喻:先翻大区账本,再翻箱格账本,最后取货。TLB 可把开销压到接近 1 次,但那是另一回事。

Q5. 与分段相比,分页最明显的劣势是?

单选题 · mid

  • A.存在外部碎片
  • B.共享与保护粒度太细← 正确答案
  • C.无法离散分配
  • D.不需要地址转换

答案:B。分页以固定页为共享/保护单位,很难按“代码段”“数据段”这样的逻辑整体来共享或加权限。仓库比喻:想共享整条商品线,却只能一格一格授权,极为繁琐。分页没有外部碎片且天然离散分配,A、C 都反了。

Q6. 下列关于分段优点的表述,正确的有?

多选题 · mid

  • A.按逻辑单位共享更方便← 正确答案
  • B.段内没有内部碎片← 正确答案
  • C.完全消除了所有碎片
  • D.便于针对段设置读写保护← 正确答案

答案:ABD。段的长度由逻辑内容决定,按需分配,段内不浪费,故 B 对;共享和按段保护都很顺手,A、D 对;但段与段之间仍可能出现空隙(外部碎片),C 错。仓库比喻:分类区内部不浪费,但区与区之间有过道。

Q7. 关于 TLB(快表)的说法,正确的有?

多选题 · hard

  • A.缓存最近用过的页表项← 正确答案
  • B.TLB 命中时无需再访问内存中的页表← 正确答案
  • C.TLB 未命中时需要查内存页表← 正确答案
  • D.TLB 容量越大访问一定越快

答案:ABC。TLB 本质是页表项的硬件高速缓存,命中即可跳过内存页表访问,未命中还需查内存中的页表并更新 TLB,A、B、C 对。但 TLB 容量受成本和功耗约束,不是越大越快,D 错。仓库比喻:记忆中的热货位置有限,记太多反而顾不过来。

Q8. 某系统页大小为 2KB,某进程占用 6000B,其内部碎片为多少?

单选题 · hard

  • A.144B← 正确答案
  • B.1024B
  • C.2048B
  • D.0

答案:A。6000B 需要 ceil(6000/2048)=3 页,3×2048=6144B,内部碎片 = 6144-6000=144B。仓库比喻:6000B 货用 3 个标准箱装,最后一箱空着 144B。前两箱是满的,只有最后一箱产生浪费。

Q9. 分段地址转换中,若段内偏移 d 大于等于段长 L,系统应当如何处理?

单选题 · hard

  • A.忽略偏移继续访问
  • B.触发越界异常← 正确答案
  • C.自动扩展该段
  • D.返回 0 值

答案:B。偏移越过段长说明访问了不属于该段的地址,是严重的地址越界,MMU 会触发保护异常,防止程序破坏其他区域。仓库比喻:你报的货位超出了本区范围,保安会拦住你,而不是放你乱闯。

Q10. 某系统虚拟地址空间 1GB,页大小 4KB,每个页表项 4B,则该进程单级页表占用多少内存?

单选题 · extreme

  • A.256KB
  • B.1MB← 正确答案
  • C.4MB
  • D.16MB

答案:B。1GB/4KB = 262144 个页表项(2^18),每项 4B,共 262144×4 = 1048576B = 1MB。仓库比喻:账本里要有 26 万条“第几箱→第几格”记录,每条占 4 字节。这正是单级页表过大的原因,也是多级页表存在的意义。