fork()的工作原理

3

我最近学习了C语言中的fork()函数。由于这个函数会创建两个并发进程,并且这两个进程共享内存,所以我有以下代码:

#include<stdio.h>
int main()
{
  int pid,i;
  i=0;
  pid=fork();
  if(pid==0)
    {
      i++;
      printf("child process:: address of i:%u value of i:%d\n",(int)&i,i);
    }
  else if(pid>0)
    {
      wait(NULL);
      i--;
      printf("parent process:: address of i:%u value of i:%d\n",(int)&i,i);
    }

  return 0;
}

我得到的输出是:
    child process:: address of i:3215563096 value of i:1
    parent process:: address of i:3215563096 value of i:-1

但是由于每次子进程都先执行,所以内存位置3215563096处的值应该变为+1,而父进程的值为0。

我的期望输出是:

    child process:: address of i:3215563096 value of i:1
    parent process:: address of i:3215563096 value of i:0

请问有人能告诉我哪里出了错吗?


有没有任何方法可以得到我想要的结果? - Piyush Goyal
使用共享内存。查看mmapshm_open等。 - cnicutar
2
抱歉挑剔一下,但fork不是C函数(也不在C语言标准中),而是Posix标准函数(实际上,在Linux上是一个系统调用)。 - Basile Starynkevitch
不要忘记使用 #include <unistd.h> 来声明 fork(),以及 #include <sys/wait.h> 来声明 wait()。在编译所有函数的原型不在作用域内时,您不应该进行编译。 - Jonathan Leffler
4个回答

5
第二个进程在使用fork时会使用与原始进程相同的内存;然而,该内存标记为写时复制(copy-on-write),这意味着一旦子进程尝试修改它,操作系统中的内存管理将复制页面,因此原进程将无法看到修改后的内存。更多信息请参见fork wiki

3
孩子和父母拥有不同的地址空间。虚拟内存地址(您正在打印的地址)是相同的。但这些地址指向不同的物理内存区域。
这就像两个人住在不同城市的“第一街道”。
这种方法的优点是每个进程都对内存有不同的视图 - 内存保护和隔离是固有的。
附注:打印地址时请使用%p而不是%u。

有没有办法获得我想要的输出? - Piyush Goyal
@Piyush:您可以映射一些共享内存,并存储指向共享内存的指针。两个进程将共享共享内存(因此得名),并且将看到彼此的更改。请参见shmat()等,或shm_open()等,或使用正确选项的mmap() - Jonathan Leffler

2
当您执行fork()函数时,子进程会获得父进程内存的一个完全复制品。它们不共享内存。
您变量i的地址是相同的,因为它们引用不同的地址空间 - 一个用于父进程,一个用于子进程。
您可能在考虑线程。线程共享内存。

1

fork()函数通过复制调用进程来创建一个新的进程。新进程被称为子进程,是调用进程的精确副本。

父进程的整个虚拟地址空间都会被复制到子进程中

这就是为什么printf()打印出相同的地址,但它们实际上位于不同的位置。

注意:

请记住,每当我们使用取地址运算符(&)时,我们只能得到虚拟地址(相对于您的进程而言)。物理地址仅由操作系统的内存管理器(操作系统的主要服务之一)进行管理。

要获取更多信息,请在Linux shell中使用命令。

man 2 fork

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接