首页 文章

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

提问于
浏览
11

我引用“当进程使用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 回答

  • 24

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

  • 5

    从另一个线程引用自己 .

    • 发出fork()系统调用时,会创建与父进程对应的所有页面的副本,由子进程的OS将其加载到单独的内存位置 . 但在某些情况下不需要这样做 . 考虑一个子进行“exec”系统调用或在fork()之后很快退出的情况 . 当需要子进程来执行父进程的命令时,不需要复制父进程的页面,因为exec用要执行的命令替换调用它的进程的地址空间 . 在这种情况下,使用称为写时复制(COW)的技术 . 使用此技术,当发生fork时,不会为子进程复制父进程的页面 . 相反,页面在子进程和父进程之间共享 . 每当进程(父进程或子进程)修改页面时,就会对执行修改的进程(父进程或子进程)单独创建该特定页面的单独副本 . 然后,此过程将使用新复制的页面,而不是将来所有引用中的共享页面 . 另一个进程(未修改共享页面的进程)继续使用页面的原始副本(现在不再共享) . 这种技术称为写时复制,因为当某个进程写入页面时会复制该页面 .

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

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

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

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

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

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

    enter image description here

  • 4

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

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

  • 4

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

  • 1

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

  • 10

    在这两种情况下,是的地址是相同的 . 但是如果你在子进程和父进程中为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
    

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

相关问题