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

时间:2014-12-15 15:00:48

标签: 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");
}

结果如下:

*** Parent*****
the address is 0x1370010
   *** Child process  ***
the address is 0x1370010

父和子都打印堆中的相同地址。

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

6 个答案:

答案 0 :(得分:26)

从另一个帖子中引用自己。

  •   

    发出fork()系统调用时,所有页面的副本   对应父进程创建,加载到单独的进程中   操作系统为子进程提供的内存位置。但事实并非如此   在某些情况下需要。考虑一个孩子执行的情况   &#34; EXEC&#34; fork()后很快系统调用或退出。当。。。的时候   只需执行父进程的命令就需要子进程,   没有必要复制父进程&#39;页面,自exec   替换用它调用它的进程的地址空间   要执行的命令。

         

    在这种情况下,使用称为写时复制(COW)的技术。同   这种技术,当发生fork时,父进程的页面不是   为子进程复制。而是在页面之间共享   孩子和父母的过程。每当一个过程(父母或孩子)   修改页面,单独创建该特定页面的单独副本   对于执行修改的那个过程(父母或孩子)。   然后,此过程将使用新复制的页面而不是   在以后的所有参考文献中共享另一个过程(那个过程   没有修改共享页面)继续使用原始副本   页面(现在不再共享)。这种技术被称为   copy-on-write,因为当某个进程写入页面时会复制该页面。

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

      

    大多数现代处理器引入了间接级别,称为   虚拟地址。使用虚拟地址,每个进程的内存   从&#34;同一&#34;开始地方,例如,零。   尽管如此,每个进程都认为它拥有整个机器   显然事实并非如此。

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

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

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

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

enter image description here

答案 1 :(得分:10)

是的,这两个进程对此变量使用相同的地址,但这些地址由不同的进程使用,因此不在同一virtual address space

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

答案 2 :(得分:5)

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

答案 3 :(得分:4)

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

答案 4 :(得分:4)

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

答案 5 :(得分:1)

是,两种情况下的地址相同。但是,如果您在子进程和父进程中为x分配了不同的值,然后同时打印x的值和x的地址,则会得到答案。

#include  <stdio.h>
#include  <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#define   MAX_COUNT  200

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

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

    pid = fork();
    if (pid == 0) {
            *x = 100;
            ChildProcess();
    }
    else {
            *x = 200;
            ParentProcess();
    }
    printf("the address is %p and value is %d\n", x, *x);
}

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

void  ParentProcess(void)
{
    printf("*** Parent*****\n");
}

此输出将是:

*** Parent*****
the address is 0xf70260 and value is 200
*** Child process  ***
the address is 0xf70260 and value is 100

现在,您可以看到值不同,但地址相同。因此,两个进程的地址空间是不同的。这些地址不是实际地址,而是逻辑地址,因此对于不同的进程,它们可能是相同的。