當(dāng)我們?cè)赪indows中雙擊一個(gè)應(yīng)用程序圖標(biāo)后,系統(tǒng)為該應(yīng)用程序創(chuàng)建一個(gè)進(jìn)程,Windows使得每個(gè)進(jìn)程都擁有2GB的地址空間,這2GB地址空間用于程序存放代碼,數(shù)據(jù),堆棧,自由存儲(chǔ)區(qū)(堆),另外2GB用于共享系統(tǒng)使用.
2.虛擬地址到實(shí)際地址的映射
前面的這些地址并不是物理內(nèi)存中的地址,而是該進(jìn)程空間中的虛擬地址,虛擬空間只是Windows為該進(jìn)程分配的一個(gè)虛擬的地址空間,只有當(dāng)其和物理內(nèi)存相關(guān)聯(lián)后才有意義.
2.1內(nèi)存分頁(yè)
每個(gè)物理地址對(duì)應(yīng)一個(gè)虛擬地址?1GB那頁(yè)表該有多長(zhǎng),所以將內(nèi)存分頁(yè)管理,4K為一頁(yè),即4K就是一個(gè)最小單位。
2.2建立映射--分頁(yè)
進(jìn)程被創(chuàng)建時(shí)會(huì)建立一個(gè)虛擬內(nèi)從到物理內(nèi)存的映射表--頁(yè)表,根據(jù)頁(yè)表可以將虛擬內(nèi)存和物理內(nèi)存關(guān)聯(lián)起來(lái).

