为什么fork()使用相同的变量但值不同?

时间:2015-09-24 12:55:44

标签: c++ c++11 fork

以下是代码:

#include <stdio.h>                                                                                                                                                            
#include <unistd.h>

void f(int&);
void g(int&);

int main(int argc, char **argv)
{
    printf("--beginning of program\n");

    int counter = 0;
    pid_t pid = fork();
    if (pid == 0) {
        f(counter);
        printf("child process: %d, %p", counter, &counter);
    } else if (pid>0) {
        g(counter);
        for (int i=0; i<5; ++i) {
            sleep(1);
            printf("parent process: %d, %p\n", counter, &counter);
        }
    }
    printf("--end of program--\n");
    return 0;
}

void f(int& counter) {
    counter = 1;
    printf("in f: %d, %p-\n", counter, &counter);
}
void g(int& counter){
} 

以及结果:

--beginning of program
in f: 1, 0x7ffc9b01c6a4-
child process: 1, 0x7ffc9b01c6a4--end of program--
parent process: 0, 0x7ffc9b01c6a4
parent process: 0, 0x7ffc9b01c6a4
parent process: 0, 0x7ffc9b01c6a4
parent process: 0, 0x7ffc9b01c6a4
parent process: 0, 0x7ffc9b01c6a4
--end of program--

显然在子进程中,它具有相同地址的相同参数,但值不同。

为什么会这样?

2 个答案:

答案 0 :(得分:10)

每个进程都有自己的虚拟内存空间

这意味着一个流程中的0x7ffc9b01c6a4与另一个流程中的0x7ffc9b01c6a4完全无关。

这不是同一个对象;它是新流程中的一个对象。由于第二个进程是从第一个进程分叉出来的,基本上是克隆进程,所以对象应该存在于第二个进程的第二个虚拟内存位置并不奇怪。

答案 1 :(得分:3)

这是fork()的基础,这正是允许它工作的原因。要理解这一点,您需要记住现代操作系统中进程的所有地址空间都是虚拟的 - 这意味着它与实际的物理内存数据无关。在地址0x8000处访问内存(如果我记得正确的地址)直接进入视频内存的日子已经一去不复返了。我以前用这种方式编程,而不是屏幕操作例程只是在视频内存中写入值,这要快得多。这很有趣:)

但现在已经没有了。现在在用户程序中,地址与物理内存无关,每当你在locaiton'0x1234567'访问内存时,都会进行转换。 CPU知道如何将此虚拟地址映射到物理内存地址,但没有其他人这样做。

因此,当您分叉您的进程时,会生成一个精确的内存副本。它具有相同的虚拟地址(因为内存副本是准确的!)。但由于现在是一个差异过程,CPU会将这些虚拟地址转换为不同的物理内存地址。至少,这是语义。在真实的现代系统中,确切的内存副本不会真正发生 - 或fork()将花费太长时间。相反,内存被标记为“写时复制”。这意味着直到数据被修改,两个进程将访问相同的物理内存。但是一旦任何进程修改了内存,它就会被复制,现在每个人都有自己的副本。