在应用程序中使用虚拟内存——Windows核心编程学习手札之十五
在應(yīng)用程序中使用虛擬內(nèi)存
——Windows核心編程學(xué)習(xí)手札之十五
Windows提供了3種進(jìn)行內(nèi)存管理的方法:
1)? 虛擬內(nèi)存,最適合用來(lái)管理大量對(duì)象或結(jié)構(gòu)數(shù)組;
2)? 內(nèi)存映射文件,最適合用來(lái)管理大型數(shù)據(jù)流(通常來(lái)自文件)以及在單個(gè)計(jì)算機(jī)上運(yùn)行的多個(gè)進(jìn)程之間共享數(shù)據(jù);
3)? 內(nèi)存堆棧,最適合用來(lái)管理大量的小對(duì)象;
用于管理虛擬內(nèi)存的函數(shù)可以用來(lái)直接保留一個(gè)地址空間區(qū)域,將物理存儲(chǔ)器(來(lái)自頁(yè)文件)提交給該區(qū)域,并且可以設(shè)置你自己的保護(hù)屬性。
通過(guò)調(diào)用VirtualAlloc函數(shù),可以在進(jìn)程的地址空間中保留一個(gè)區(qū)域:
PVOID VirtualAlloc(
???????????????? PVOID pvAddress,
???????????????? SIZE_T dwSize,
???????????????? DWORD fdwAllocationType,
???????????????? DWORD fdwProtect);
第一個(gè)參數(shù)pvAddress包含一個(gè)內(nèi)存地址,用于設(shè)定想讓系統(tǒng)將地址空間保留在什么地址,多數(shù)情況下,該參數(shù)傳遞NULL,用于告訴VirtualAlloc,保存一個(gè)空間地址區(qū)域的記錄的系統(tǒng)應(yīng)該將區(qū)域保留在它認(rèn)為合適的任何地方。系統(tǒng)可以從進(jìn)程的地址空間的任何位置來(lái)保留一個(gè)區(qū)域,因?yàn)椴荒鼙WC系統(tǒng)可以從地址空間的底部向上或從上面向底部來(lái)分配各個(gè)區(qū)域,使用MEM_TOP_DOWN標(biāo)志來(lái)說(shuō)明該分配方式。
分配內(nèi)存時(shí),操作系統(tǒng)尋找一個(gè)大小滿足需要的內(nèi)存塊,并返回內(nèi)存塊的地址,由于每個(gè)進(jìn)程有自己的地址空間,可以設(shè)定一個(gè)基本內(nèi)存地址,在這個(gè)地址上讓操作系統(tǒng)保留地址空間區(qū)域。例如,將一個(gè)從50MB開(kāi)始的區(qū)域保留在進(jìn)程的地址空間中,傳遞pvAddress為52428800(50*1024*1024),如果該內(nèi)存地址有一個(gè)足夠大的空閑區(qū)域滿足你的要求,那系統(tǒng)就保留這個(gè)區(qū)域并返回地址,如果在特定的地址上不存在空閑區(qū)域,或者空閑區(qū)域不夠大,那系統(tǒng)就不滿足需求,VirtualAlloc函數(shù)返回NULL。注意,為pvAddress參數(shù)傳遞的任何地址必須始終位于進(jìn)程的用戶方式分區(qū)中,否則對(duì)VirtualAlloc函數(shù)的調(diào)用就會(huì)失敗,導(dǎo)致其返回NULL。地址空間區(qū)域總是按照分配粒度的邊界來(lái)保留(迄今為止所有的Windows環(huán)境下都是64KB),因此,如果試圖在進(jìn)程地址空間中保留一個(gè)從19668992(300*65356+8192)這個(gè)地址開(kāi)始的區(qū)域,系統(tǒng)就會(huì)將這個(gè)地址保留為64KB的倍數(shù),即19660800(300*65356)開(kāi)始的區(qū)域。如果VirtualAlloc函數(shù)滿足了需求,就返回保留區(qū)域的基地址,如果傳遞一個(gè)指定的地址作為VirtualAlloc的pvAddress參數(shù),那么該返回值與傳遞給VirtualAlloc的值相同,并取為64KB的整數(shù)倍。
VirtualAlloc函數(shù)的第二個(gè)參數(shù)是dwSize,用于設(shè)定想保留的區(qū)域大小(以字節(jié)為單位),系統(tǒng)保留的區(qū)域必須是CPU頁(yè)面大小的倍數(shù),如試圖保留一個(gè)跨越62KB的區(qū)域,結(jié)果就會(huì)在使用4KB/8KB或16KB頁(yè)面的計(jì)算機(jī)上產(chǎn)生一個(gè)跨越64KB的區(qū)域。
VirtualAlloc函數(shù)的第三個(gè)參數(shù)是fdwAllocationType,告訴系統(tǒng)想保留一個(gè)區(qū)域還是提交物理存儲(chǔ)器(VirtualAlloc函數(shù)也可以用來(lái)提交物理存儲(chǔ)器),若要保留一個(gè)地址空間區(qū)域,傳遞MEM_RESERVE標(biāo)識(shí)符作為fdwAllocationType參數(shù)的值。如果保留區(qū)域預(yù)計(jì)在很長(zhǎng)時(shí)間內(nèi)不會(huì)被釋放,那可以在盡可能高的內(nèi)存地址上保留該區(qū)域,這樣,該區(qū)域就不會(huì)從進(jìn)程地址空間的中間位置上進(jìn)行保留,因此這個(gè)位置可能導(dǎo)致區(qū)域分成碎片,如果想讓系統(tǒng)在最高內(nèi)存地址上保留一個(gè)區(qū)域,需為pvAddress參數(shù)和fdwAllocationType參數(shù)傳遞NULL,還必須逐位使用OR將MEM_TOP_DOWN標(biāo)志和MEM_RESERVE標(biāo)志連接起來(lái)。
VirtualAlloca最后一個(gè)參數(shù)是fdwProtect,用于指明應(yīng)該賦予該地址空間區(qū)域的保護(hù)屬性。與該區(qū)域相關(guān)聯(lián)的保護(hù)屬性對(duì)映射到該區(qū)域的已提交內(nèi)存沒(méi)有影響,無(wú)論賦予區(qū)域的保護(hù)屬性是社呢,如果沒(méi)有提交任何物理存儲(chǔ)器,那訪問(wèn)該范圍中的內(nèi)存地址的任何企圖都將導(dǎo)致該線程引發(fā)一個(gè)訪問(wèn)違規(guī)。
當(dāng)保留一個(gè)區(qū)域后,必須將物理存儲(chǔ)器提交給該區(qū)域,然后才能訪問(wèn)該區(qū)域中包含的內(nèi)存地址,系統(tǒng)從它的頁(yè)文件中將已提交的物理存儲(chǔ)器分配給一個(gè)區(qū)域,物理存儲(chǔ)器總是按頁(yè)面邊界和頁(yè)面大小的塊來(lái)提交的。若要提交物理存儲(chǔ)器,須再次調(diào)用VirtualAlloc函數(shù),設(shè)置參數(shù)fdwAllocationType為MEM_COMMIT,傳遞的頁(yè)面保護(hù)屬性一般與調(diào)用VirtualAlloc來(lái)保留區(qū)域時(shí)使用的保護(hù)屬性相同(大多數(shù)情況下是PAGE_READWRITE)。在已保留的區(qū)域中,須告訴VirtualAlloc函數(shù),要將物理存儲(chǔ)器提交到那里以及提交多大物理存儲(chǔ)空間,實(shí)現(xiàn)這一點(diǎn),需要在pvAddress參數(shù)中設(shè)定需要的內(nèi)存地址,并在dwSize參數(shù)上設(shè)定物理存儲(chǔ)器的大小。
提交物理存儲(chǔ)器的例子:應(yīng)用程序在X86CPU上運(yùn)行,保留了一個(gè)從地址5242880開(kāi)始的512KB區(qū)域,現(xiàn)在將物理存儲(chǔ)器提交給已保留區(qū)域的6KB部分,從2KB的地方開(kāi)始,直到已保留區(qū)域的地址空間??烧{(diào)用帶有MEM_COMMIT標(biāo)志的VirtualAlloc函數(shù):
VirtualAlloc((PVOID)(5242880+(2*1024)),6*1024,MEM_COMMIT,PAGE_READWRITE);
例子中,系統(tǒng)必須提交8KB的物理存儲(chǔ)器,地址范圍從5242880到5251071(5242880+8KB-1),這兩個(gè)提交的頁(yè)面都擁有PAGE_READWRITE保護(hù)屬性,保護(hù)屬性在整個(gè)頁(yè)面單位內(nèi)生效,同一個(gè)內(nèi)存頁(yè)面的不同部分不能使用不同的保護(hù)屬性,但在不同區(qū)域中的一個(gè)頁(yè)面可以使用兩種以上保護(hù)屬性。
若要回收映射到一個(gè)區(qū)域的物理存儲(chǔ)器,或者釋放這個(gè)地址空間區(qū)域,可調(diào)用VirtualFree函數(shù):
BOOL VirtualFree(
?????? ????????LPVOID pvAddress,
?????????????? SIZE_T dwSize,
?????????????? DWORD fdwFreeType);
當(dāng)進(jìn)程不再訪問(wèn)區(qū)域中的物理存儲(chǔ)器,可以釋放整個(gè)保留的區(qū)域和所有提交給該區(qū)域的物理存儲(chǔ)器,方法是一次調(diào)用VirtualFree函數(shù)。pvAddress是釋放區(qū)域的基地址,與該區(qū)域被保留時(shí)VirtualAlloc函數(shù)返回的地址相同,系統(tǒng)知道在特定內(nèi)存地址上的區(qū)域大小,因此dwSize參數(shù)可以為零,實(shí)際,該參數(shù)必須傳遞零,否則調(diào)用VirtualFree失敗,對(duì)最后一個(gè)參數(shù)fdwFreeType,必須傳遞MEM_RELEASE,以告訴系統(tǒng)將所有映射的物理存儲(chǔ)器提給該區(qū)域并釋放該區(qū)域,當(dāng)釋放一個(gè)區(qū)域時(shí),必須釋放該區(qū)域保留的所有地址空間。如不想保留128KB的區(qū)域,不能只釋放64KB。當(dāng)想要從一個(gè)區(qū)域回收某些物理存儲(chǔ)器,但是卻不釋放該區(qū)域,設(shè)置參數(shù)fdwFreeType為MEM_DECOMMIT標(biāo)志。回收時(shí)也按照頁(yè)面的分配粒度來(lái)進(jìn)行,設(shè)定一個(gè)頁(yè)面中間的一個(gè)內(nèi)存地址就可以回收整個(gè)頁(yè)面,如果pvAddress+dwSize的值位于一個(gè)頁(yè)面的中間,那包含該地址的整個(gè)頁(yè)面將被回收,因此,位于pvAddress至pvAddress+dwSize范圍內(nèi)的所有頁(yè)面均被回收。
?
總結(jié)
以上是生活随笔為你收集整理的在应用程序中使用虚拟内存——Windows核心编程学习手札之十五的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 虚拟内存——Windows核心编程学习手
- 下一篇: 线程的堆栈——Windows核心编程学习