叉 - 相同的内存地址?
这是关于Linux中的C.叉 - 相同的内存地址?
我有fork()
在main()
我在哪里创建2个子进程。然后,在两个子进程中运行函数abc()
,其中有一个局部变量x
。我写了一些价值。然后我用printf("%p",&x)
打印这个变量的地址。
这两个进程都打印相同的地址。我认为每个孩子都会得到父母记忆的(独立)副本。我需要每个流程都有自己的变量x
。我该怎么做,或者我做错了什么?
您需要了解物理内存与进程的虚拟地址空间之间存在断开。
每一个进程都得到它的自己的 4G虚拟地址空间,这是操作系统和硬件内存管理器将您的虚拟地址映射到物理地址空间的工作。
所以,虽然它可能似乎这两个进程具有相同地址的变量,这仅仅是虚拟地址。
的内存管理器将其映射到一个完全不同的物理地址一个。
这个映射也允许你运行10个进程,每个进程占用1G,即使你的机器只有4G的物理内存。当您尝试使用它们时,操作系统可以将您的内存位移出到磁盘并将其重新接入。
一个:大多数情况下,这是事实。如果您在进程之间共享内容,它可能映射到相同的物理地址。例如,共享内存,内核代码和数据,动态库等等。
由于虚拟内存系统的原因,每个子进程都有自己的具有相同(虚拟)地址的变量。
相同的虚拟地址不会指向相同的物理位置。
如果您停下来思考一分钟,那么fork
将不可能在父进程和子进程中为变量提供单独的地址。你可能已经将地址存储在内存中的任何地方,或者散列它们,或将它们保存到一个文件或任何其他地方,然后依赖这些有效地址的孩子中的任何内容都将被破坏。实际上fork
确实是和必须创建一个子进程,其中虚拟地址空间与父虚拟地址空间相同。
我想fork的可测量副作用实际上并不是fork时的虚拟地址空间的一部分。 – Neil 2011-03-19 23:39:51
如果加载到新进程中(通常情况下),“保存到文件中的地址”可能会被破坏,所以这几乎不是'fork'的限制。但是,内存中复杂的指针使用数据结构必须保持不变。 – 2011-03-20 00:15:29
@Ben:如果重新加载到未调用exec的相同进程或分支后代,则不会。 – 2011-03-20 00:45:18
要了解如何发生这种情况,您需要了解Linux的进程/线程模型。 Linux遵循从UNIX继承的fork-and-exec模型。在这个模型中由fork()系统调用产生的进程是线程和Windows进程之间的交叉。
当线程产生时(在Linux或Windows中无关紧要),新线程与父级共享其地址空间。两者都可以通过访问相同的地址找到相同的对象。但是这些线程使用不同的堆栈。结果两个线程的局部变量保证不具有相同的地址。
当在Windows环境中产生进程时,操作系统从头建立全新的地址空间并通过内存和需要的数据填充它。理论上,两个进程的局部变量可以具有相同的地址,但实际上这个可能性非常低。即使在两个变量都使用相同地址的情况下,这两个变量仍然是不同的对象。
UNIX的进程与线程和Windows进程具有相似性。就像第二种情况一样,OS将为子进程创建新的地址空间,但与Windows相反,Linux通过使用写时复制(COW)方法惰性地复制父进程地址空间来创建它。 COW意味着两个进程将共享相同的内存,但直到其中一个进程将修改它为止。在试图写入内存的时刻,操作系统将再次涉及到复制将被更改的内存块,并将一个副本分配给父节点,并将另一个副本分配给子节点。从这一刻开始,每个进程都将在修改后的内存块中使用它自己的独立副本,但它们仍然具有相同的地址。存储在其上的堆栈和局部变量也是如此。
在你的情况下,你有两个孩子有两个同样的堆栈副本,其中局部变量存储在相同的地址但不同的地址空间。然后你在两个孩子身上运行相同的代码。换句话说,您具有相同的堆栈布局初始状态,并以相同的方式运行修改此布局的相同代码。因此,您将拥有位于相同地址的相同本地变量。
因为您正在打印堆栈变量(局部变量)的地址。它的地址将是相同的(无论你是否更新它的值)。因为这两个进程共享一个共同的虚拟堆栈。
但是,如果您试图在公用函数内打印一个全局变量的地址(从父进程和子进程调用),那么其地址将相同,直到您不更新它的值。 如果进程更新全局变量的值,那么该进程将具有唯一副本(通过写入机制复制)。
您是否有原因发布到3.5岁的问题?请尽量避免“我也是”的答案。 – 2014-12-18 05:42:15
事实上,它将全部映射到相同的物理地址,直到发生写时复制。 – 2011-03-19 23:36:24
谢谢,我现在明白了。 – Asheron2332 2011-03-20 00:08:49
@R,我会声称作为末尾的“等等”位的一部分:-) – paxdiablo 2011-03-20 02:16:16