Linux內存模型

內存模型

 

瞭解linux的內存模型,或許不能讓你大幅度提高編程能力,但是作爲一個基本知識點應該熟悉。坐火車外出旅行時,即時你對沿途的地方一無所知,仍然可以到達目標地。但是你對整個路途都很比較清楚的話,每到一個站都知道自己在哪裏,知道當地的風土人情,對比一下所見所想,旅程可能更有趣一些。

 

類似的,瞭解linux的內存模型,你知道每塊內存,每個變量,在系統中處於什麼樣的位置。這同樣會讓你心情愉快,知道這些,有時還會讓你的生活輕更鬆些。看看變量的地址,你可以大致斷定這是否是一個有效的地址。一個變量被破壞了,你可以大致推斷誰是犯罪嫌疑人。

 

Linux的內存模型,一般爲:

地址

作用

說明

>=0xc000 0000

內核虛擬存儲器

用戶代碼不可見區域

<0xc000 0000

Stack(用戶棧)

ESP指向棧頂

 

 

 

空閒內存

>=0x4000 0000

文件映射區

 

<0x4000 0000

 

 

 

空閒內存

 

 

Heap(運行時堆)

通過brk/sbrk系統調用擴大堆,向上增長。

 

.data.bss(讀寫段)

從可執行文件中加載

>=0x0804 8000

.init.text.rodata(只讀段)

從可執行文件中加載

<0x0804 8000

保留區域

 

 

很多書上都有類似的描述,本圖取自於《深入理解計算機系統》p603,略做修改。本圖比較清析,很容易理解,但仍然有兩點不足。下面補充說明一下:

 

1.         第一點是關於運行時堆的

爲說明這個問題,我們先運行一個測試程序,並觀察其結果:

#include <stdio.h>

 

int main(int argc, char* argv[])

{

    int  first = 0;

    int* p0 = malloc(1024);

    int* p1 = malloc(1024 * 1024);

    int* p2 = malloc(512 * 1024 * 1024 );

    int* p3 = malloc(1024 * 1024 * 1024 );

    printf("main=%p print=%p/n", main, printf);

    printf("first=%p/n", &first);

    printf("p0=%p p1=%p p2=%p p3=%p/n", p0, p1, p2, p3);

 

    getchar();

 

    return 0;

}

 

運行後,輸出結果爲:

main=0x8048404 print=0x8048324

first=0xbfcd1264

p0=0x9253008 p1=0xb7ec0008 p2=0x97ebf008 p3=0x57ebe008

 

l         mainprint兩個函數是代碼段(.text)的,其地址符合表一的描述。

l         first是第一個臨時變量,由於在first之前還有一些環境變量,它的值並非0xbfffffff,而是0xbfcd1264,這是正常的。

l         p0是在堆中分配的,其地址小於0x4000 0000,這也是正常的。

l         p1p2也是在堆中分配的,而其地址竟大於0x4000 0000,與表一描述不符。

 

原因在於:運行時堆的位置與內存管理算法相關,也就是與malloc的實現相關。關於內存管理算法的問題,我們在後繼文章中有詳細描述,這裏只作簡要說明。在glibc實現的內存管理算法中,Malloc小塊內存是在小於0x4000 0000的內存中分配的,通過brk/sbrk不斷向上擴展,而分配大塊內存,malloc直接通過系統調用mmap實現,分配得到的地址在文件映射區,所以其地址大於0x4000 0000

 

maps文件中可以清楚的看到一點:

00514000-00515000 r-xp 00514000 00:00 0

00624000-0063e000 r-xp 00000000 03:01 718192     /lib/ld-2.3.5.so

0063e000-0063f000 r-xp 00019000 03:01 718192     /lib/ld-2.3.5.so

0063f000-00640000 rwxp 0001a000 03:01 718192     /lib/ld-2.3.5.so

00642000-00766000 r-xp 00000000 03:01 718193     /lib/libc-2.3.5.so

00766000-00768000 r-xp 00124000 03:01 718193     /lib/libc-2.3.5.so

00768000-0076a000 rwxp 00126000 03:01 718193     /lib/libc-2.3.5.so

0076a000-0076c000 rwxp 0076a000 00:00 0

08048000-08049000 r-xp 00000000 03:01 1307138    /root/test/mem/t.exe

08049000-0804a000 rw-p 00000000 03:01 1307138    /root/test/mem/t.exe

09f5d000-09f7e000 rw-p 09f5d000 00:00 0          [heap]

57e2f000-b7f35000 rw-p 57e2f000 00:00 0

b7f44000-b7f45000 rw-p b7f44000 00:00 0

bfb2f000-bfb45000 rw-p bfb2f000 00:00 0          [stack]

 

2.         第二是關於多線程的。

現在的應用程序,多線程的居多。表一所描述的模型無法適用於多線程環境。按表一所述,程序最多擁有上G的棧空間,事實上,在多線程情況下,能用的棧空間是非常有限的。爲了說明這個問題,我們再看另外一個測試:

#include <stdio.h>

#include <pthread.h>

 

 

void* thread_proc(void* param)

{

    int  first = 0;

    int* p0 = malloc(1024);

    int* p1 = malloc(1024 * 1024);

 

    printf("(0x%x): first=%p/n",    pthread_self(), &first);

    printf("(0x%x): p0=%p p1=%p /n", pthread_self(), p0, p1);

 

    return 0;

}

 

#define N 5

int main(int argc, char* argv[])

{

    int first = 0;

    int i= 0;

    void* ret = NULL;

    pthread_t tid[N] = {0};

 

    printf("first=%p/n", &first);

    for(i = 0; i < N; i++)

    {

        pthread_create(tid+i, NULL, thread_proc, NULL);

    }

 

    for(i = 0; i < N; i++)

    {

        pthread_join(tid[i], &ret);

    }

 

    return 0;

}

 

運行後,輸出結果爲:

first=0xbfd3d35c

(0xb7f2cbb0): first=0xb7f2c454

(0xb7f2cbb0): p0=0x84d52d8 p1=0xb4c27008

(0xb752bbb0): first=0xb752b454

(0xb752bbb0): p0=0x84d56e0 p1=0xb4b26008

(0xb6b2abb0): first=0xb6b2a454

(0xb6b2abb0): p0=0x84d5ae8 p1=0xb4a25008

(0xb6129bb0): first=0xb6129454

(0xb6129bb0): p0=0x84d5ef0 p1=0xb4924008

(0xb5728bb0): first=0xb5728454

(0xb5728bb0): p0=0x84d62f8 p1=0xb7e2c008

 

我們看一下:

主線程與第一個線程的堆之間的距離:0xbfd3d35c - 0xb7f2c454=0x7e10f08=126M

第一個線程與第二個線程的堆之間的距離:0xb7f2c454 - 0xb752b454=0xa01000=10M

其它幾個線程間的距離均爲10M

也就是說,主線程的堆空間最大爲126M,而普通線程的棧空間僅爲10M,超這個範圍就會造成棧溢出。

 

棧溢出的後果是比較嚴重的,或者出現Segmentation fault錯誤,或者出現莫名其妙的錯誤。

轉自:http://dev.csdn.net/author/absurd/6360b845596c434ca9246c7fead47830.html

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章