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