天天看點

linux程序記憶體映象解析

一、程式如何轉化為程序

程式轉化為程序一般有兩個步驟:

1、核心會将程式從磁盤讀入記憶體,為程式配置設定記憶體空間

2、核心會為程序儲存PID以及相應的狀态資訊(儲存在task_struct中),将程序放在運作隊列中等待執行。

程式轉變為程序以後就可以被作業系統排程程式執行了。

二、記憶體映象

記憶體映象指的是核心如何在記憶體中存放可執行程式。

在程式轉化為程序的過程中,作業系統可直接将可執行程式複制到記憶體中,其分布狀況如下:

linux程式記憶體映象解析

(1)棧的位址是由高位址向低位址生長的(這也就解釋了為什麼我們定義數組的時候要給定數組大小)

(2)堆的位址是有低位址向高位址生長的

三、虛拟位址與實體位址

結合子程序與父程序,我們可以将上圖畫的更詳細:

linux程式記憶體映象解析

1、位址空間(在作業系統中是現實存在的,由位址空間結構體支援):指的是虛拟位址上的一個位址範圍,按需要來擷取。舉個形象點的例子來說明:你活動的範圍是西安,但是最終你真正落腳的隻是交通大學419宿舍的2号床鋪。也就是說整個西安你都可以通路,但最終屬于你的隻是你現在坐的那塊固定大小的地方。

2、虛拟位址:每個程序有運作在一個屬于自己的位址空間裡面,這個位址空間就是虛拟位址(我們平時見到的也都是虛拟位址,但最終還是要映射到實體位址上去)。

3、MMU:記憶體管理單元(硬體),結合頁表可以完成映射。

上面我們說虛拟位址是每個程序特有的空間,并且我們見到的和用的都是虛拟位址,現在我們通過一段來驗證一下:

int g_val=;
int main()
{
    pid_t id=fork();
    if(id<)//fork error
    {
        printf("fork error\n");
        return ;
    }
    else if(id==)//child 
    {
        g_val=;
        printf("child ,pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
    }
    else
    {
        sleep();
        printf("father ,pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
    }
    return ;
           

輸出:

linux程式記憶體映象解析

在子程序中将全局變量g_val的值改為1000;

在父程序中顯示值為100,子程序中值為1000,并且兩者的位址是相同的。

結合上面的圖我們來解釋一下這個現象:我們說子程序和父程序共享代碼,但是資料并不是共享的,也就是說,他們的虛拟位址是一樣的,但是實體位址就肯定不一樣了。父程序根據自己的頁表和MMU映射到一個實體位址,子程序也映射到一個位址。子程序改一個值并不影響父程序的值,是以就出現了上面變量位址相同而值不同的情況。

PS:父子程序資料私有化的實作:并不是立刻實作,而是當有程序對某個區域的資料進行寫入的時候,實作對該程序空間的資料的私有化,私有化的實作技術是寫時拷貝。這樣做的目的是為了節省消耗,提高效率。

繼續閱讀