2.3 存储管理与地址变换


2.3 存储管理与地址变换

本节摘要:8086 用"段基址左移四位加偏移"把两个 16 位数拼成 20 位物理地址——这个看似笨拙的方案既成全了低成本,也留下了段重叠、重定位、实模式与保护模式之分的全部故事。

时序问题上一站已经讲透,本节补齐主线出发前的最后一环:指令与数据的地址从哪来。理解了地址变换,2.2 节里"T1 拍送上 20 位地址"的那个数字才算有了出处。

一个地址的两副面孔

程序里出现的地址从来不是物理地址,而是"段值:偏移"的逻辑地址对。CPU 拿到它后做一次固定动作:段值乘以十六(二进制下左移四位),加上偏移,得到 20 位物理地址。比如逻辑地址 1234H:5678H 变换如下:

段值 1234H → 左移四位 → 12340H 偏移 5678H → 对齐相加 → 05678H 物理地址 = 179B8H

这个设计的动机是纯粹的经济学。8086 引脚上要送 20 位地址(1MB 空间),但内部寄存器全是 16 位,直接寻址只能覆盖 64KB。扩寄存器到 20 位意味着指令格式、数据通路全面加宽,成本陡增;用一个移位加法拼地址,就把"寄存器 16 位"与"空间 1MB"两个需求同时满足了。每条访存指令都由 BIU 的加法器现场完成这次变换,程序员几乎无感。

段重叠是理解这个方案的钥匙。 段值只需 16 位且以 16 字节为步进对齐,相邻段之间必然有覆盖:逻辑地址 1000H:FFFFH 与 2000H:0FFFH 指向同一个物理单元。同一个物理地址可以由四万多种逻辑地址对拼出来——这带来两个后果。好的一面:代码可以整体搬移,只要段值同步改写,偏移不变,程序照跑;坏的一面:没有硬件检查的实模式下,段越界只是悄悄改写了隔壁段的数据,错误安静得可怕。

案例展开:重定位是怎么发生的

背景:一个 DOS 时代的执行程序被加载到内存时,加载器无法预知空闲内存的起点,代码里却写死了对数据段的访问偏移。操作:链接器在生成文件时把"所有引用段基址的位置"登记成重定位表;加载器选定实际段址后,逐项把段值填进代码预留的位置。结果:同一份程序,这次加载到 2000H 段、下次加载到 3C00H 段,都能正确运行。解读:段机制天然携带了"代码与位置解耦"的能力——偏移是编译期决定的相对坐标,段值是加载期填入的绝对锚点。这个思想在今天的共享库里依然活着,只是锚点从段寄存器换成了页表。变式:若程序内用绝对物理地址硬编码(直接写死 179B8H 这样的值),重定位机制立即失效——这也是嵌入式代码审查中"禁止裸地址常量"规则的出处。

实模式与保护模式的分野

段机制的原始形态就是实模式:变换只有一步,无任何保护检查,四个段寄存器直接当基址用。80286 之后引入保护模式,段寄存器的含义脱胎换骨——它不再存基址,而是存一个"选择子",指向操作系统维护的描述符表,由硬件查表得到真正的基址与界限,越界访问当场触发异常。分页机制随后叠加上来,把连续的线性地址切成页帧映射到物理内存。

三者的关系可以这样概括:

特性 实模式 保护模式
地址变换 段值移位加偏移,一步到位 段选择子查描述符,再经页表映射
保护能力 无,越界静默发生 特权级、界限检查、页级读写控制
寻址空间 1MB 视位宽,32 位下 4GB 起步
典型居民 固件引导、裸机程序、教学 现代操作系统与应用

为什么本册仍以实模式为主线:它的地址变换肉眼可算、时序完全裸露,是建立直觉的最佳标本;保护模式的一切检查都叠在同样的总线时序之上,直觉可以平移。现代 ARM 与 RISC-V 处理器开机同样运行在"裸地址直通"的状态,由固件一步步把 MMU 配起来——每个系统的幼年期都是实模式。

手算练习与著名的回卷现象

地址变换必须练到脱口而出。三道题热身:逻辑地址 2000H:1234H,物理地址=20000H 加 1234H=21234H;逻辑地址 0FFFFH:8000H,物理地址=0FFFF0H 加 8000H=107FEFH;注意第二题——结果超过了 1MB 边界(0FFFFFH)。这正是实模式一个著名现象的来路:八十位的加法器送出二十一位结果,而地址总线只有二十位,最高位被丢弃,地址"卷回"到低端。早期 PC 用第 21 根地址线的通断来兼容老软件,那个著名的闸门(工程界称 A20 门)控制的就是这位要不要放行。一个历史的补丁能活几十年,这就是契约稳定性的双面性。

再来一道设计题:若要把程序放在物理地址 40000H 处、段内偏移从 0000H 开始,段值应是多少?四万零除以十六,段值取 4000H 即可。但若程序必须放进 3FFFFH 结尾、40000H 开始的区域而段值只能取整——段值以十六为步进,段起点只能是十六的倍数,程序的实际存放地址要与段起点对齐后再算偏移。段对齐的粒度决定了地址分配的自由度,这也是老程序常按段(十六字节)对齐的缘故。

追问之外再补一条常见的困惑:段寄存器为什么恰好四个?答案是"够用且省"。代码、数据、栈、附加段,恰好覆盖一个朴素程序的四种访问去向;每加一个段寄存器,指令编码就要多一组位、硬件多一套寄存器。四段制是那个年代"最小完备"的标本——不多给一个,也不少给一个。现代系统的段概念早已淡化为保护机制的载体,但"用少量基址锚定大量访问"的思想,在页表、在共享库、在片上的地址翻译里都还活着。

高频追问两则

问:段值乘十六这个设计,为什么不直接用二十位寄存器? 当年的工艺下,二十位寄存器意味着指令格式、加法器、数据通路全面加宽,成本陡增;而移位四位在硬件上只是接线错位,零成本。工程史上的经典权衡——用软件可见的别扭换硬件可承受的成本——在这里体现得淋漓尽致。

问:保护模式的描述符表为什么能提供保护? 因为基址与界限不再由程序员随手填写,而是由操作系统审核后登记在表里;硬件每次访存都先查表核对"地址是否越界、特权级是否够格",越界即触发异常。保护的本质是把"信任"从程序转移到审核机制——这与现代操作系统的权限思想同源。

本节要点回顾

  • 两副面孔:程序用逻辑地址,总线送物理地址,BIU 的加法器负责换算。
  • 段重叠双刃剑:成就了重定位的灵活性,也放任了无保护的越界。
  • 重定位思想不朽:相对坐标加加载期锚点,从段机制一路活到共享库。
  • 保护模式是叠加而非替换:查表与分页都架设在同一条总线时序之上。

地址的事说清了,主线还差一段"软件与硬件的会师":高级语言写的程序,怎样把函数调用安顿在这些寄存器与栈里——这是下一节混合编程的主题。


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