存储管理 · 易错点总结
为什么要分逻辑地址和物理地址?
核心原因:让程序不用关心自己实际放在内存的哪个位置。
没有分离会发生什么?
- 程序员必须写明变量放在内存第几号位置 → 换台机器就崩
- 两个程序用了同一个物理地址 → 同时运行互相覆盖
分离后解决了什么问题?
| 问题 | 解决方案 |
|---|---|
| 程序独立性 | 每个程序都从”自己的0地址”开始写,OS 负责映射到真实物理地址 |
| 内存保护 | 程序A的逻辑地址0 ≠ 程序B的逻辑地址0,映射到不同物理区域,无法越界 |
| 灵活加载 | 物理内存哪里有空洞就映射到哪里,程序不用管 |
| 虚拟内存 | 逻辑地址空间可以大于物理内存,暂时不用的页放磁盘,用时调入 |
| 共享内存 | 两个进程的逻辑地址映射到同一块物理内存,实现高效通信 |
类比
逻辑地址 = 酒店房号(每个客人都以为自己是"101号房"的住客)
物理地址 = GPS经纬度(前台知道"101号房"实际对应大楼的哪个物理位置)
换了一个酒店 → 房号还是101 → GPS坐标完全不同
程序也一样,逻辑地址不变,物理地址随加载位置变化
谁来做转换?
MMU(内存管理单元) —— CPU 中的硬件,自动查页表完成逻辑→物理转换,程序无感知。
指针指向的是逻辑地址还是物理地址?
逻辑地址。 用户程序永远接触不到物理地址,从 CPU 执行的第一条指令到最后一个字节,全程经过 MMU 转换:
程序中的指针 → 逻辑地址 ──[MMU]──→ 物理地址 ──→ 实际内存芯片
↑ ↑
你能看到的 硬件自动完成,不可见
直观验证
int a = 42;
int *p = &a;
printf("%p", p); // 输出如 0x7ffd5c8a1234 → 这是逻辑地址
换个进程跑同样代码,打印出来的地址可能一模一样——因为每个进程有独立的逻辑地址空间,但映射到完全不同的物理位置。
指针不变,物理地址在变
进程A:指针 0x1000 → 逻辑地址 0x1000 ──→ 物理地址 0x8A000
进程B:指针 0x1000 → 逻辑地址 0x1000 ──→ 物理地址 0x3F000
↑ 相同 ↑ 不同
同一个指针值,换个进程、甚至 OS 把页面 swap 后重新调入,物理地址都可能完全不同——但程序逻辑不受影响,这正是分离的核心价值。
页式存储地址转换
逻辑地址和物理地址的关系
逻辑地址是程序访问时使用的地址,物理地址是数据在内存中的真实地址。
在页式存储中:
逻辑地址 = 页号 × 页面大小 + 页内偏移
物理地址 = 物理块号 × 页面大小 + 页内偏移
转换核心:
页号 -> 查页表 -> 物理块号
页内偏移保持不变
易错点
- 页面大小为 4K 时,等于
4096B = 1000H - 十六进制逻辑地址按
1000H拆页号和页内偏移 - 地址转换时只把页号换成物理块号,页内偏移不变
例题
页面大小为 4K,逻辑地址为 1B1AH:
1B1AH = 1 × 1000H + B1AH
所以:
页号 = 1
页内偏移 = B1AH
若页表中:
页号 1 -> 物理块号 6
则:
物理地址 = 6 × 1000H + B1AH = 6B1AH
口诀:页号换块号,偏移不变。