内存区划分;内存分配;堆、栈概念分析;动态内存管理数据结构及程序样例;核心态与用户态...
一. 在c中分為這幾個(gè)存儲(chǔ)區(qū)
1.棧 - 由編譯器自動(dòng)分配釋放
2.堆 - 一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時(shí)可能由OS回收
3.全局區(qū)(靜態(tài)區(qū)),全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。- 程序結(jié)束釋放
4.另外還有一個(gè)專門放常量的地方。- 程序結(jié)束釋放
?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
在函數(shù)體中定義的變量通常是在棧上,用malloc, calloc, realloc等分配內(nèi)存的函數(shù)分配得到的就是在堆上。在所有函數(shù)體外定義的是全局量,加了static修飾符后不管在哪里都存放在全局區(qū)(靜態(tài)區(qū)),在所有函數(shù)體外定義的static變量表示在該文件中有效,不能extern到別的文件用,在函數(shù)體內(nèi)定義的static表示只在該函數(shù)體內(nèi)有效。另外,函數(shù)中的"adgfdf"這樣的字符串存放在常量區(qū)。比如:
int a = 0; //全局初始化區(qū)
char *p1; //全局未初始化區(qū)
void main()
{
??? int b; //棧
??? char s[] = "abc"; //棧
??? char *p2; //棧
??? char *p3 = "123456"; //123456{post.content}在常量區(qū),p3在棧上
??? static int c = 0; //全局(靜態(tài))初始化區(qū)
??? p1 = (char *)malloc(10); //分配得來得10字節(jié)的區(qū)域在堆區(qū)
??? p2 = (char *)malloc(20); //分配得來得20字節(jié)的區(qū)域在堆區(qū)
??? strcpy(p1, "123456");
??? //123456{post.content}放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"123456"優(yōu)化成一塊
}
二.在C++中,內(nèi)存分成5個(gè)區(qū),他們分別是堆、棧、自由存儲(chǔ)區(qū)、全局/靜態(tài)存儲(chǔ)區(qū)和常量存儲(chǔ)區(qū)
1.棧,就是那些由編譯器在需要的時(shí)候分配,在不需要的時(shí)候自動(dòng)清楚的變量的存儲(chǔ)區(qū)。里面的變量通常是局部變量、函數(shù)參數(shù)等。
2.堆,就是那些由new分配的內(nèi)存塊,他們的釋放編譯器不去管,由我們的應(yīng)用程序去控制,一般一個(gè)new就要對(duì)應(yīng)一個(gè)delete。如果程序員沒有釋放掉,那么在程序結(jié)束后,操作系統(tǒng)會(huì)自動(dòng)回收。
3.自由存儲(chǔ)區(qū),就是那些由malloc等分配的內(nèi)存塊,他和堆是十分相似的,不過它是用free來結(jié)束自己的生命的。
4.全局/靜態(tài)存儲(chǔ)區(qū),全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中,在以前的C語言中,全局變量又分為初始化的和未初始化的,在C++里面沒有這個(gè)區(qū)分了,他們共同占用同一塊內(nèi)存區(qū)。
5.常量存儲(chǔ)區(qū),這是一塊比較特殊的存儲(chǔ)區(qū),他們里面存放的是常量,不允許修改(當(dāng)然,你要通過非正當(dāng)手段也可以修改)
三. 談?wù)劧雅c棧的關(guān)系與區(qū)別
具體地說,現(xiàn)代計(jì)算機(jī)(串行執(zhí)行機(jī)制),都直接在代碼底層支持棧的數(shù)據(jù)結(jié)構(gòu)。這體現(xiàn)在,有專門的寄存器指向棧所在的地址,有專門的機(jī)器指令完成數(shù)據(jù)入棧出棧的操作。這種機(jī)制的特點(diǎn)是效率高,支持的數(shù)據(jù)有限,一般是整數(shù),指針,浮點(diǎn)數(shù)等系統(tǒng)直接支持的數(shù)據(jù)類型,并不直接支持其他的數(shù)據(jù)結(jié)構(gòu)。因?yàn)闂5倪@種特點(diǎn),對(duì)棧的使用在程序中是非常頻繁的。對(duì)子程序的調(diào)用就是直接利用棧完成的。機(jī)器的call指令里隱含了把返回地址推入棧,然后跳轉(zhuǎn)至子程序地址的操作,而子程序中的ret指令則隱含從堆棧中彈出返回地址并跳轉(zhuǎn)之的操作。C/C++中的自動(dòng)變量是直接利用棧的例子,這也就是為什么當(dāng)函數(shù)返回時(shí),該函數(shù)的自動(dòng)變量自動(dòng)失效的原因。
和棧不同,堆的數(shù)據(jù)結(jié)構(gòu)并不是由系統(tǒng)(無論是機(jī)器系統(tǒng)還是操作系統(tǒng))支持的,而是由函數(shù)庫提供的。基本的malloc/realloc/free 函數(shù)維護(hù)了一套內(nèi)部的堆數(shù)據(jù)結(jié)構(gòu)。當(dāng)程序使用這些函數(shù)去獲得新的內(nèi)存空間時(shí),這套函數(shù)首先試圖從內(nèi)部堆中尋找可用的內(nèi)存空間,如果沒有可以使用的內(nèi)存空間,則試圖利用系統(tǒng)調(diào)用來動(dòng)態(tài)增加程序數(shù)據(jù)段的內(nèi)存大小,新分配得到的空間首先被組織進(jìn)內(nèi)部堆中去,然后再以適當(dāng)?shù)男问椒祷亟o調(diào)用者。當(dāng)程序釋放分配的內(nèi)存空間時(shí),這片內(nèi)存空間被返回內(nèi)部堆結(jié)構(gòu)中,可能會(huì)被適當(dāng)?shù)奶幚?比如和其他空閑空間合并成更大的空閑空間),以更適合下一次內(nèi)存分配申請(qǐng)。這套復(fù)雜的分配機(jī)制實(shí)際上相當(dāng)于一個(gè)內(nèi)存分配的緩沖池(Cache),使用這套機(jī)制有如下若干原因:
1. 系統(tǒng)調(diào)用可能不支持任意大小的內(nèi)存分配。有些系統(tǒng)的系統(tǒng)調(diào)用只支持固定大小及其倍數(shù)的內(nèi)存請(qǐng)求(按頁分配);這樣的話對(duì)于大量的小內(nèi)存分類來說會(huì)造成浪費(fèi)。
2. 系統(tǒng)調(diào)用申請(qǐng)內(nèi)存可能是代價(jià)昂貴的。系統(tǒng)調(diào)用可能涉及用戶態(tài)和核心態(tài)的轉(zhuǎn)換。
3. 沒有管理的內(nèi)存分配在大量復(fù)雜內(nèi)存的分配釋放操作下很容易造成內(nèi)存碎片。
堆和棧的對(duì)比
從以上知識(shí)可知,棧是系統(tǒng)提供的功能,特點(diǎn)是快速高效,缺點(diǎn)是有限制,數(shù)據(jù)不靈活;而棧是函數(shù)庫提供的功能,特點(diǎn)是靈活方便,數(shù)據(jù)適應(yīng)面廣泛,但是效率有一定降低。棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對(duì)于進(jìn)程/線程是唯一的;堆是函數(shù)庫內(nèi)部數(shù)據(jù)結(jié)構(gòu),不一定唯一。不同堆分配的內(nèi)存無法互相操作。棧空間分靜態(tài)分配和動(dòng)態(tài)分配兩種。靜態(tài)分配是編譯器完成的,比如自動(dòng)變量(auto)的分配。動(dòng)態(tài)分配由alloca函數(shù)完成。棧的動(dòng)態(tài)分配無需釋放(是自動(dòng)的),也就沒有釋放函數(shù)。為可移植的程序起見,棧的動(dòng)態(tài)分配操作是不被鼓勵(lì)的!堆空間的分配總是動(dòng)態(tài)的,雖然程序結(jié)束時(shí)所有的數(shù)據(jù)空間都會(huì)被釋放回系統(tǒng),但是精確的申請(qǐng)內(nèi)存/ 釋放內(nèi)存匹配是良好程序的基本要素。
??? 1.碎片問題:對(duì)于堆來講,頻繁的new/delete勢(shì)必會(huì)造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對(duì)于棧來講,則不會(huì)存在這個(gè)問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對(duì)應(yīng),以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以>參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。
??? 2.生長(zhǎng)方向:對(duì)于堆來講,生長(zhǎng)方向是向上的,也就是向著內(nèi)存地址增加的方向;對(duì)于棧來講,它的生長(zhǎng)方向是向下的,是向著內(nèi)存地址減小的方向增長(zhǎng)。
??? 3.分配方式:堆都是動(dòng)態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動(dòng)態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。
??? 4.分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫函數(shù)會(huì)按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會(huì)分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。
??? 明確區(qū)分堆與棧:
??? 在bbs上,堆與棧的區(qū)分問題,似乎是一個(gè)永恒的話題,由此可見,初學(xué)者對(duì)此往往是混淆不清的,所以我決定拿他第一個(gè)開刀。
??? 首先,我們舉一個(gè)例子:
void f()
{
??? int* p=new int[5];
}
這條短短的一句話就包含了堆與棧,看到new,我們首先就應(yīng)該想到,我們分配了一塊堆內(nèi)存,那么指針p呢?他分配的是一塊棧內(nèi)存,所以這句話的意思就是:在棧內(nèi)存中存放了一個(gè)指向一塊堆內(nèi)存的指針p。在程序會(huì)先確定在堆中分配內(nèi)存的大小,然后調(diào)用operator new分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧中,他在VC6下的匯編代碼如下:
??? 00401028??? push???????? 14h
??? 0040102A??? call??????????? operator new (00401060)
??? 0040102F??? add?????????? esp,4
??? 00401032??? mov????????? dword ptr [ebp-8],eax
??? 00401035??? mov????????? eax,dword ptr [ebp-8]
??? 00401038??? mov????????? dword ptr [ebp-4],eax
??? 這里,我們?yōu)榱撕?jiǎn)單并沒有釋放內(nèi)存,那么該怎么去釋放呢?是delete p么?澳,錯(cuò)了,應(yīng)該是delete []p,這是為了告訴編譯器:我刪除的是一個(gè)數(shù)組,VC6就會(huì)根據(jù)相應(yīng)的Cookie信息去進(jìn)行釋放內(nèi)存的工作。
??? 好了,我們回到我們的主題:堆和棧究竟有什么區(qū)別?
??? 主要的區(qū)別由以下幾點(diǎn):
??? 1、管理方式不同;
??? 2、空間大小不同;
??? 3、能否產(chǎn)生碎片不同;
??? 4、生長(zhǎng)方向不同;
??? 5、分配方式不同;
??? 6、分配效率不同;
??? 管理方式:對(duì)于棧來講,是由編譯器自動(dòng)管理,無需我們手工控制;對(duì)于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。
??? 空間大小:一般來講在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個(gè)角度來看堆內(nèi)存幾乎是沒有什么限制的。但是對(duì)于棧來講,一般都是有一定的空間大小的,例如,在VC6下面,默認(rèn)的棧空間大小是1M(好像是,記不清楚了)。當(dāng)然,我們可以修改:
??? 打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。
注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁文件里面,它設(shè)置的較大會(huì)使棧開辟較大的值,可能增加內(nèi)存的開銷和啟動(dòng)時(shí)間。
??? 堆和棧相比,由于大量new/delete的使用,容易造成大量的內(nèi)存碎片;由于沒有專門的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶態(tài)和核心態(tài)的切換,內(nèi)存的申請(qǐng),代價(jià)變得更加昂貴。所以棧在程序中是應(yīng)用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。
另外對(duì)存取效率的比較:
代碼:
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運(yùn)行時(shí)刻賦值的(位于棧上);
而bbbbbbbbbbb是在編譯時(shí)就確定的(位于堆上);
但是,在以后的存取中,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快。
比如:
void main()
{
??? char a = 1;
??? char c[] = "1234567890";
??? char *p ="1234567890";
??? a = c[1];
??? a = p[1];
??? return;
}
對(duì)應(yīng)的匯編代碼
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一種在讀取時(shí)直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據(jù)edx讀取字符,顯然慢了.
??? 無論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生(除非你是故意使其越界),因?yàn)樵浇绲慕Y(jié)果要么是程序崩潰,要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運(yùn)行過程中,沒有發(fā)生上面的問題,你還是要小心,說不定什么時(shí)候就崩掉,編寫穩(wěn)定安全的代碼才是最重要的
static用來控制變量的存儲(chǔ)方式和可見性
??? 函數(shù)內(nèi)部定義的變量,在程序執(zhí)行到它的定義處時(shí),編譯器為它在棧上分配空間,函數(shù)在棧上分配的空間在此函數(shù)執(zhí)行結(jié)束時(shí)會(huì)釋放掉,這樣就產(chǎn)生了一個(gè)問題: 如果想將函數(shù)中此變量的值保存至下一次調(diào)用時(shí),如何實(shí)現(xiàn)? 最容易想到的方法是定義一個(gè)全局的變量,但定義為一個(gè)全局變量有許多缺點(diǎn),最明顯的缺點(diǎn)是破壞了此變量的訪問范圍(使得在此函數(shù)中定義的變量,不僅僅受此 函數(shù)控制)。
??? 需要一個(gè)數(shù)據(jù)對(duì)象為整個(gè)類而非某個(gè)對(duì)象服務(wù),同時(shí)又力求不破壞類的封裝性,即要求此成員隱藏在類的內(nèi)部,對(duì)外不可見。
??? static的內(nèi)部機(jī)制:
??? 靜態(tài)數(shù)據(jù)成員要在程序一開始運(yùn)行時(shí)就必須存在。因?yàn)楹瘮?shù)在程序運(yùn)行中被調(diào)用,所以靜態(tài)數(shù)據(jù)成員不能在任何函數(shù)內(nèi)分配空間和初始化。
??? 這樣,它的空間分配有三個(gè)可能的地方,一是作為類的外部接口的頭文件,那里有類聲明;二是類定義的內(nèi)部實(shí)現(xiàn),那里有類的成員函數(shù)定義;三是應(yīng)用程序的main()函數(shù)前的全局?jǐn)?shù)據(jù)聲明和定義處。
??? 靜態(tài)數(shù)據(jù)成員要實(shí)際地分配空間,故不能在類的聲明中定義(只能聲明數(shù)據(jù)成員)。類聲明只聲明一個(gè)類的“尺寸和規(guī)格”,并不進(jìn)行實(shí)際的內(nèi)存分配,所以在類聲 明中寫成定義是錯(cuò)誤的。它也不能在頭文件中類聲明的外部定義,因?yàn)槟菚?huì)造成在多個(gè)使用該類的源文件中,對(duì)其重復(fù)定義。
??? static被引入以告知編譯器,將變量存儲(chǔ)在程序的靜態(tài)存儲(chǔ)區(qū)而非棧上空間,靜態(tài)數(shù)據(jù)成員按定義出現(xiàn)的先后順序依次初始化,注意靜態(tài)成員嵌套時(shí),要保證所嵌套的成員已經(jīng)初始化了。消除時(shí)的順序是初始化的反順序。
??? static的優(yōu)勢(shì):
??? 可以節(jié)省內(nèi)存,因?yàn)樗撬袑?duì)象所公有的,因此,對(duì)多個(gè)對(duì)象來說,靜態(tài)數(shù)據(jù)成員只存儲(chǔ)一處,供所有對(duì)象共用。靜態(tài)數(shù)據(jù)成員的值對(duì)每個(gè)對(duì)象都是一樣,但它的 值是可以更新的。只要對(duì)靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對(duì)象存取更新后的相同的值,這樣可以提高時(shí)間效率。
??? 引用靜態(tài)數(shù)據(jù)成員時(shí),采用如下格式:
??? <類名>::<靜態(tài)成員名>
??? 如果靜態(tài)數(shù)據(jù)成員的訪問權(quán)限允許的話(即public的成員),可在程序中,按上述格式來引用靜態(tài)數(shù)據(jù)成員。
??? PS:
??? (1)類的靜態(tài)成員函數(shù)是屬于整個(gè)類而非類的對(duì)象,所以它沒有this指針,這就導(dǎo)致了它僅能訪問類的靜態(tài)數(shù)據(jù)和靜態(tài)成員函數(shù)。
??? (2)不能將靜態(tài)成員函數(shù)定義為虛函數(shù)。
??? (3)由于靜態(tài)成員聲明于類中,操作于其外,所以對(duì)其取地址操作,就多少有些特殊,變量地址是指向其數(shù)據(jù)類型的指針 ,函數(shù)地址類型是一個(gè)“nonmember函數(shù)指針”。
??? (4)由于靜態(tài)成員函數(shù)沒有this指針,所以就差不多等同于nonmember函數(shù),結(jié)果就產(chǎn)生了一個(gè)意想不到的好處:成為一個(gè)callback函數(shù),使得我們得以將C++和C-based XWindow系統(tǒng)結(jié)合,同時(shí)也成功的應(yīng)用于線程函數(shù)身上。
??? (5)static并沒有增加程序的時(shí)空開銷,相反她還縮短了子類對(duì)父類靜態(tài)成員的訪問時(shí)間,節(jié)省了子類的內(nèi)存空間。
??? (6)靜態(tài)數(shù)據(jù)成員在<定義或說明>時(shí)前面加關(guān)鍵字static.
??? (7)靜態(tài)數(shù)據(jù)成員是靜態(tài)存儲(chǔ)的,所以必須對(duì)它進(jìn)行初始化。
??? (8)靜態(tài)成員初始化與一般數(shù)據(jù)成員初始化不同:
??? 初始化在類體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或?qū)ο笙嗷煜?#xff1b;初始化時(shí)不加該成員的訪問權(quán)限控制符private,public等;
??? 初始化時(shí)使用作用域運(yùn)算符來標(biāo)明它所屬類;
??? 所以我們得出靜態(tài)數(shù)據(jù)成員初始化的格式:
??? <數(shù)據(jù)類型><類名>::<靜態(tài)數(shù)據(jù)成員名>=<值>
??? (9)為了防止父類的影響,可以在子類定義一個(gè)與父類相同的靜態(tài)變量,以屏蔽父類的影響。這里有一點(diǎn)需要注意:我們說靜態(tài)成員為父類和子類共享,但我們有重復(fù)定義了靜態(tài)成員,這會(huì)不會(huì)引起錯(cuò)誤呢?不會(huì),我們的編譯器采用了一種絕妙的手法:name-mangling 用以生成唯一的標(biāo)志。
??? 補(bǔ)充:new delete[],基本類型的對(duì)象沒有析構(gòu)函數(shù)(例如 int,char),所以回收基本類型組成的數(shù)組空間delete delete[] 都是應(yīng)該可以如:int p = new int[10], delete p 和delete[]p 都可 .但是對(duì)于類對(duì)象數(shù)組(如string strArr = new string[10]),只能 delete[].對(duì) new 的單個(gè)對(duì)象,只能 delete 不能 delete[] 回收空間.
一個(gè)典型的嵌入式平臺(tái)動(dòng)態(tài)內(nèi)存管理機(jī)制
(http://www.elechome.com/Article/ShowArticle.asp?ArticleID=170)
當(dāng)前,絕大多數(shù)嵌入式平臺(tái)上的軟件都采用C語言編寫。除了代碼簡(jiǎn)潔、運(yùn)行高效之外,靈活操作內(nèi)存的能力更是C語言的重要特色。然而,不恰當(dāng)?shù)膬?nèi)存操作通常也是錯(cuò)誤的根源之一。如“內(nèi)存泄漏” ——不能正確地釋放已分配的動(dòng)態(tài)內(nèi)存,就是一種非常難于檢測(cè)的存錯(cuò)誤。持續(xù)的內(nèi)存泄漏會(huì)使程序性能下降到最終完全不能運(yùn)行,進(jìn)而影響到所有其它有動(dòng)態(tài)內(nèi)存需求的程序,在某些相對(duì)簡(jiǎn)單的嵌入式平臺(tái)上甚至?xí)恋K操作系統(tǒng)的運(yùn)轉(zhuǎn)。再如“寫內(nèi)存越界”,一種不合法的寫內(nèi)存操作,極可能破壞到本程序中正在使用的其它數(shù)據(jù),嚴(yán)重的時(shí)候還可能對(duì)其它正在運(yùn)行的程序甚至整個(gè)系統(tǒng)造成影響。為此,本文介紹一個(gè)增強(qiáng)的、可定制的動(dòng)態(tài)內(nèi)存管理模塊(以下不妨簡(jiǎn)稱Fense),在 C語言提供的內(nèi)存分配函數(shù)基礎(chǔ)上,增加了對(duì)動(dòng)態(tài)內(nèi)存的管理功能;能記錄軟件運(yùn)行過程中出現(xiàn)的內(nèi)存泄漏信息,同時(shí)也具一定的監(jiān)測(cè)內(nèi)存操作的能力;可以發(fā)現(xiàn)絕大多數(shù)對(duì)動(dòng)態(tài)內(nèi)存的寫越界錯(cuò)誤。
Fense的設(shè)計(jì)原理
通過設(shè)立一個(gè)雙向鏈表(struct Head *stHead)來保存所有被分配的動(dòng)態(tài)內(nèi)存塊的信息。鏈表中的每個(gè)節(jié)點(diǎn)對(duì)應(yīng)一個(gè)動(dòng)態(tài)內(nèi)存塊,節(jié)點(diǎn)中包括此內(nèi)存大小、分配發(fā)生時(shí)所在的源文件名和行號(hào)以及被釋放的時(shí)候,Fense又從st_Head中刪除之,檢查st_Head中的節(jié)點(diǎn)即可得到未被釋放的本節(jié)點(diǎn)的數(shù)值校驗(yàn)和等。Fense將每一個(gè)分配的動(dòng)態(tài)內(nèi)存塊插入到鏈表st_Head中;當(dāng)此內(nèi)存放內(nèi)存塊信息。鏈表節(jié)點(diǎn)結(jié)構(gòu)定義如下:
struct Head{
char file; /分配所在源文件名*/
unsigned long line; /*分配所在的行號(hào)*/
size_t size; /*分配的內(nèi)存大小*/
int checksum; /*鏈表節(jié)點(diǎn)校驗(yàn)和*/
struct Head prev,next; /*雙鏈表的前后節(jié)點(diǎn)指針*/
};
/*全局的雙向鏈表*/
struct Head *st_Head=NULL;
為了檢測(cè)寫越界的錯(cuò)誤,Fense在用戶申請(qǐng)的內(nèi)存前后各增加了一定大小的內(nèi)存作為監(jiān)測(cè)區(qū)域,并初始化成預(yù)定值。這樣,當(dāng)程序發(fā)生越界寫操作時(shí),預(yù)定值就會(huì)發(fā)生改變,Fense即可檢測(cè)到錯(cuò)誤。
Fense的具體實(shí)現(xiàn)
Fense 提供Fense_Malloc、Fense_Free、Fense_Realloc及Fense_Calloc等內(nèi)存管理函數(shù),功能和調(diào)用形式與C語言中的malloc、free、realloc和calloc保持一致。限于篇幅,這里僅對(duì)Fense_Malloc和Fense_Free的實(shí)現(xiàn)過程做一個(gè)簡(jiǎn)單描述,
/*內(nèi)存分配函數(shù)*/
void *Fense_Malloc(size_t size,char *file,unsigned long line)
{
//檢查Fense的運(yùn)行時(shí)開關(guān),如果Fense被關(guān)閉,則調(diào)用malloc
//分配并返回
//檢查是否零分配,如有則提示警告信息后返回0(用戶定制選項(xiàng))
//分配內(nèi)存,包括鏈表節(jié)點(diǎn)區(qū)域和前/后監(jiān)測(cè)區(qū)域
//初始化鏈表節(jié)點(diǎn),保存分配內(nèi)存的信息,包括分配的大小、所在文件名和行號(hào)
//將此節(jié)點(diǎn)插入鏈表st_Head
//為本節(jié)點(diǎn)區(qū)域計(jì)算校驗(yàn)和
//用預(yù)設(shè)值初始化前/后監(jiān)測(cè)區(qū)域
//用預(yù)設(shè)值填充用戶內(nèi)存區(qū)域(用戶定制選項(xiàng))
//返回用戶內(nèi)存區(qū)域的起始位置
}
/*內(nèi)存釋放函數(shù)*/
void Fense_Free(void *uptr,char *file,unsigned long line)
{
//檢查Fense的運(yùn)行時(shí)開關(guān),如果Fense初關(guān)閉,則調(diào)用free釋譯并返回
//檢查所有Fense管理下的動(dòng)態(tài)內(nèi)存(用戶定制選項(xiàng))
//判斷當(dāng)前內(nèi)存塊是否在鏈表st_Head中,如果不在則提示
//警靠信息,退出(用戶定制選項(xiàng))
//檢查當(dāng)前內(nèi)存塊是否存在越界操作
//將當(dāng)前內(nèi)存塊的相應(yīng)的鏈表節(jié)點(diǎn)從st_Head中刪除
//重新計(jì)算當(dāng)前節(jié)點(diǎn)的前后相鄰節(jié)點(diǎn)的校驗(yàn)和
//用預(yù)設(shè)值填充被釋放的內(nèi)存區(qū)(用戶定制選項(xiàng))
//調(diào)用free釋放當(dāng)前的內(nèi)存塊
}
用戶態(tài)與核心態(tài)
386及以上的CPU實(shí)現(xiàn)了4個(gè)特權(quán)級(jí)模式(WINDOWS只用到了其中兩個(gè)),其中特權(quán)級(jí)0(Ring0)是留給操作系統(tǒng)代碼,設(shè)備驅(qū)動(dòng)程序代碼使用的,它們工作于系統(tǒng)核心態(tài);而特權(quán)極3(Ring3)則給普通的用戶程序使用,它們工作在用戶態(tài)。運(yùn)行于處理器核心態(tài)的代碼不受任何的限制,可以自由地訪問任何有效地址,進(jìn)行直接端口訪問。而運(yùn)行于用戶態(tài)的代碼則要受到處理器的諸多檢查,它們只能訪問映射其地址空間的頁表項(xiàng)中規(guī)定的在用戶態(tài)下可訪問頁面的虛擬地址,且只能對(duì)任務(wù)狀態(tài)段(TSS)中I/O許可位圖(I/O Permission Bitmap)中規(guī)定的可訪問端口進(jìn)行直接訪問(此時(shí)處理器狀態(tài)和控制標(biāo)志寄存器EFLAGS中的IOPL通常為0,指明當(dāng)前可以進(jìn)行直接I/O的最低特權(quán)級(jí)別是Ring0)。以上的討論只限于保護(hù)模式操作系統(tǒng),象DOS這種實(shí)模式操作系統(tǒng)則沒有這些概念,其中的所有代碼都可被看作運(yùn)行在核心態(tài)。既然運(yùn)行在核心態(tài)有如此之多的優(yōu)勢(shì),那么病毒當(dāng)然沒有理由不想得到Ring0。處理器模式從Ring3向Ring0的切換發(fā)生在控制權(quán)轉(zhuǎn)移時(shí),有以下兩種情況:訪問調(diào)用門的長(zhǎng)轉(zhuǎn)移指令CALL,訪問中斷門或陷阱門的INT指令。具體的轉(zhuǎn)移細(xì)節(jié)由于涉及復(fù)雜的保護(hù)檢查和堆棧切換,不再贅述,請(qǐng)參閱相關(guān)資料。現(xiàn)代的操作系統(tǒng)通常使用中斷門來提供系統(tǒng)服務(wù),通過執(zhí)行一條陷入指令來完成模式切換,在INTEL X86上這條指令是INT,如在WIN9X下是INT30(保護(hù)模式回調(diào)),在LINUX下是INT80,在WINNT/2000下是INT2E。用戶模式的服務(wù)程序(如系統(tǒng)DLL)通過執(zhí)行一個(gè)INTXX來請(qǐng)求系統(tǒng)服務(wù),然后處理器模式將切換到核心態(tài),工作于核心態(tài)的相應(yīng)的系統(tǒng)代碼將服務(wù)于此次請(qǐng)求并將結(jié)果傳給用戶程序。
用戶態(tài)又稱目態(tài),核心態(tài)又稱管態(tài)???
在X86下,可以理解成ring3 和 ring 0???
用戶態(tài)權(quán)限低,無權(quán)調(diào)用一些核心態(tài)才能調(diào)用的指令
這是兩種內(nèi)存保護(hù)態(tài),一個(gè)進(jìn)程4G地址空間中的每一頁均被標(biāo)記出它是否是處于核心態(tài),所有系統(tǒng)地址空間中的頁是核心態(tài),用戶空間中的頁則為用戶態(tài)。???
訪問標(biāo)記為核心頁的的唯一途徑是運(yùn)行在核心態(tài),而只有操作系統(tǒng)和設(shè)備驅(qū)動(dòng)才能運(yùn)行在核心態(tài)。???
因此一個(gè)應(yīng)用程序不能使自己運(yùn)行在核心態(tài)中,這樣為應(yīng)用程序和操作系統(tǒng)提供了內(nèi)存保護(hù)的堅(jiān)固級(jí)別。用戶態(tài)怎么修改都不能讓系統(tǒng)崩潰。當(dāng)然應(yīng)用程序可以通過加載設(shè)備驅(qū)動(dòng)進(jìn)入核心態(tài),去修改系統(tǒng)數(shù)據(jù)。
MFC中,消息傳遞是在用戶態(tài)完成的。同步對(duì)象大部分在核心態(tài),只有CriticalSection可以在用戶態(tài)工作(也可能進(jìn)入核心態(tài)),所以CriticalSection是不能用于進(jìn)程間同步的。
轉(zhuǎn)載于:https://www.cnblogs.com/clive/archive/2009/10/29/Memory_details_stack_and_heap.html
總結(jié)
以上是生活随笔為你收集整理的内存区划分;内存分配;堆、栈概念分析;动态内存管理数据结构及程序样例;核心态与用户态...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【ABAP系列】SAP ABAP模块-任
- 下一篇: Chord算法