混乱的fork系统调用

4
我刚才在查看fork系统调用的行为,发现它非常令人困惑。我在一个网站上看到了以下内容:
Unix会制作父进程地址空间的精确副本并将其提供给子进程。因此,父进程和子进程有单独的地址空间。
#include <stdio.h>
#include <sys/types.h>

int main(void)
{
pid_t pid;
char y='Y';
char *ptr;
ptr=&y;
pid = fork();
if (pid == 0)
{
y='Z';
printf(" *** Child process  ***\n");
printf(" Address  is %p\n",ptr);
printf(" char value is %c\n",y);
sleep(5);
}
else
{
sleep(5);
printf("\n ***parent process ***\n",&y);
printf(" Address  is %p\n",ptr);
printf(" char value is %c\n",y);
}
}

上述程序的输出如下:
 *** Child process  ***
 Address  is 69002894
 char value is Z

 ***parent process ***
 Address  is 69002894
 char value is Y

从上述声明中可以看出,子进程和父进程具有独立的地址空间。这就是为什么char值被单独打印,并且为什么我在子进程和父进程中看到变量的地址相同的原因。
请帮助我理解这个问题!

某些特定语言的变量名称仅在编译时存在。在运行时,您的代码使用精确地址(对于堆栈,它使用从某个寄存器的偏移量,并且内存指针在程序映射到内存后被重新定位)。正如您的示例所述-在分叉当前进程之前,您将指针存储在某个位置(甚至可以是某个外部存储)。因此,如果fork()导致内存布局发生更改,则在从该系统调用返回后,您的程序将无法继续工作。 - ony
答案是正确的,但在您的实际示例中,还有另一个问题。在fork之前,您将Y的地址存储在ptr内,因此ptr包含“69002894”。在fork之后,无论Y的地址是否在两个进程中更改,ptr在父进程和子进程中仍将包含该值。为了正确,您必须在printf语句中打印&Y。 - Laurent Parenteau
@laurent 在 fork 之后,指针 ptr 也会被复制到子进程中。 这意味着子进程和父进程将拥有两个不同的指针。 请按照您自己在 printf 中建议的更改语句来检查,您将得到相同的结果。 - Vijay
是的,我知道你会得到相同的结果,原因在答案中提到了(虚拟地址空间)。 我想说的是,如果y的地址在子进程中不同(每个人都认为这不是情况),则PTR的值仍将是父进程中Y的地址,而不是子进程中Y的地址。 因此,在您的printf中看不到差异。 - Laurent Parenteau
5个回答

7
基本上,虚拟内存的概念让进程觉得它是系统的唯一所有者。它感觉自己可以访问完整的内存。
但事实上,操作系统只提供了一个由MMU映射到实际内存的虚拟内存。
因此,在您的情况下,每个进程(父进程和子进程)都有自己的地址空间。这对于两个进程是分离的。现在,在这里,地址空间指的是虚拟地址空间。
因此,尽管父进程和子进程中都存在相同的地址,但这只是虚拟地址,并且每个地址映射到不同的物理地址。
希望这能帮助你!

6
你说得对。一旦调用fork(),就会存在两个相同的进程。因此,每个进程中的y的地址都是相同的。两个进程之间唯一的区别在于,一个进程中fork()返回了0,而另一个进程中返回了子进程的PID。你的程序使用这些信息在父进程和子进程中执行不同的行为,以便获得适当的输出。
在“现实生活”中,操作系统进行了很多优化,使fork()非常快速。这意味着实际的物理行为可能并不涉及完全复制内存空间。但从逻辑上讲,你可以将其视为这样做。

但是关于“为子进程和父进程分离地址空间”的评论呢? - Vijay
3
@benjamin,是的,它们是不同的进程,因此每个进程都有自己独立的虚拟内存空间。一旦 fork() 返回,它们就完全独立了。 - Carl Norum
2
“独立的地址空间”意味着一个进程中的特定地址与另一个进程中相同地址的内存位是分开的。这就是为什么在您的示例中,地址69002894能够同时存储XY - “进程A中的地址69002894”是一个不同的位置,与“进程B中的地址69002894”不同。 - caf

6

它们具有独立的地址空间——这正是为什么相同的内存地址允许具有不同的值。内存地址只在进程上下文中具有意义。


3

221号P街地址与221号C街地址是两座独立的建筑物,尽管它们有相同的地址编号,但它们的内容不同。


1
你需要将 pid 替换为 ptr。要获取父进程和子进程的 ID,必须在 printf(" Address is %p\n",pid); 中使用 pid,而不是 ptr。在调用 fork() 后,程序会分成两个部分:一个是子进程,另一个是父进程。如果从在 fork() 之前使用过的变量中调用地址,则父进程和子进程都会得到相同的结果。

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