物理地址 vs 逻辑地址:内存世界的“身份证号“与“快递地址“之谜
|
zhenglin
2025年11月3日 10:32
本文热度 311
|
先来个生活比喻:
假如你网购商品📦:
一、内存寻址的挑战
程序直接操作物理内存会引发灾难:
// 危险操作:直接写物理地址
*(0x0000FFFF) = 100; // 可能覆盖操作系统代码!
问题❌:
多程序可能互相覆盖内存(A程序破坏B程序数据)
无法保证内存隔离与安全
程序需知道物理布局才能运行(移植性差)
解决方案:
引入地址空间抽象层,让程序活在"虚拟世界"!
二、逻辑地址:程序眼中的"虚拟世界"
特点:
程序员/编译器看到的地址(如0x401000)
连续的地址空间(从0到最大值)
每个程序独占自己的逻辑地址空间
// C语言代码中的地址都是逻辑地址
int num = 10;
printf("变量地址:%p\n", &num); // 输出类似0x7ffd4a3b2c
逻辑地址空间布局:

三、物理地址:硬件的"真实坐标"
特点:

关键区别:
逻辑地址是连续的虚拟序列,物理地址是离散的硬件位置!
四、为什么需要双重地址?
四大核心需求:
五、地址转换:MMU的魔法
内存管理单元(MMU) 负责实时转换:

转换过程详解:
CPU生成逻辑地址(代码中看到的地址)
MMU通过页表/段表查询映射关系
计算得到物理地址(硬件实际位置)
内存控制器访问对应物理位置
六、两种转换机制对比
1. 分段机制(早期方案)
// 段寄存器存储基址
struct Segment {
uint32_t base; // 段基址
uint32_t limit; // 段长度
};
// 转换过程
physical_address = segment.base + logical_address;
缺点:易产生内存碎片
2. 分页机制(现代方案)

页表示例(4KB页):

七、代码实战:观察地址转换
C程序查看地址:
#include <stdio.h>
int global; // 全局变量(数据段)
int main() {
int stack = 0; // 栈变量
int *heap = malloc(sizeof(int)); // 堆变量
printf("代码段地址:%p\n", main);
printf("数据段地址:%p\n", &global);
printf("堆地址:%p\n", heap);
printf("栈地址:%p\n", &stack);
free(heap);
return 0;
}
/* 输出示例:
代码段地址:0x401520
数据段地址:0x404000
堆地址:0x1e8b260
栈地址:0x7ffd4a3b2c
*/
Python查看内存映射
# Linux查看进程内存映射
$ pmap -x <pid>
# 输出示例:
Address RSS Mode Mapping
0000555555554000 4K r-x-- main # 代码段
0000555555755000 4K rw--- [heap] # 堆
00007ffff7a00000 1800K r---- libc.so.6
00007ffff7dd6000 16K rw--- [stack] # 栈
八、物理地址 vs 逻辑地址 终极对决

九、总结
逻辑地址 = 程序的"虚拟门牌号"(开发者可见)
物理地址 = 内存的"真实GPS坐标"(硬件使用)
MMU = 实时地址转换的"魔法翻译官"
分页机制 = 现代系统的内存管理基石
参考文章:原文链接
该文章在 2025/11/3 10:33:49 编辑过