6.4 内存保护:MMU 与 MPU


6.4 内存保护:MMU 与 MPU

本节摘要:保护是访存契约的最后一道门。本节比较两套门禁系统:面向通用应用的 MMU(页级翻译加权限位,缺页即报警)与面向实时的 MPU(固定区域加属性,不做翻译)。读完你应当能解释一次"段错误"从访问到异常的完整路径,并能为嵌入式与通用平台分别说清保护模型的选型理由。

圈地和门卫

6.1 节的页表顺便携带了保护属性:每个页除了物理页号,还挂着可读、可写、可执行的权限位与访问级别(用户态或特权态)。翻译硬件在给出物理地址之前顺手查权限——不符则抛保护异常。保护与翻译共用一套机构,是 MMU 模型最优雅的设计决定:圈地免费,因为翻译本来就要逐页进行。

权限位直接对应你熟悉的故障 taxonomy。代码段映射为可读可执行、不可写——往代码段写数据触发写保护异常(writable 与 executable 双向把关);数据段可读可写、不可执行——数据区的机器码想被执行(注入攻击的惯用手法)会被执行权限拦下,这就是 W 与 X 互斥防线(W^X)的硬件底座;栈页按需扩展、守卫页(guard page)故意不映射——栈溢出越界访问守卫页立刻触发异常,把静默的内存踩踏变成当场报错的崩溃。安全审计里的"数据不可执行、代码不可写",全是这些权限位在幕后当值。

MMU 模型的另一张王牌是按需调页(6.1 节已铺陈):页表项可以声明"此页暂不在内存",访问时缺页异常召唤操作系统搬货。这让程序可以声明远超物理内存的地址空间,也让零页探测、写时复制、内存映射文件这些机制得以运转。MMU 的代价同样清晰:页表本体占内存、TLB 未命中要遍历、异常处理路径长而不可预测——对硬实时系统,这些不确定性是原罪。

MPU:不打翻译的门卫

实时与深度嵌入世界给出另一套答案:MPU(内存保护单元)。它不翻译地址,物理地址直来直去;保护以少量固定区域为单位(典型 8 到 16 个),每个区域登记起止地址与属性(可读、可写、可执行、可缓存、特权访问)。访存时硬件拿地址与区域表比对,命中哪个区域就按哪个属性放行,落在区域之外按默认策略拒绝或放行。

RISC-V 的 PMP(物理内存保护)是这一思路的标准件:一组寄存器定义物理地址区间与权限,机器态(7 章的特权级)配置它们,用户态访问越权即异常。ARM 的 Cortex-M 系列走同路线(其地址映射甚至不叫 MMU 而叫 PMSA——保护式地址映射)。MPU 的哲学是小而确定:区域检查在单拍内完成、无翻译无 TLB、异常路径极短——中断延迟可精确到周期级,这正是实时控制(电机、汽车域控、医疗设备)的命门。

维度 MMU(应用世界) MPU(实时世界)
地址翻译 页表逐页翻译 不翻译,物理直通
保护颗粒 页(4 KB 起),数量近乎无限 固定区域(8 到 16 个)
异常延迟 长(走完整异常与缺页路径) 短且确定
按需调页 支持(缺页召唤操作系统) 不支持(内存规划在编译期或启动期)
典型宿主 应用处理器:桌面、手机、服务器 微控制器:电机、汽车、物联网终端

这张表的选择逻辑直白:需要进程隔离、按需内存与大地址空间,选 MMU;需要确定延迟与极简硬件,选 MPU。中间地带也存在——带 MMU 的处理器把某些区域用 MPU 思路锁死(内核只读数据),两个模型在同一颗芯片上分工协作。

一次段错误的完整旅程

把两章知识串成一条故障链,看清"段错误"到底走了哪些关卡。程序解引用一个野指针(比如偏移很大的空指针):

加载指令发出虚拟地址 0x0000000800000008 → 6.1:TLB 未命中,页表遍历到末级——表项标记无效 → 6.4:翻译失败 + 权限检查失败,硬件记下出错地址与访问类型 → 7.1(预告):抛出缺页异常,切换到内核态,异常向量指向操作系统 → 操作系统查映射表:该地址不在任何映射区 → 判定恶性缺页 → 向进程发送段错误信号,默认处置:终止并生成核心转储

你在调试器里看到的"访问违例发生在地址 X",是这条链上硬件记录的第一手证词;而"哪一步开始走错"要沿链条回溯——地址怎么算出来的、指针何时被污染。读懂故障链,调试就从"玄学"变成"读证词"。

两套门禁的选型推演

用一个选型场景把 6.4 收拢。某团队要做汽车域控的软件平台,候选方案:带 MMU 的应用处理器跑 Linux,或带 MPU 的微控制器跑实时操作系统。推演要点按本节知识逐条过:功能安全要求中断延迟确定——MMU 的缺页路径不可预测,直接减分;多应用隔离需求——MPU 的个位数区域扛不住多进程颗粒度,又一分给 MMU;内存预算紧张——MMU 的页表与 TLB 开销是净成本。两条约束打架时,产业的标准答案是双芯分工:实时域交给 MPU 微控制器,非实时的信息娱乐域交给 MMU 应用处理器,中间用受控通道通信。推演的意义不在答案,而在把 6.1 至 6.4 的知识变成可以摆上台面的权衡语句。

容易踩的坑

第一个坑:把保护异常一律当 bug。守卫页、写时复制、按需调页全是"故意触发的异常"——异常计数在健康程序里并不为零;性能剖析时要把良性缺页从故障统计里摘出去。第二个坑:以为开了 MMU 就万事大吉。页级权限防不了同进程内的逻辑越界(野指针落在你自己映射的有效页上照踩不误),更强的防线(语言安全、地址消毒器、7.4 节的硬件扩展)各有辖区。第三个坑:MPU 区域数量当无限用。实时系统里区域表只有个位数条目,每加一个保护区域都在挤占别人的名额,区域规划要像预算一样精打细算——这也是实时软件坚持静态内存布局的深层原因。

本节要点回顾

  • 保护与翻译共用页表机构:权限位随页走,W 与 X 互斥、守卫页、写时复制全是权限位的剧本。
  • MMU 的王牌是按需调页与进程隔离,代价是长而不可预测的异常路径——应用世界乐见,实时世界难容。
  • MPU 用少量固定区域换确定性:无翻译、单拍检查、异常极短,是实时与深度嵌入的标准答案;RISC-V 的 PMP 是其标准形态。
  • 段错误是一条完整故障链:TLB 未命中、页表无效、权限失败、异常入口、系统判定、信号处置——每环都有证词。
  • 保护异常不全是 bug:良性缺页是机制,恶性缺页才是事故,剖析时分开统计。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U