fork时未触发写时复制?为何父子进程内存地址仍相同
问题:子进程修改内存后,父子进程内存地址为何一致?
根据写时复制(copy-on-write)机制,fork创建子进程后,初始时父子进程指向同一内存区域,直到其中一个进程修改内存时,才会复制该内存并在副本上执行修改操作。但如下代码的输出显示,子进程修改gFlag后,父子进程的gFlag内存地址仍保持一致,请问这一现象的原因是什么?
using namespace std; using ULL = unsigned long long; int gFlag = 0; int main () { gFlag = 1; cout << (ULL)&gFlag << endl; pid_t ret = fork (); if (ret > 0) { sleep (6); cout << "Parent: " << gFlag << endl; cout << "Parent: " << (ULL)&gFlag << endl; return 0; } else { gFlag = 15; cout << "Child: " << gFlag << endl; cout << "Child: " << (ULL)&gFlag << endl; } return 0; }
输出结果:
94022699245908 Child: 15 Child: 94022699245908 Parent: 1 Parent: 94022699245908
解答
这是因为你打印的是虚拟地址,而非真实的物理内存地址:
- 现代操作系统都采用虚拟内存机制,每个进程都拥有独立的虚拟地址空间。fork创建子进程时,会完整复制父进程的虚拟地址映射关系,所以父子进程的虚拟地址空间布局完全一致。
- 写时复制(COW)机制作用的是物理内存页:当子进程修改
gFlag时,操作系统会为该变量所在的物理内存页创建一个副本,子进程的虚拟地址会重新映射到这个新的物理页;而父进程的虚拟地址依然映射到原来的物理页。 - 但父子进程的虚拟地址是各自进程内部的逻辑地址,二者的虚拟地址空间相互独立,所以
&gFlag打印出的数值完全相同——这个地址只是进程自己“认知”的地址,和真实的物理内存位置没有直接对应关系。
简单总结:虚拟地址只是进程内部的逻辑标识,父子进程的虚拟地址可以完全相同,但它们背后映射的物理内存已经在修改操作后分开了,所以变量值不同,但打印的地址看起来一致。
内容的提问来源于Stack Exchange,提问作者Vipul Gupta
相关产品推荐
相关产品推荐