2.3虛擬內(nèi)存
就是把磁盤(pán)拿來(lái)當(dāng)內(nèi)存用,這是以前買(mǎi)電腦時(shí)的想法。所以就一直都想不明白一個(gè)問(wèn)題:要真是這樣,那內(nèi)存分個(gè)什么1GB,2GB,4GB,大家都買(mǎi)個(gè)1M的內(nèi)存條,然后把自己磁盤(pán)拿來(lái)當(dāng)內(nèi)存用多好,比2GB,4GB不知道要大多少。
其實(shí)這個(gè)說(shuō)法有一點(diǎn)擦邊球的味道,虛擬內(nèi)存是一些系統(tǒng)頁(yè)文件,存放在磁盤(pán)上,每個(gè)系統(tǒng)頁(yè)文件大小也為4K,物理內(nèi)存也被分頁(yè),每個(gè)頁(yè)大小也為4K,這樣虛擬頁(yè)文件和物理內(nèi)存頁(yè)就可以對(duì)應(yīng),實(shí)際上虛擬內(nèi)存就是用于物理內(nèi)存的臨時(shí)存放的磁盤(pán)空間。
頁(yè)文件就是內(nèi)存頁(yè),物理內(nèi)存中每頁(yè)叫物理頁(yè),磁盤(pán)上的頁(yè)文件叫虛擬頁(yè),物理頁(yè)+虛擬頁(yè)就是系統(tǒng)所以使用的頁(yè)文件的總和。還有映像頁(yè)文件和映射頁(yè)文件,映像頁(yè)文件就是拿程序本身當(dāng)頁(yè)文件使用(而不是用系統(tǒng)的頁(yè)文件),映射頁(yè)文件就是使用磁盤(pán)上的文件(非系統(tǒng)頁(yè)文件)來(lái)當(dāng)頁(yè)文件使用(這主要用于讀取文件)。
虛擬地址頁(yè)的狀態(tài):
(1)空閑:該區(qū)域沒(méi)有被所使用,也沒(méi)有被預(yù)定,沒(méi)有和物理內(nèi)存管理
(2)私有:該區(qū)域雖然沒(méi)有被使用,但是已經(jīng)被申請(qǐng)(預(yù)定了),別人無(wú)法使用它。同樣也沒(méi)和物理內(nèi)存關(guān)聯(lián)
(3)提交:該區(qū)域已經(jīng)和物理內(nèi)存管理,可以使用了
2.4虛擬內(nèi)存和物理內(nèi)存的管理
Windows是多任務(wù)的系統(tǒng),在每個(gè)進(jìn)程創(chuàng)建時(shí),系統(tǒng)為每個(gè)進(jìn)程也創(chuàng)建了一個(gè)頁(yè)表,用于虛擬地址到物理地址的轉(zhuǎn)換。比如現(xiàn)在程序在執(zhí)行進(jìn)程A,用戶(hù)切換到了另外一個(gè)進(jìn)程B,則系統(tǒng)會(huì)將進(jìn)程A在內(nèi)存中的數(shù)據(jù)存放到頁(yè)文件中,并更新進(jìn)程A的頁(yè)表(使虛擬地址和頁(yè)文件形成映射)。然后讀取進(jìn)程B的頁(yè)表,根據(jù)頁(yè)表判斷進(jìn)程B的數(shù)據(jù)是在內(nèi)存中還是在頁(yè)文件中(通過(guò)頁(yè)文件的類(lèi)型來(lái)判斷),如果在內(nèi)存中就直接讀取,如果在頁(yè)面文件中,就將頁(yè)面文件內(nèi)容讀入物理內(nèi)存,然后更新頁(yè)表(使虛擬地址和物理內(nèi)存形成映射)。這樣一看,虛擬內(nèi)存實(shí)際上就是冒牌的物理內(nèi)存了吧。
3.程序執(zhí)行
一個(gè)PE文件有數(shù)據(jù)區(qū),代碼區(qū),堆棧區(qū)(由系統(tǒng)分配,用于管理局部變量),使用OD載入一個(gè)程序就可以知道這些都是以二進(jìn)制的形式保存在文件中。
程序剛運(yùn)行的時(shí)候,系統(tǒng)不直接將整個(gè)程序載入到物理內(nèi)存中,也不將其載入到頁(yè)文件中,而是以程序文件本身作為頁(yè)文件形成映射(虛擬地址到頁(yè)文件的映射),建立頁(yè)表,然后隨著程序的執(zhí)行通過(guò)頁(yè)表來(lái)將其虛擬地址轉(zhuǎn)換成物理地址(將頁(yè)文件讀入內(nèi)存),然后在讀取內(nèi)存中的指令或數(shù)據(jù)。當(dāng)進(jìn)程被切換時(shí),將內(nèi)存內(nèi)容保存到頁(yè)文件,更新頁(yè)表,如此往復(fù),實(shí)現(xiàn)多任務(wù)操作。
可以知道,程序的代碼段,數(shù)據(jù)段,堆棧區(qū)(系統(tǒng)分配)這些虛擬地址區(qū)域已經(jīng)是映射狀態(tài),即有相應(yīng)的物理內(nèi)存與之對(duì)應(yīng)。系統(tǒng)為每個(gè)進(jìn)程提供了2G的自己的虛擬地址空間,剩下的虛擬地址空間干什么用?
剩下的虛擬地址空間就是給程序運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存使用。C++中 new的功能就是動(dòng)態(tài)分配地址空間:
申請(qǐng)內(nèi)存的最小單位是區(qū)域,每個(gè)區(qū)域?yàn)镃PU粒度大小,即64K,每次申請(qǐng)的內(nèi)存都必須是64K的整數(shù)倍,C++ new功能申請(qǐng)一個(gè)區(qū)域,保留該區(qū)域,然后提交需要的頁(yè),其他的保留。
char *address=new char[1024]; //分配1K的內(nèi)存
這條語(yǔ)句首先申請(qǐng)一個(gè)區(qū)域的地址空間,表示這個(gè)區(qū)域已經(jīng)被預(yù)定了,這就是上述區(qū)域狀態(tài)中的私有狀態(tài),雖然預(yù)定了,但是還沒(méi)有和物理內(nèi)存關(guān)聯(lián)起來(lái),所以程序也無(wú)法使用該內(nèi)存,然后程序?qū)⑦@1K的內(nèi)存提交,就是映射到了內(nèi)存當(dāng)中,區(qū)域的狀態(tài)就變成了映射狀態(tài),這樣程序就可以使用這1K的內(nèi)存了,而剩下的頁(yè)仍然為保留狀態(tài)。那當(dāng)進(jìn)程被切換時(shí),這1K的進(jìn)程存放在哪呢?程序本身的頁(yè)文件已經(jīng)被代碼,全局?jǐn)?shù)據(jù),堆棧這些所使用了,所以系統(tǒng)會(huì)為自由存儲(chǔ)區(qū)分配的內(nèi)存分配新的頁(yè)文件來(lái)做虛擬內(nèi)存。
局部變量的定義是由系統(tǒng)分配的,它將局部變量分配到堆棧區(qū),因?yàn)槎褩^(qū)已經(jīng)映射了,所以不用在映射,故不用使用新的頁(yè)文件了。堆棧區(qū)的大小為1M左右,如果分配的局部變量超過(guò)1M會(huì)產(chǎn)生堆棧溢出。

