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