前言
在 C/C++ 开发中,我们经常会打印变量或函数的地址,但你有没有想过:这些地址真的是物理内存地址吗?为什么父子进程中同一个变量的地址相同,内容却能各自独立?其实,我们看到的所有地址都是虚拟地址,而 Linux 的'程序地址空间'(准确说是进程地址空间)正是这一切的核心。本文从地址空间布局、虚拟地址与物理地址的映射、内核数据结构三个维度,拆解 Linux 程序地址空间的底层逻辑,帮你搞懂'为什么虚拟地址能隔离进程''为什么 malloc 不是真的分配物理内存'等关键问题。
一。先看现象:打破你对'地址'的认知!
先通过一个简单的代码实验,感受虚拟地址的'诡异'之处:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0; // 全局变量
int main() {
pid_t id = fork(); // 创建子进程
if (id < 0) {
perror("fork failed");
return 1;
} else if (id == 0) {
// 子进程
g_val = 100; // 子进程修改全局变量
printf("子进程 [PID:%d]: g_val=%d, 地址=%p\n", getpid(), g_val, &g_val);
} else {
// 父进程
sleep(3); // 等待子进程修改完成
printf("父进程 [PID:%d]: g_val=%d, 地址=%p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
编译运行结果:
子进程 [PID:12345]: =, 地址=x80497e8
父进程 : =, 地址=x80497e8