可以看到,系統(tǒng)的單個(gè)頁(yè)文件大小為4K,程序自己的虛擬空間地址00010000到7FFEFFFF差不多是2G動(dòng)態(tài)分配一個(gè)500M的內(nèi)存后,物理內(nèi)存,頁(yè)文件,可用的虛擬地址空間都減少了500M
查詢(xún)內(nèi)存狀態(tài)使用VirtualQuery(Address[n],&membaseinf,sizeof(MEMORY_BASIC_INFORMATION))
定義3個(gè)變量
char Stack[20*1024];//存在堆棧中,堆棧在程序啟動(dòng)時(shí)已經(jīng)被映像到內(nèi)存中了
char* Dynamic=new char[64*1024]; //動(dòng)態(tài)分配一個(gè)70K的內(nèi)存
char* Dynamic2=new char[1024]; //動(dòng)態(tài)分配一個(gè)1K的內(nèi)存

參數(shù)說(shuō)明:
地址所在頁(yè)面基地址:查詢(xún)的地址所在的頁(yè)面的起始地址
頁(yè)面所在區(qū)域的基地址:頁(yè)面所在區(qū)域的起始地址
區(qū)域保護(hù)屬性:分配區(qū)域時(shí)要設(shè)置區(qū)域的讀寫(xiě)屬性
從頁(yè)面基地址開(kāi)始擁有相同屬性(空閑,保留,提交)的所有頁(yè)的字節(jié)數(shù):可以看到這些都是4096的整數(shù)倍,因?yàn)橐粋€(gè)頁(yè)4096,該大小一般都和申請(qǐng)的內(nèi)存空間大小相當(dāng),因?yàn)檫@些內(nèi)存都被提交了。
申請(qǐng)一個(gè)內(nèi)存空間的過(guò)程
首先申請(qǐng)一個(gè)虛擬地址空間區(qū)域,然后提交申請(qǐng)的內(nèi)存空間大小的頁(yè)(將其和頁(yè)文件關(guān)聯(lián))。其他的地址空間保留。
第一條指令分配了一個(gè)字符數(shù)組的局部變量,該變量分配在堆棧中,由系統(tǒng)分配,所以其區(qū)域?yàn)槌绦虻撵o態(tài)存儲(chǔ)區(qū),即在程序啟動(dòng)時(shí)候這個(gè)區(qū)域的所有虛擬地址就和程序文件本身映像了,所以局部變量的區(qū)域基地址都是一樣的,那為什么它的頁(yè)面文件類(lèi)型是頁(yè)文件呢?不應(yīng)該是exe映像么?因?yàn)楝F(xiàn)在文件在內(nèi)存中,所有是物理頁(yè),就是頁(yè)文件。
第二條指令動(dòng)態(tài)分配了一塊大小為1K的內(nèi)存區(qū)域,這塊內(nèi)存分配在自由存儲(chǔ)區(qū),它所在的區(qū)域是在堆中申請(qǐng)的一個(gè)區(qū)域,第三條指令在堆中分配了一個(gè)70K左右的內(nèi)存,因?yàn)樗麄兪窃诙阎蟹峙涞模赃@2個(gè)變量的區(qū)域基地址是不一樣的。
分配的區(qū)域有多大?
第三條指令分配了一個(gè)70K左右的內(nèi)存,它會(huì)向系統(tǒng)申請(qǐng)多大的區(qū)域呢?由區(qū)域大小為64K的整數(shù)倍知該區(qū)域至少為128K,查詢(xún)這70K之后的虛擬地址的狀態(tài)
可以看到該地址所在的區(qū)域和Dynamic是一樣的,它的基地址為580000(轉(zhuǎn)載者加:不應(yīng)該是594000吧),在那70K之后,這之后的區(qū)域的狀態(tài)為保留,說(shuō)明系統(tǒng)保留了剩下的區(qū)域,這剩下的區(qū)域有966656,就是966K左右的大小,那整個(gè)區(qū)域的大小就是(0x14000)81920+966656。