1. 程式人生 > >父子程序變數的地址一樣,但值不一樣

父子程序變數的地址一樣,但值不一樣

執行這一段C程式碼, 然後看一下執行結果
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

main(){
    char *str="asd";
    pid_t pid=fork();
    if(pid==0){
        str="olm";
        printf("子程序中str=%s\n",str);
        printf("子程序中str的地址:%x\n",&str);
        printf("子程序中str指向的首地址:%x\n",(int)str);
    }
    else{
        sleep(1);
        printf("父程序中str=%s\n",str);
        printf("父程序中str的地址:%x\n",&str);
        printf("父程序中str指向的首地址:%x\n",(int)str);
    }
}

輸出:

子程序中str=olm
子程序中str的地址:bfdc3f98
子程序中str指向的首地址:80485a4
父程序中str=asd
父程序中str的地址:bfdc3f98
父程序中str指向的首地址:80485a0

當然地址值有可能不一樣,但是之間的邏輯聯絡在不同的主機上肯定是一樣的!

有沒有發現, 父子程序的str地址是一樣的,但是值卻各異, 到底是誰在作怪!

這裡就涉及到實體地址和邏輯地址(或稱虛擬地址)的概念。

邏輯地址:CPU所生成的地址。CPU產生的邏輯地址被分為 :p (頁號) 它包含每個頁在實體記憶體中的基址,

用來作為頁表的索引;d (頁偏移),同基址相結合,用來確定送入記憶體裝置的實體記憶體地址。

實體地址:記憶體單元所看到的地址。

使用者程式看不見真正的實體地址。使用者只生成邏輯地址,且認為程序的地址空間為0到max。實體地址範圍從R+0到R+max

R為基地址,地址對映-將程式地址空間中使用的邏輯地址變換成記憶體中的實體地址的過程。由記憶體管理單元(MMU)來完成。

fork時子程序獲得父程序資料空間、堆和棧的複製,所以變數的地址(當然是虛擬地址)也是一樣的。

每個程序都有自己的虛擬地址空間,不同程序的相同的虛擬地址顯然可以對應不同的實體地址。因此地址相同(虛擬地址)而值不同沒什麼奇怪。

具體過程是這樣的:
fork子程序完全複製父程序的棧空間,也複製了頁表,但沒有複製物理頁面,所以這時虛擬地址相同,實體地址也相同,

但是會把父子共享的頁面標記為“只讀”(類似mmap的private的方式),如果父子程序一直對這個頁面是同一個頁面,

知道其中任何一個程序要對共享的頁面“寫操作”,這時核心會複製一個物理頁面給這個程序使用,同時修改頁表。

而把原來的只讀頁面標記為“可寫”,留給另外一個程序使用。

這就是所謂的“寫時複製”。正因為fork採用了這種寫時複製的機制,所以fork出來子程序之後,父子程序哪個先排程呢?

核心一般會先排程子程序,因為很多情況下子程序是要馬上執行exec,會清空棧、堆。。這些和父程序共享的空間,

載入新的程式碼段。。。,這就避免了“寫時複製”拷貝共享頁面的機會。如果父程序先排程很可能寫共享頁面,會產生“寫時複製”的無用功。

所以,一般是子程序先排程滴。

轉載地址 : http://www.kuqin.com/networkprog/20120110/317254.html