大內高手 記憶體模型

2021-04-09 04:33:10 字數 3831 閱讀 7822

了解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)

執行後,輸出結果為:

main=0x8048404 print=0x8048324

first=0xbfcd1264

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

main和print兩個函式是**段(.text)的,其位址符合表一的描述。 l

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

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

但p1和p2也是在堆中分配的,而其位址竟大於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

void

* thread_proc

(void

* param)

#define n5

int

main

(int

argc

, char

* argv)

;

printf

("first=%p/n"

, &first);

for(

i= 0;

i< n;

i++)

for(

i= 0;

i< n;

i++)

return0;

}

執行後,輸出結果為:

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錯誤,或者出現莫名其妙的錯誤。

大內高手 記憶體模型

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

大內高手 記憶體模型

大內高手 記憶體模型 作者 李先靜 2007 7 9 了解 linux 的記憶體模型,或許不能讓你大幅度提高程式設計能力,但是作為乙個基本知識點應該熟悉。坐火車外出旅行時,即時你對沿途的地方一無所知,仍然可以到達目標地。但是你對整個路途都很比較清楚的話,每到乙個站都知道自己在 知道當地的風土人情,對...

大內高手 記憶體模型

大內高手 記憶體模型 作者 李先靜 了解linux的記憶體模型,或許不能讓你大幅度提高程式設計能力,但是作為乙個基本知識點應該熟悉。坐火車外出旅行時,即時你對沿途的地方一無所知,仍然可以到達目標地。但是你對整個路途都很比較清楚的話,每到乙個站都知道自己在 知道當地的風土人情,對比一下所見所想,旅程可...