为什么子进程和父进程的变量的地址是相同

问题描述:

这里是我的代码为什么子进程和父进程的变量的地址是相同

int main() 
{ 
    pid_t pid; 
    int y = 3; 
    if ((pid = fork()) <0) 
    return -1;; 

    if(pid == 0) /* child */ 
    { 
    printf(" before: %d %p\n", y, &y); 
    y *= 10; 
    printf("after: %d %p\n", y, &y); 
    } 
    else /* father */ 
    { 
    sleep(1); 
    printf("father: %d %p\n" , y , &y); 

    } 
    return 0; 
} 

程序的输出就像下面:

before: 3 ffbff440 
after: 30 ffbff440 
father: 3 ffbff440 

我的问题是,为什么是地址子和父变量相同但值不同?

因为这是一个虚拟地址,而不是物理地址。每个进程都有自己的地址空间(例如,一个32位系统可能允许每个进程拥有自己的地址空间,并拥有完整的4G范围)。

这是将虚拟地址映射到物理地址的内存管理单元(如果换出的页面需要从辅助存储中购回,则处理页面错误等问题)。

下图可以帮助,每个部分代表的存储器中的4K块:

Process A   Physical Memory  Process B 
    +-------+   +-------------+  +-------+ 
0K |  |----> 0K | (shared) | <----|  | 0K 
    +-------+   +-------------+  +-------+ 
4K |  |--+  4K |    | <----|  | 4K 
    +-------+ |  +-------------+  +-------+ 
8K |  | +-> 8K |    |  |  | 8K 
    +-------+   +-------------+  +-------+ 
     |    : : : : : : :   | 
     |    +-------------+   | 
     |   128K |    | <--------+ 
     |    +-------------+ 
     +--------> 132K |    | 
         +-------------+ 

可以看到,该图中,虚拟存储器地址和物理存储器地址(以及工艺的可能性之间的脱节以共享内存块)。左下方的地址是进程所看到的虚拟地址。

*块中的地址是实际的物理地址,其中数据“真”是,而MMU处理映射。

有关fork(和exec)的更深入说明,您可能还需要查看this answer

+0

另请注意,它必须这样。如果'y'的地址确实发生了变化,那么在fork之前的变量或结构中保存的任何指针(地址)将不再正确。这会使得fork变得更加有用,因为子进程将无法访问任何数据结构(链接列表,树)或以前动态分配的数据,因为这些依赖于指针。没有虚拟内存的系统几乎不能实现fork,您可以改为使用vfork(http://pubs.opengroup.org/onlinepubs/7908799/xsh/vfork.html)。 –

+0

从技术上讲,“必须这样” - 如果你的C运行时支持,你可以使用双向间接指针(换句话说,指针是一个已知值的偏移量,当分叉时,你将数据移动到其他地方孩子并调整已知的值)。这将是一个性能杀手。我认为实模式Windows通过锁定和解锁内存做了类似的事情。 – paxdiablo

+1

不够公平,你已经基本上描述了一种廉价而快乐的(或昂贵而悲惨的,取决于你如何看待它)的虚拟内存的软件实现。在这种情况下,您仍然可以看到提问者观察到的情况,即分叉前后的地址打印为相同的值。除非'%p'打印格式化程序也添加到基地址中,否则我想:-) –

地址是'相同的',因为每个进程都有自己的虚拟地址空间,变量通常会被加载到相同的位置。请注意,这不是内存中的物理地址。还要注意,有些方案故意随机加载进程的位置,以便更难以攻击/破解进程。在这种情况下,地址将会不同。