fork()系统调用和进程的内存空间

gma*_*man 11 c operating-system fork

我引用"当进程使用fork()调用创建一个新进程时,只有父进程和新分叉的子进程之间共享共享内存段.堆栈和堆的副本是为新创建的进程"从Silberschatz的"操作系统概念"解决方案.

但是当我尝试这个程序的时候

#include  <stdio.h>
#include  <sys/types.h>

#define   MAX_COUNT  200

void  ChildProcess(void);                /* child process prototype  */
void  ParentProcess(void);               /* parent process prototype */

void  main(void)
{
         pid_t  pid;
         char * x=(char *)malloc(10);

         pid = fork();
         if (pid == 0) 
            ChildProcess();
         else 
            ParentProcess();
        printf("the address is %p\n",x);
}

void  ChildProcess(void)
{
          printf("   *** Child process  ***\n");
}

void  ParentProcess(void)
{
         printf("*** Parent*****\n");
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

*** Parent*****
the address is 0x1370010
   *** Child process  ***
the address is 0x1370010
Run Code Online (Sandbox Code Playgroud)

父和子两者都打印堆中的相同地址.

谁能解释我这里的矛盾.请清楚说明父母和孩子在内存空间中共享的所有内容.

Pat*_*ard 27

从另一个线程引用自己.

  • 发出fork()系统调用时,会创建与父进程对应的所有页面的副本,由OS进行子进程加载到单独的内存位置.但在某些情况下不需要这样做.考虑一个子进行"exec"系统调用或在fork()之后很快退出的情况.当需要子进程来执行父进程的命令时,不需要复制父进程的页面,因为exec用要执行的命令替换调用它的进程的地址空间.

    在这种情况下,使用称为写时复制(COW)的技术.使用此技术,当发生fork时,不会为子进程复制父进程的页面.相反,页面在子进程和父进程之间共享.每当进程(父进程或子进程)修改页面时,就会对执行修改的进程(父进程或子进程)单独创建该特定页面的单独副本.然后,此过程将使用新复制的页面,而不是将来所有引用中的共享页面.另一个进程(未修改共享页面的进程)继续使用页面的原始副本(现在不再共享).这种技术称为写时复制,因为当某个进程写入页面时会复制该页面.

  • 另外,要理解为什么这些程序似乎使用相同的内存空间(事实并非如此),我想引用"操作系统:原理与实践"一书的一部分.

    大多数现代处理器引入了一种间接级别,称为虚拟地址.使用虚拟地址,每个进程的内存都从"相同"的位置开始,例如,零.每个过程都认为它拥有整个机器,尽管实际上并非如此.

    所以这些虚拟地址是物理地址的翻译,并不代表相同的物理内存空间,如果我们编译并运行多次显示静态变量方向的程序,我们可以做一个更实际的测试示例,比如这个程序.

    #include <stdio.h>
    
    int main() {
        static int a = 0;
    
        printf("%p\n", &a);
    
        getchar();
    
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    如果我们直接处理物理内存,就不可能在两个不同的程序中获得相同的内存地址.

    多次运行程序得到的结果是......

在此输入图像描述

  • @solidak,Patricio 的屏幕截图是 Windows 的。默认情况下,Windows 不会为所有进程启用地址空间布局随机化([wikipedia](https://en.wikipedia.org/wiki/Address_space_layout_randomization#Microsoft_Windows)),而 OSX 和 Linux 都这样做。 (2认同)

Eld*_*Bug 10

是的,两个进程对此变量使用相同的地址,但这些地址由不同的进程使用,因此不在同一个虚拟地址空间中.

这意味着地址是相同的,但它们并不指向相同的物理内存.您应该阅读有关虚拟内存的更多信息以了解这一点

  • 作为一种称为[copy-on-write](http://en.wikipedia.org/wiki/Copy-on-write)的优化技术,虚拟内存在fork()之后开始指向相同的物理内存,但是如果其中一个进程写入内存,则将复制内存并更改映射. (4认同)

Ser*_*rov 5

地址相同,但地址空间不同。每个进程都有自己的地址空间,因此父进程的 0x1370010 与子进程的 0x1370010 不同。


ine*_*ght 5

当内核fork()控制进程时,复制的内存信息继承相同的地址信息,因为堆是按原样有效复制的。如果地址不同,您将如何更新自定义结构内部的指针?内核对该信息一无所知,因此这些指针将失效。因此,物理地址可能会发生变化(事实上,即使在可执行文件的生命周期内,即使没有fork()ing,物理地址也经常会发生变化,但逻辑地址保持不变。


dav*_*vid 5

您可能正在使用虚拟内存的操作系统上运行程序.在fork()调用之后,父级和子级具有单独的地址空间,因此地址0x1370010不指向同一位置.如果一个进程写入*x,另一个进程将看不到更改.(实际上,这些内容可能是相同的内存页面,甚至是交换文件中的相同块,直到它被更改,但操作系统确保在父项或子项写入页面时立即复制页面,所以,只要该程序可以告诉它正在处理自己的副本.)