分叉 - 相同的内存地址?

时间:2011-03-19 23:17:36

标签: c linux fork

这是关于Linux的C.

我在fork()中有main(),我创建了2个子进程。然后,在两个子进程中运行函数abc(),其中有一个局部变量x。我在里面写了一些价值。然后我用printf("%p",&x)打印此变量的地址。

两个进程都打印SAME地址。我以为每个孩子都得到父母记忆的(独立)副本。我需要每个进程都有自己的变量x。我怎么能这样做或者我做错了什么?

5 个答案:

答案 0 :(得分:22)

您需要了解物理内存与进程的虚拟地址空间之间存在脱节。

每个进程都获得自己的 4G虚拟地址空间,操作系统和硬件内存管理器的工作就是将虚拟地址映射到物理地址。

所以,虽然它可能似乎两个进程具有相同的变量地址,但这只是虚拟地址。

内存管理器会将其映射到完全不同的物理地址 a

此映射还允许您运行十个进程,每个进程占用1G,即使您的计算机只有4G物理内存。操作系统可以将内存中的一些内容交换到磁盘,并在您尝试使用它们时重新启用它们。


a :大多数情况下,这都是事实。如果您在进程之间共享内容,它可能会映射到相同的物理地址。例如,共享内存,内核代码和数据,动态库等。

答案 1 :(得分:5)

如果你停下思考一分钟,fork就不可能在父和子进程中为变量提供单独的地址。您可能已经将地址存储在内存中的任何位置,或者对它们进行哈希处理,或将它们保存到文件或其他任何内容中,然后孩子中依赖这些地址有效的任何内容都会可怕地中断。事实上,fork 确实必须创建一个子进程,其中虚拟地址空间与父虚拟地址空间相同。

答案 2 :(得分:2)

由于虚拟内存系统,每个子进程都有自己的变量,具有相同的(虚拟)地址。

相同的虚拟地址不会指向相同的物理位置。

答案 3 :(得分:1)

要了解这是如何发生的,您需要了解Linux的进程/线程模型。 Linux遵循从UNIX继承的fork-and-exec模型。在此模型中由fork()系统调用生成的进程是线程和Windows进程之间的交叉。

当线程产生时(在Linux或Windows中没有关系),新线程与父级共享其地址空间。两者都可以通过访问相同的地址找到相同的对象。但是这些线程使用不同的堆栈。因此,保证两个线程的局部变量不具有相同的地址。

当在Windows环境中生成进程时,操作系统从头开始构建全新的地址空间,并根据所需的内存和数据填充它。理论上,两个过程的局部变量可以具有相同的地址,但实际上这个概率非常低。即使在两个变量都使用相同地址的情况下,这两个变量仍然是不同的对象。

UNIX的进程与线程和Windows进程具有相似性。与第二个类似,OS将为子进程创建新的地址空间,但与Windows相反,Linux通过使用写时复制(COW)方法延迟复制父进程地址空间来创建它。 COW意味着两个进程将共享相同的内存,但直到其中一个进程将修改它。在尝试写入内存的那一刻,操作系统将再次涉及复制将要更改的内存块,并将一个副本分配给父项,将另一个副本分配给子项。从这一刻开始,每个进程都将使用自己独立的对象在修改的内存块中复制,但它们仍将具有相同的地址。对于存储在其上的堆栈和局部变量也是如此。

在您的情况下,您有两个具有相同堆栈的两个副本的子项,其中局部变量存储在相同的地址上但位于不同的地址空间中。然后你在两个孩子上运行相同的代码。换句话说,您具有相同的堆栈布局初始状态,并运行相同的代码,以相同的方式修改此布局。因此,您将在相同的地址上拥有相同的局部变量。

答案 4 :(得分:0)

因为您正在打印堆栈变量的地址(局部变量)。它的地址是相同的(你更新它的值是否重要)。因为这两个进程共享一个公共虚拟堆栈。

但是如果你试图在公共函数中打印全局变量的地址(从父进程和子进程调用),那么它的地址将是相同的,直到你没有更新它的值为止。 如果进程更新全局变量的值,那么该进程将具有它的唯一副本(通过写入机制上的副本)。