这是关于Linux的C.
我在fork()
中有main()
,我创建了2个子进程。然后,在两个子进程中运行函数abc()
,其中有一个局部变量x
。我在里面写了一些价值。然后我用printf("%p",&x)
打印此变量的地址。
两个进程都打印SAME地址。我以为每个孩子都得到父母记忆的(独立)副本。我需要每个进程都有自己的变量x
。我怎么能这样做或者我做错了什么?
答案 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)
因为您正在打印堆栈变量的地址(局部变量)。它的地址是相同的(你更新它的值是否重要)。因为这两个进程共享一个公共虚拟堆栈。
但是如果你试图在公共函数中打印全局变量的地址(从父进程和子进程调用),那么它的地址将是相同的,直到你没有更新它的值为止。 如果进程更新全局变量的值,那么该进程将具有它的唯一副本(通过写入机制上的副本)。