为什么子进程和父进程的变量的地址是相同
问题描述:
这里是我的代码为什么子进程和父进程的变量的地址是相同
int main()
{
pid_t pid;
int y = 3;
if ((pid = fork()) <0)
return -1;;
if(pid == 0) /* child */
{
printf(" before: %d %p\n", y, &y);
y *= 10;
printf("after: %d %p\n", y, &y);
}
else /* father */
{
sleep(1);
printf("father: %d %p\n" , y , &y);
}
return 0;
}
程序的输出就像下面:
before: 3 ffbff440
after: 30 ffbff440
father: 3 ffbff440
我的问题是,为什么是地址子和父变量相同但值不同?
答
因为这是一个虚拟地址,而不是物理地址。每个进程都有自己的地址空间(例如,一个32位系统可能允许每个进程拥有自己的地址空间,并拥有完整的4G范围)。
这是将虚拟地址映射到物理地址的内存管理单元(如果换出的页面需要从辅助存储中购回,则处理页面错误等问题)。
下图可以帮助,每个部分代表的存储器中的4K块:
Process A Physical Memory Process B
+-------+ +-------------+ +-------+
0K | |----> 0K | (shared) | <----| | 0K
+-------+ +-------------+ +-------+
4K | |--+ 4K | | <----| | 4K
+-------+ | +-------------+ +-------+
8K | | +-> 8K | | | | 8K
+-------+ +-------------+ +-------+
| : : : : : : : |
| +-------------+ |
| 128K | | <--------+
| +-------------+
+--------> 132K | |
+-------------+
可以看到,该图中,虚拟存储器地址和物理存储器地址(以及工艺的可能性之间的脱节以共享内存块)。左下方的地址是进程所看到的虚拟地址。
*块中的地址是实际的物理地址,其中数据“真”是,而MMU处理映射。
有关fork
(和exec
)的更深入说明,您可能还需要查看this answer。
答
地址是'相同的',因为每个进程都有自己的虚拟地址空间,变量通常会被加载到相同的位置。请注意,这不是内存中的物理地址。还要注意,有些方案故意随机加载进程的位置,以便更难以攻击/破解进程。在这种情况下,地址将会不同。
另请注意,它必须这样。如果'y'的地址确实发生了变化,那么在fork之前的变量或结构中保存的任何指针(地址)将不再正确。这会使得fork变得更加有用,因为子进程将无法访问任何数据结构(链接列表,树)或以前动态分配的数据,因为这些依赖于指针。没有虚拟内存的系统几乎不能实现fork,您可以改为使用vfork(http://pubs.opengroup.org/onlinepubs/7908799/xsh/vfork.html)。 –
从技术上讲,“必须这样” - 如果你的C运行时支持,你可以使用双向间接指针(换句话说,指针是一个已知值的偏移量,当分叉时,你将数据移动到其他地方孩子并调整已知的值)。这将是一个性能杀手。我认为实模式Windows通过锁定和解锁内存做了类似的事情。 – paxdiablo
不够公平,你已经基本上描述了一种廉价而快乐的(或昂贵而悲惨的,取决于你如何看待它)的虚拟内存的软件实现。在这种情况下,您仍然可以看到提问者观察到的情况,即分叉前后的地址打印为相同的值。除非'%p'打印格式化程序也添加到基地址中,否则我想:-) –