首页 文章

Fork - 相同的内存地址?

提问于
浏览
14

这是关于Linux中的C语言 .

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

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

5 回答

  • 0

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

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

  • 20

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

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

    因此,虽然看起来两个进程对于变量具有相同的地址,但这只是虚拟地址 .

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

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


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

  • 1

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

  • 2

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

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

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

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

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

  • 5

    因为您正在打印堆栈变量的地址(局部变量) . 它的地址是一样的(无论你是否更新它的 Value 都没关系) . 因为这两个进程共享一个公共虚拟堆栈 .

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

相关问题