2011-08-31 63 views
8

這裏是我的代碼爲什麼子進程和父進程的變量的地址是相同

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 

我的問題是,爲什麼是地址子和父變量相同但值不同?

回答

21

因爲這是一個虛擬地址,而不是物理地址。每個進程都有自己的地址空間(例如,一個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'打印格式化程序也添加到基地址中,否則我想:-) –

1

地址是'相同的',因爲每個進程都有自己的虛擬地址空間,變量通常會被加載到相同的位置。請注意,這不是內存中的物理地址。還要注意,有些方案故意隨機加載進程的位置,以便更難以攻擊/破解進程。在這種情況下,地址將會不同。

相關問題