1. 程式人生 > >內存區域的劃分

內存區域的劃分

分享圖片 malloc str fontsize 進程 不一定 vma 內核啟動 kernel

本文主要講解應用層(c/c++內存劃分)、linux內核層(X86體系和ARM系統)關於內存上面的劃分相關知識點。
一、應用層
1. 在c中分為這幾個存儲區:堆、棧、全局區(靜態區)、常量區
(1).棧 - 由編譯器自動分配釋放。 棧又稱堆棧, 是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味著在數據段中存放變量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。
(2).堆 - 一般由程序員分配釋放,若程序員不釋放,程序結束時可能由操作系統回收。 堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
(3).全局區(靜態區),全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。- 程序結束釋放
(4).另外還有一個專門放常量的地方,叫做常量區,是存在於代碼段的。- 程序結束釋放
技術分享圖片

從上面的內存模型圖中我們可以看出好像和我們上面所說的有出入,其實不然,我們來時,堆和棧區是沒有問題的,下面就是全局區(靜態區),上面說了未初始化的全局變量和靜態變量是在一起的,這個區域叫做BSS段, 初始化的全局變量和靜態變量在一塊區域 ,是存在於數據段中的,上面BSS段和數據段我們又可以稱為數據區,最後就是常量區,它其實是存放在代碼段的(只讀,在程序裝載的時候就已經裝載到代碼段),因此從上面模型又可以把c語言分為以下幾個儲存區:堆、棧、數據區、代碼段(個人覺得這樣劃分合理一點)。
BSS段:BSS段(bss segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。一般在初始化時bss 段部分將會清零。bss段屬於靜態內存分配,即程序一開始就將其清零了。

數據段:數據段(data segment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域。數據段屬於靜態內存分配。

代碼段:代碼段(code segment/text segment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀, 某些架構也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。代碼段是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程序,那麽它們就可以使用同一個代碼段。

在函數體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內存的函數分配得到的就是在堆上。在所有函數體外定義的是全局量,加了static修飾符後不管在哪裏都存放在全局區(靜態區),在所有函數體外定義的static變量表示在該文件中有效,不能extern到別的文件用,在函數體內定義的static表示只在該函數體內有效。另外,函數中的”adgfdf”這樣的字符串存放在常量區。比如:
int a = 0; //全局初始化區
char *p1; //全局未初始化區
void main()
{
int b; //棧
char s[] = “abc”; //棧
char *p2; //棧
char *p3 = “123456”; //123456{post.content}在常量區,p3在棧上
static int c = 0; //全局(靜態)初始化區
p1 = (char *)malloc(10); //分配得來得10字節的區域在堆區
p2 = (char *)malloc(20); //分配得來得20字節的區域在堆區
strcpy(p1, “123456”);
//123456{post.content}放在常量區,編譯器可能會將它與p3所指向的”123456”優化成一塊
}

BSS段的應用:
用cl編譯兩個小程序如下:

程序1:
int ar[30000];
void main()
{
……
}

程序2:
int ar[300000] = {1, 2, 3, 4, 5, 6 };
void main()
{
……
}

發現程序2編譯之後所得的.exe文件比程序1的要大得多。原因就是,一個位於.bss段,而另一個位於.data段,兩者的區別在於:全局的未初始化變量存在於.bss段中,具體體現為一個占位符;全局的已初始化變量存於.data段中;而函數內的自動變量都在棧上分配空間。.bss是不占用.exe文件空間的,其內容由操作系統初始化(清零);而.data卻需要占用,其內容由程序初始化,因此造成了上述情況。

由此最終說明了:
1.bss段(未手動初始化的數據)並不給該段的數據分配空間,只是記錄數據所需空間的大小。
2.data(已手動初始化的數據)段則為數據分配空間,數據保存在目標文件中。
3.text和data段都在可執行文件中(在嵌入式系統裏一般是固化在鏡像文件中),由系統從可執行文件中加載;而bss段不在可執行文件中,由系統初始化。

2.在C++中,內存分成5個區,他們分別是堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區
(1).棧,就是那些由編譯器在需要的時候分配,在不需要的時候自動清楚的變量的存儲區。裏面的變量通常是局部變量、函數參數等。
(2).自由存儲區,就是那些由new分配的內存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個new就要對應一個delete。如果程序員沒有釋放掉,那麽在程序結束後,操作系統會自動回收。

C++ 自由存儲區是否等價於堆?
(3).堆,就是那些由malloc等分配的內存塊,他和堆是十分相似的,不過它是用free來結束自己的生命的。
(4).全局/靜態存儲區,全局變量和靜態變量被分配到同一塊內存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++裏面沒有這個區分了,他們共同占用同一塊內存區。
(5).常量存儲區,這是一塊比較特殊的存儲區,他們裏面存放的是常量,不允許修改(當然,你要通過非正當手段也可以修改)
堆於自由儲存的區別:
自由存儲是C++中通過new與delete動態分配和釋放對象的抽象概念,而堆(heap)是C語言和操作系統的術語,是操作系統維護的一塊動態分配內存。
new所申請的內存區域在C++中稱為自由存儲區。如果由堆實現的自由存儲,可以說new所申請的內存區域在堆上。
堆與自由存儲區還是有區別的,它們並非等價,因為自由儲存的內存分配不一定是從堆上分配得來。
new/delete實現是運算符,而malloc/free的實現是函數庫,明顯不同等同來說。
具體解析:http://blog.jobbole.com/109562/

分析:堆和棧的區別。

二、內核層內存分配。
1.x86:將1g的內核虛擬內存地址分別劃分為4個區域:直接內存映射區 動態內存映射區 永久內存映射區 固定內存映射區
直接內存映射區 映射關系:在內核初始化(不是動態映射),如果物理內存大於896MB(1g),內核將內核的1g虛擬地址前896mb虛擬內存和物理進行一一映射;1g的虛擬內存還剩余128m,
起始地址:0xC000000大小如果物理內存大於1g,那麽直接內存區的大小為896MB,如果物理內存小於896MB,那麽物理內存的大小就是直接內存映射區的大小 別名:低端內存
動態內存映射區 映射關系:如果需要某一個物理地址(物理內存地址 寄存器地址),,只需動態的建立物理地址和動態內存映射區的映射關系(頁表),即可,每當程序訪問這個內核的動態內存映射的虛擬地址其實最終也是訪問對應的物理地址(mmu);如果不在使用對應的物理地址,一定要解除映射關系。
起始地址:隨著物理內存的大小變化而變化,如果物理內存小於896MB,比如512M,起始地址=0xc000000+物理內存的大小,如果物理內存大於896MB,起始地址0xC0000000+896MB,默認的大小為12MB。
永久內存映射區:映射關系:它也是實現物理地址和內核虛擬地址的動態映射,只是如果在某些時刻,訪問這個物理地址的頻率很大,如果頻繁的建立頁表,那麽訪問效率不高,於是可以將這個物理地址映射到永久內存映射區,一經建立映射關系,無需再銷毀對應的映射關系,加快地址的訪問速度。當然可以人為的銷毀這個映射關系。使用kmap函數進行映射,這個映射有可能會休眠,所以不能再中斷上下文使用。大小:4M
固定內存映射區:和永久內存映射區的目的是一樣的,區別僅僅是固定內存映射區的虛擬地址映射的時候,可以使用在中斷的上下文中,禁止內核搶占。
用戶虛擬地址劃分:棧 mmap映射區 堆 bbs 數據段 代碼段
註意:直接內存映射區又叫低端內存:
動態內存映射區+永久內存映射區+固定內存映射區=高端內存;

2.ARM體系:1G的內核虛擬內存區域的劃分:5個區域。異常向量表(vetor)、固定內存映射區、DMA內存映射區、動態內存映射區、直接內存映射區。 註意:arm架構本身的異常向量表的入口地址0xffff0000–0xffff1000和0地址處。3g的用戶空間與物理內存訪問需要在內核的動態映射區訪問。
S5PV210處理器1G內核虛擬地址的劃分:Linux內核啟動打印的內核信息:
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB) //異常向量表入口地址
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) //固定內存映射區
DMA : 0xff000000 - 0xffe00000 ( 14 MB) //dma內存映射
vmalloc : 0xf4800000 - 0xfc000000 (120 MB) / /動態內存映射區
lowmem : 0xc0000000 - 0xf4000000 ( 832 MB) //低端內存(直接內存映射區)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.init : 0xc0008000 - 0xc003a000 ( 200 kB)
.text : 0xc003a000 - 0xc09ad000 (9676 kB)
.data : 0xc09ae000 - 0xc0a15d20 ( 416 kB)
內核啟動的時候將會把內核的1g內存映射進行初始化,頁表初始化放在內存中。
虛擬地址 = (物理地址–物理地址的起始地址) + 0xC0000000 ;

--------------------- 本文來自 嵌入式小青年 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/a1299600490/article/details/77119939?utm_source=copy

內存區域的劃分