Linux 进程地址空间与虚拟内存机制深度解析
程序运行的本质是进程的执行,而进程地址空间正是支撑进程独立运行的核心骨架。它划分内存区域、隔离不同进程,决定了代码、数据该如何存放与调用。搞懂它,才算真正触碰到程序运行的底层逻辑。
一、重新认识 C/C++ 内存
在 C/C++ 学习中,我们常接触内存区域划分:

不妨大胆猜想一下:我在栈上创建的临时变量 a,它打印出的这个地址,是物理内存地址吗?
通过一段代码验证:


这里存在一个值得注意的现象:子进程修改了全局变量 a 的值,父进程没跟着变,这符合父子进程相互独立的预期;但问题是,他俩的 a 值明明不一样,对应的地址怎么会是相同的呢?
结论很明确:我们平时在代码里看到的地址,其实并不是物理地址,而是虚拟地址。
二、Fork 遗留问题探讨
在学习 fork 函数时,常有一个疑问:一个变量怎么会有不同的内容?当时讲的是'写时拷贝'(Copy-On-Write),为父子进程的变量各自创建副本。但具体机制如何运作,往往不够直观。
这就涉及到进程地址空间的本质。创建进程时,操作系统会分配 task_struct(相当于进程的身份证),并配置两个关键组件:进程地址空间和页表。
我们在代码中看到的'地址',全是进程地址空间里的虚拟地址。虚拟地址如何对应到真实内存?全靠页表这个'翻译官'。比如定义全局变量 int a = 100,它待在进程地址空间的'已初始化数据区',页表会给它分配一个物理内存位置。访问 a 时,实际上是让页表把虚拟地址翻译成物理地址,再去真实内存取值。
当调用 fork 生成子进程时,子进程会复制父进程的进程地址空间和页表。刚开始,父子俩的代码、数据都是共享的,页表映射关系也一模一样。
但如果子进程尝试修改 a(例如改成 200),系统会检测到该地址是'共享只读'的。此时'写时拷贝'机制介入:系统给子进程新划一块物理内存,复制原值(100)过去,再改为 200,最后更新子进程的页表,将 a 的虚拟地址指向这块新地儿。
至此,父子俩的 a 虚拟地址看着一样,实际早已躺在不同物理内存里。这就是'同一个变量名,能存俩值'的底层原理。
三、进程地址空间详解
1、什么是进程地址空间?
进程地址空间是操作系统为每个进程虚拟出来的、独立的内存地址范围(如 32 位系统中通常是 0~4GB)。它让进程以为自己独占了内存,实际是通过'虚拟地址→物理地址'的映射来管理真实内存。
CPU 与内存的交互逻辑





