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

Why fork() use the same variable but different value?

本文关键字:变量 fork 为什么      更新时间:2023-10-16

代码:

#include <stdio.h>                                                                                                                                                            
#include <unistd.h>
void f(int&);
void g(int&);
int main(int argc, char **argv)
{
    printf("--beginning of programn");
    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, %pn", 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--

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

为什么会这样?

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

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

这不是同一个对象;它是新工艺中的一个对象。由于第二个进程是从第一个进程派生出来的,本质上是克隆了这个进程,所以对象在第二个进程中应该存在于与第一个进程相同的虚拟内存位置也就不足为奇了。

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

但它不再是。现在在用户程序中,地址与物理内存无关,每当您访问位于位置'0x1234567'的内存时,都会进行转换。CPU知道如何将这个虚拟地址映射到物理内存地址,但是没有其他人知道。

因此,当您fork进程时,会生成一个精确的内存副本。它具有相同的虚拟地址(因为内存副本是精确的!)。但是由于现在是一个不同的进程,CPU会将这些虚拟地址转换为不同的物理内存地址。至少,这是语义。在真正的现代系统中,确切的内存副本不会真正发生—或者fork()需要很长时间。相反,内存被标记为"写时复制"。这意味着在数据被修改之前,两个进程将访问相同的物理内存。但是一旦任何进程修改了内存,它就会被复制,现在每个进程都有自己的副本。