深入理解C程序内存布局
生活随笔
收集整理的這篇文章主要介紹了
深入理解C程序内存布局
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、堆和棧的區(qū)別,堆和棧的最大限制
??? 堆主要用來(lái)分配動(dòng)態(tài)內(nèi)存,操作系統(tǒng)提供了malloc等內(nèi)存分配機(jī)制來(lái)供程序員進(jìn)行堆內(nèi)存的分配,同時(shí),堆內(nèi)存的釋放需要程序員來(lái)進(jìn)行。malloc分配的是虛擬地址空間,和用到的實(shí)實(shí)在在的物理內(nèi)存是兩碼事,只有真正往空間里寫東西了,os內(nèi)核會(huì)觸發(fā)缺頁(yè)異常,然后才真正得到物理內(nèi)存。32位Linux系統(tǒng)總共有4G內(nèi)存空間,Linux把最高的1G(0xC0000000-0xFFFFFFFF)作為內(nèi)核空間,把低地址的3G(0x00000000-0xBFFFFFFF)作為用戶空間。malloc函數(shù)在這3G的用戶空間中分配內(nèi)存,能分配到的大小不超過(guò)3G,需除去棧、數(shù)據(jù)段(初始化及未初始化的)、共享so及代碼段等占的內(nèi)存空間。 堆的地址空間是由低向高增長(zhǎng)的 (先分配低地址)。我用以下程序進(jìn)行測(cè)試:
#include?<stdio.h>?
#include?<stdlib.h>?
int?main(int?argc,?char?*argv[])?
{?
???? char?*ch?=?NULL;?
???? unsigned?int?size?=?2147?*?1000000;?
???? ch?=?(char?*)malloc(size);?
?????if(ch?==?NULL)?
???????? perror("malloc failed\n");?
?????else?
???????? printf("malloc success\n");?
???? free(ch);?
???? return 0;?
}
發(fā)現(xiàn)最大能分配的內(nèi)存約為2.147GB。?
為什么這么說(shuō): 我們可以看malloc函數(shù)的原型void *malloc(size_t size);?size_t 在stddef.h里定義的是unsigned int類型,故在ilp32平臺(tái)上其最大取值是2147483647
?? 棧由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。棧的地址空間是由高向低減少的(先分配高地址)。在Linux中,用ulimit -a命令可以看到棧的最大分配空間(stack size)是8192kB,即8MB多。
?2、Linux中進(jìn)程最大地址空間
Linux的虛擬地址空間也為0~4G。Linux內(nèi)核將虛擬的4G字節(jié)的空間分為兩部分。
將最高的1G字節(jié)(從虛擬地址0xC0000000到0xFFFFFFFF),供內(nèi)核使用,稱為"內(nèi)核空間"。將較低的3G字節(jié)(從虛擬地址0x00000000到0xBFFFFFFF),供各個(gè)進(jìn)程使用,稱為"用戶空間"。因?yàn)槊總€(gè)進(jìn)程可以通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核,因此,Linux內(nèi)核由系統(tǒng)的所有進(jìn)程共享。于是,從具體進(jìn)程的角度來(lái)看,每個(gè)進(jìn)程可以擁有4G字節(jié)的虛擬空間。
其中,很重要的一點(diǎn)是虛擬地址空間,并不是實(shí)際的地址空間。進(jìn)程地址空間是用多少分配多少,4G僅僅是最大限額而已。往往,一個(gè)進(jìn)程的地址空間總是小于4G的,你可以通過(guò)查看/proc/pid/maps文件來(lái)獲悉某個(gè)具體進(jìn)程的地址空間。但進(jìn)程的地址空間并不對(duì)應(yīng)實(shí)際的物理頁(yè),Linux采用Lazy的機(jī)制來(lái)分配實(shí)際的物理頁(yè)("Demand paging"和"和寫時(shí)復(fù)制(Copy On Write)的技術(shù)"),從而提高實(shí)際內(nèi)存的使用率。即每個(gè)虛擬內(nèi)存頁(yè)并不一定對(duì)應(yīng)于物理頁(yè)。虛擬頁(yè)和物理頁(yè)的對(duì)應(yīng)是通過(guò)映射的機(jī)制來(lái)實(shí)現(xiàn)的,即通過(guò)頁(yè)表進(jìn)行映射到實(shí)際的物理頁(yè)。因?yàn)槊總€(gè)進(jìn)程都有自己的頁(yè)表,因此可以保證不同進(jìn)程從上到下(地址從高到低)依次為棧(函數(shù)內(nèi)部局部變量),堆(動(dòng)態(tài)分配內(nèi)存) ,bss段(存未初始化的全局變量),數(shù)據(jù)段(存初始化的全局變量),文本段(存代碼)同虛擬地址可以映射到不同的物理頁(yè),從而為不同的進(jìn)程都可以同時(shí)擁有4G的虛擬地址空間提供了可能。
3、C/C++程序在Linux中的內(nèi)存布局
用以下程序進(jìn)行測(cè)試
#include?<stdio.h>?
#include?<stdlib.h>?
char bss_1[40];?
static double bss_2;?
int?data_1?=?13;?
static long data_2?=?2001;?
int?main(int argc, char *argv[])?
{?
????int?stack_1?=?3,?stack_2,?*heap_1,?*heap_2;?
????heap_1?=?malloc(sizeof(stack_1));?
????heap_2?=?malloc(sizeof(stack_1));?
????bss_1[5]?=?stack_1;?
????bss_2?=?2.0?*?data_1;?
????printf("stack segment: stack_1:%p, stack_2:%p\n",?&stack_1,?&stack_2);?
????printf("heap segment: heap_1:%p, heap_2:%p\n",?heap_1,?heap_2);?
????printf("bss segment: bss_1:%p, bss_2:%p\n",?bss_1,?&bss_2);?
????printf("data segment: data_1:%p, data_2:%p\n",?&data_1,?&data_2);?
????printf("the stack top is near %p\n",?&stack_1);?
????return 0;?
}
運(yùn)行結(jié)果:
stack segment: stack_1:0xbfab012c, stack_2:0xbfab0128
stack segment: stack_1:0xbfb9e2ec, stack_2:0xbfb9e2e8
heap segment: heap_1:0x8c56008, heap_2:0x8c56018
bss segment:? bss_1:0x804a040, bss_2:0x804a028
data segment: data_1:0x804a018, data_2:0x804a01c
the stack top is near 0xbfb9e2ec
由此可見(jiàn):從上到下(地址從高到低)依次為棧(函數(shù)內(nèi)部局部變量),動(dòng)態(tài)鏈接庫(kù),堆(動(dòng)態(tài)分配內(nèi)存),bss段(存未初始化的全局變量),數(shù)據(jù)段(存初始化的全局變量),文本段(存代碼)
4、在 linux 下,程序運(yùn)行時(shí)空間的分配情況: #cat /proc/self/maps ===============================================================================
00110000-00267000 r-xp 00000000 08:08 406311?/lib/libc-2.12.1.so
00267000-00269000 r--p 00157000 08:08 406311?/lib/libc-2.12.1.so
00269000-0026a000 rw-p 00159000 08:08 406311?/lib/libc-2.12.1.so
0026a000-0026d000 rw-p 00000000 00:00 0?
0038f000-00390000 r-xp 00000000 00:00 0?[vdso]
0040e000-0042a000 r-xp 00000000 08:08 402927?/lib/ld-2.12.1.so
0042a000-0042b000 r--p 0001b000 08:08 402927?/lib/ld-2.12.1.so
0042b000-0042c000 rw-p 0001c000 08:08 402927?/lib/ld-2.12.1.so
08048000-08051000 r-xp 00000000 08:08 390934?/bin/cat
08051000-08052000 r--p 00008000 08:08 390934?/bin/cat
08052000-08053000 rw-p 00009000 08:08 390934?/bin/cat
09058000-09079000 rw-p 00000000 00:00 0?[heap]
b740f000-b75a2000 r--p 002a3000 08:08 130931?/usr/lib/locale/locale-archive
b75a2000-b77a2000 r--p 00000000 08:08 130931?/usr/lib/locale/locale-archive
b77a2000-b77a3000 rw-p 00000000 00:00 0?
b77b5000-b77b7000 rw-p 00000000 00:00 0?
bf987000-bf9a8000 rw-p 00000000 00:00 0?[stack]
=============================================================================== 解釋:
每個(gè)區(qū)間都有四個(gè)屬性: r 表示可以讀取。 w 表示可以修改。 x 表示可以執(zhí)行。 p/s 表示是否為共享內(nèi)存。 有文件名的內(nèi)存區(qū)間,屬性為 r—p 表示存放的是 rodata。 有文件名的內(nèi)存區(qū)間,屬性為 rw-p 表示存放的是 bss 和 data 有文件名的內(nèi)存區(qū)間,屬性為 r-xp 表示存放的是 text 數(shù)據(jù)。 沒(méi)有文件名的內(nèi)存區(qū)間,表示用 mmap 映射的匿名空間。 文件名為[stack]的內(nèi)存區(qū)間表示是棧。 文件名為[heap]的內(nèi)存區(qū)間表示是堆。
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
??? 堆主要用來(lái)分配動(dòng)態(tài)內(nèi)存,操作系統(tǒng)提供了malloc等內(nèi)存分配機(jī)制來(lái)供程序員進(jìn)行堆內(nèi)存的分配,同時(shí),堆內(nèi)存的釋放需要程序員來(lái)進(jìn)行。malloc分配的是虛擬地址空間,和用到的實(shí)實(shí)在在的物理內(nèi)存是兩碼事,只有真正往空間里寫東西了,os內(nèi)核會(huì)觸發(fā)缺頁(yè)異常,然后才真正得到物理內(nèi)存。32位Linux系統(tǒng)總共有4G內(nèi)存空間,Linux把最高的1G(0xC0000000-0xFFFFFFFF)作為內(nèi)核空間,把低地址的3G(0x00000000-0xBFFFFFFF)作為用戶空間。malloc函數(shù)在這3G的用戶空間中分配內(nèi)存,能分配到的大小不超過(guò)3G,需除去棧、數(shù)據(jù)段(初始化及未初始化的)、共享so及代碼段等占的內(nèi)存空間。 堆的地址空間是由低向高增長(zhǎng)的 (先分配低地址)。我用以下程序進(jìn)行測(cè)試:
點(diǎn)擊(此處)折疊或打開(kāi)
為什么這么說(shuō): 我們可以看malloc函數(shù)的原型void *malloc(size_t size);?size_t 在stddef.h里定義的是unsigned int類型,故在ilp32平臺(tái)上其最大取值是2147483647
?? 棧由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。棧的地址空間是由高向低減少的(先分配高地址)。在Linux中,用ulimit -a命令可以看到棧的最大分配空間(stack size)是8192kB,即8MB多。
?2、Linux中進(jìn)程最大地址空間
Linux的虛擬地址空間也為0~4G。Linux內(nèi)核將虛擬的4G字節(jié)的空間分為兩部分。
將最高的1G字節(jié)(從虛擬地址0xC0000000到0xFFFFFFFF),供內(nèi)核使用,稱為"內(nèi)核空間"。將較低的3G字節(jié)(從虛擬地址0x00000000到0xBFFFFFFF),供各個(gè)進(jìn)程使用,稱為"用戶空間"。因?yàn)槊總€(gè)進(jìn)程可以通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核,因此,Linux內(nèi)核由系統(tǒng)的所有進(jìn)程共享。于是,從具體進(jìn)程的角度來(lái)看,每個(gè)進(jìn)程可以擁有4G字節(jié)的虛擬空間。
其中,很重要的一點(diǎn)是虛擬地址空間,并不是實(shí)際的地址空間。進(jìn)程地址空間是用多少分配多少,4G僅僅是最大限額而已。往往,一個(gè)進(jìn)程的地址空間總是小于4G的,你可以通過(guò)查看/proc/pid/maps文件來(lái)獲悉某個(gè)具體進(jìn)程的地址空間。但進(jìn)程的地址空間并不對(duì)應(yīng)實(shí)際的物理頁(yè),Linux采用Lazy的機(jī)制來(lái)分配實(shí)際的物理頁(yè)("Demand paging"和"和寫時(shí)復(fù)制(Copy On Write)的技術(shù)"),從而提高實(shí)際內(nèi)存的使用率。即每個(gè)虛擬內(nèi)存頁(yè)并不一定對(duì)應(yīng)于物理頁(yè)。虛擬頁(yè)和物理頁(yè)的對(duì)應(yīng)是通過(guò)映射的機(jī)制來(lái)實(shí)現(xiàn)的,即通過(guò)頁(yè)表進(jìn)行映射到實(shí)際的物理頁(yè)。因?yàn)槊總€(gè)進(jìn)程都有自己的頁(yè)表,因此可以保證不同進(jìn)程從上到下(地址從高到低)依次為棧(函數(shù)內(nèi)部局部變量),堆(動(dòng)態(tài)分配內(nèi)存) ,bss段(存未初始化的全局變量),數(shù)據(jù)段(存初始化的全局變量),文本段(存代碼)同虛擬地址可以映射到不同的物理頁(yè),從而為不同的進(jìn)程都可以同時(shí)擁有4G的虛擬地址空間提供了可能。
3、C/C++程序在Linux中的內(nèi)存布局
用以下程序進(jìn)行測(cè)試
點(diǎn)擊(此處)折疊或打開(kāi)
stack segment: stack_1:0xbfab012c, stack_2:0xbfab0128
stack segment: stack_1:0xbfb9e2ec, stack_2:0xbfb9e2e8
heap segment: heap_1:0x8c56008, heap_2:0x8c56018
bss segment:? bss_1:0x804a040, bss_2:0x804a028
data segment: data_1:0x804a018, data_2:0x804a01c
the stack top is near 0xbfb9e2ec
由此可見(jiàn):從上到下(地址從高到低)依次為棧(函數(shù)內(nèi)部局部變量),動(dòng)態(tài)鏈接庫(kù),堆(動(dòng)態(tài)分配內(nèi)存),bss段(存未初始化的全局變量),數(shù)據(jù)段(存初始化的全局變量),文本段(存代碼)
4、在 linux 下,程序運(yùn)行時(shí)空間的分配情況: #cat /proc/self/maps
點(diǎn)擊(此處)折疊或打開(kāi)
總結(jié)
以上是生活随笔為你收集整理的深入理解C程序内存布局的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++程序的内存布局
- 下一篇: 个简单C++程序反汇编解析 (Rev.