內(nèi)存管理
如果你在寫Windows CE 程序中遇到的最重要的問題,那一定是內(nèi)存問題。一個(gè)WinCE 系統(tǒng)可能只有4MB 的RAM,這相對于個(gè)人電腦來說是十分少的,因?yàn)閭€(gè)人電腦的標(biāo)準(zhǔn)配置已經(jīng)到了128MB 甚至更多。事實(shí)上,運(yùn)行WinCE 的機(jī)器的內(nèi)存十分缺乏,以至于有時(shí)候有必要在寫程序的時(shí)候?yàn)楣?jié)約內(nèi)存而犧牲程序的整體性能。
幸運(yùn)的是,盡管WinCE系統(tǒng)的內(nèi)存很小,但可用來管理內(nèi)存的函數(shù)卻十分完善。WinCE實(shí)現(xiàn)了Microsoft Windows XP和Microsoft Windows Me中可用到的幾乎全部的Win32內(nèi)存管理API。WinCE支持虛擬內(nèi)存(virtual memory)分配,本地(local)和分離(separate)的堆(heaps),甚至還有(memory-mapped files)內(nèi)存映射文件。
像Windows XP一樣,WinCE支持一個(gè)帶有應(yīng)用程序間內(nèi)存保護(hù)功能的32位平面地址空間,但是WinCE是被設(shè)計(jì)來應(yīng)用于不同場合,所以它底層的內(nèi)存結(jié)構(gòu)不同于Windows XP。這些不同能夠影響到你如何設(shè)計(jì)一個(gè)WinCE 應(yīng)用程序。在這一章中,我將講述最基礎(chǔ)的WinCE內(nèi)存結(jié)構(gòu)。我也將講述包括WinCE中可用的內(nèi)存分配方式中的不同點(diǎn)以及如何使用這些不同的內(nèi)存類型來最小化你的程序的內(nèi)存占有率。
內(nèi)存基礎(chǔ)
對所有的電腦來說,系統(tǒng)地運(yùn)行一個(gè)WinCE,需要ROM(只讀存儲(chǔ)器)和RAM(隨機(jī)存儲(chǔ)器)。但不論如何,在WinCE系統(tǒng)中,ROM和RAM的使用還是稍微有些不同于個(gè)人電腦環(huán)境。
關(guān)于RAM
RAM在WinCE 系統(tǒng)中被分為兩個(gè)區(qū)域:第一個(gè)是程序的存儲(chǔ)區(qū)(program memory),也叫做系統(tǒng)堆(system heap)。第二個(gè)是對象存儲(chǔ)區(qū)(object store)。這個(gè)對象存儲(chǔ)區(qū)有點(diǎn)像一個(gè)永久的虛擬RAM磁盤。不同于PC上的舊式的虛擬RAM磁盤,對象存儲(chǔ)區(qū)保留存儲(chǔ)的文件甚至當(dāng)系統(tǒng)被關(guān)閉以后。(腳注)這種安排的原因是WinCE 系統(tǒng),例如Pocket PC代表性地具有一個(gè)主電池和一個(gè)備用電池。當(dāng)用戶更換主電池的時(shí)候,備用電池的工作是提供電源給RAM以便維持文件在對象存儲(chǔ)區(qū)的存儲(chǔ)。當(dāng)用戶按了重啟鍵之后,WinCE核心就開始尋找在關(guān)閉系統(tǒng)前建立的對象存儲(chǔ)區(qū),如果找到的話就將繼續(xù)使用它。
RAM中的另一個(gè)區(qū)域則用作程序存儲(chǔ)區(qū)。程序存儲(chǔ)區(qū)有點(diǎn)像個(gè)人電腦中的RAM,它為正在運(yùn)行的應(yīng)用程序保存堆和棧的內(nèi)容。在對象存儲(chǔ)區(qū)和程序存儲(chǔ)區(qū)之間的分界線是可以通過移動(dòng)它來改變的,用戶可以在控制面板中找到改變這條分界線的設(shè)置。在可用內(nèi)存降低的(low-memory)條件下,系統(tǒng)將會(huì)彈出對話框詢問用戶是否要將對象存儲(chǔ)區(qū)RAM劃分一些給程序存儲(chǔ)區(qū)RAM以滿足要運(yùn)行的應(yīng)用程序的需求。
關(guān)于ROM
在個(gè)人電腦中,ROM是用來存儲(chǔ)BIOS(基本輸入輸出系統(tǒng))并且只有64-128KB。在WinCE系統(tǒng)中,ROM大小可以從4MB到32MB并且存放整個(gè)操作系統(tǒng)以及和系統(tǒng)捆綁在一起的應(yīng)用程序。在這種情況下,ROM在WinCE系統(tǒng)中就好像一個(gè)只讀的硬盤。
在一個(gè)WinCE系統(tǒng)中,存儲(chǔ)在ROM之上的程序能夠以現(xiàn)場執(zhí)行(Execute in Place,XIP)的方式運(yùn)行。換句話說,程序可以直接從ROM中執(zhí)行而不必先加載到RAM中再執(zhí)行。這種能力對小型系統(tǒng)來說,使之在兩個(gè)方面具有巨大的優(yōu)勢。代碼直接從ROM中執(zhí)行意味著程序代碼不會(huì)占據(jù)更有價(jià)值的RAM。同樣,程序在執(zhí)行前也不必先復(fù)制到RAM中,這樣就只需要很少的時(shí)間來啟動(dòng)一個(gè)應(yīng)用程序。不在ROM中,但是被包含在對象存儲(chǔ)區(qū)(譯者注:上文將對象存儲(chǔ)區(qū)比作永久的RAM磁盤,故此處要說明,只有Intel力推的nor flash memroy類型才能以XIP方式執(zhí)行,ROM其實(shí)也是一種nor flash memory類型)或閃存卡(Flash memory storage card)中的程序?qū)⒉荒芤袁F(xiàn)場方式執(zhí)行,它們將被復(fù)制到RAM中再執(zhí)行。
關(guān)于虛擬內(nèi)存
WinCE 實(shí)現(xiàn)了系統(tǒng)的虛擬內(nèi)存管理,在一個(gè)虛擬內(nèi)存系統(tǒng)中,應(yīng)用程序主要處理這個(gè)分離(譯者注:物理上可能分離,但系統(tǒng)將它們聯(lián)系起來),虛擬的地址空間,因此并不涉及到由硬件管理的物理內(nèi)存。操作系統(tǒng)使用微處理器的內(nèi)存管理單元來處理虛擬地址和物理地址間的實(shí)時(shí)轉(zhuǎn)換。
這種虛擬內(nèi)存方法的優(yōu)勢能從MS-DOS系統(tǒng)復(fù)雜的地址空間看出來。一旦請求的RAM超過最初PC設(shè)計(jì)的640-KB限制,程序設(shè)計(jì)者將不得不作出像擴(kuò)展內(nèi)存一樣的計(jì)劃以便增加可用內(nèi)存的數(shù)量。OS/2 1.x(譯者注:IBM研制的操作系統(tǒng))和Windows 3.0采用了一種基于段(segment-based)的虛擬內(nèi)存系統(tǒng)來解決問題。應(yīng)用程序使用虛擬內(nèi)存不需要知道實(shí)際物理內(nèi)存的位置,只要有內(nèi)存可用就行。在這些系統(tǒng)中,虛擬內(nèi)存以一種段的方式被實(shí)現(xiàn)了,即可移動(dòng)的內(nèi)存塊(譯者注:段其實(shí)就是內(nèi)存分塊)大小從16字節(jié)到64KB。64-KB的限制并不是由于段本身原因,而是由于Intel 80286的特性所致,這就是Windows3.x和OS/21.x的分段式虛擬內(nèi)存系統(tǒng)結(jié)構(gòu)。
分頁存儲(chǔ)
Intel 80386支持的段大小已經(jīng)超過64KB,但是Microsoft和IBM開始設(shè)計(jì)OS/2 2.0,他們選擇了一種不同的虛擬內(nèi)存系統(tǒng),隨后也被386所支持,這就是分頁式虛擬內(nèi)存系統(tǒng)。在一個(gè)分頁存儲(chǔ)的系統(tǒng)中,最小的可被微處理器管理的單元是頁(page)。對于Windows NT和OS/2 2.0系統(tǒng)來說,頁大小都被設(shè)置為386處理器默認(rèn)的4096字節(jié)。當(dāng)一個(gè)應(yīng)用程序存取一個(gè)頁的時(shí)候,微處理器將轉(zhuǎn)換該頁的虛擬內(nèi)存地址到實(shí)際的ROM或RAM中的物理頁(譯者注:這就是實(shí)現(xiàn)了地址映射和轉(zhuǎn)換,將虛擬的和實(shí)際的存儲(chǔ)單元一一對應(yīng)),這一頁同時(shí)被標(biāo)記以便其他程序?qū)υ擁摰脑L問將被排斥。操作系統(tǒng)決定虛擬內(nèi)存頁是否有效,如果有效,將做一個(gè)物理內(nèi)存頁到虛擬頁的映射。
WinCE實(shí)現(xiàn)了一個(gè)和其他Win32操作系統(tǒng)類似的分頁式虛擬內(nèi)存系統(tǒng)。在WinCE中,一頁的大小可以從1024字節(jié)到4096字節(jié),基于微處理器的不同而不同。這和Windows XP不同,Windows XP頁面尺寸是Intel微處理器所支持的4096字節(jié)。對WinCE所支持的CPU類型來說,有486,Intel的Strong-ARM,和Hitachi SH4 都是是用了4096-byte 的頁面。NEC 4100在Windows CE 3.0中使用了4-KB的頁面尺寸但是在較早期的開放式系統(tǒng)版本中使用了1-KB的頁面大小。
虛擬內(nèi)存頁可以處在三種狀態(tài):自由(free),保留(reserved),或被提交(committed),)。自由頁就像它的名稱一樣,自由并且可被分配。保留頁是虛擬地址已經(jīng)被保留,并且不能被操作系統(tǒng)或進(jìn)程中的其他線程重新分配。保留頁不能用在別處,但是它同樣不能被當(dāng)前程序使用,因?yàn)樗鼪]有被映射到物理內(nèi)存。要想執(zhí)行映射,它必須被提交,一個(gè)提交頁能被應(yīng)用程序保留,并且直接映射到物理地址。
所有我剛才講述的內(nèi)容對有經(jīng)驗(yàn)的Win32 程序員們來說是些陳舊的知識(shí)。對Windows CE 程序員來說最重要的東西是學(xué)習(xí)Windows CE 是如何改變這些因素的。當(dāng)Windows CE 實(shí)現(xiàn)了大部分和它的老大哥Win32一樣的內(nèi)存API集的時(shí)候,Windows CE下面的基礎(chǔ)結(jié)構(gòu)將影響到上面的程序。在分開來看Window CE 應(yīng)用程序的內(nèi)存結(jié)構(gòu)之前,讓我們先來看看一些提供系統(tǒng)內(nèi)存全局狀態(tài)的函數(shù)。
查詢系統(tǒng)的內(nèi)存
如果一個(gè)應(yīng)用程序知道系統(tǒng)當(dāng)前的內(nèi)存狀態(tài),它將可以較好地管理可用到的資源。WinCE實(shí)現(xiàn)了Win32的GetSystemInfo和GlobalMemoryStatus函數(shù),GetSystemInfo函數(shù)原型如下:
VOID?GetSystemInfo?(LPSYSTEM_INFO?lpSystemInfo);
它傳遞了一個(gè)指針給SYSTEM_INFO結(jié)構(gòu),定義如下
wProcessorArchitecture參數(shù)表示系統(tǒng)微處理器的架構(gòu)。它的值是定義在Winnt.h中,例如PROCESSOR_ARCHITECTURE_INTEL。Windows CE擴(kuò)展了這些常數(shù),包括PROCESSOR_ARCHITECTURE_ARM,PROCESSOR_ARCHITECTURE_SHx,等等。增加的常數(shù)包括像Win32操作系統(tǒng)支持的網(wǎng)絡(luò)CPU(net CPU)。跳過一些參數(shù),我們看dwProcessorType參數(shù),它來自于特定的微處理器類型。常數(shù)有Hitachi SHx架構(gòu)中的PROCESSOR_HITACHI_SH3和PROCESSOR_HITACHI_SH4。最后兩個(gè)參數(shù),wProcessorLevel和wProcessorRevision,指明了CPU類型的特征。wProcessorLevel參數(shù)類似于dwProcessorType參數(shù),它一個(gè)指定的微處理器系列中被定義了,dwProcessorRevision告訴你模式(model)和芯片的步進(jìn)級(jí)別(stepping level)。
typedef?struct?{
WORD?wProcessorArchitecture; WORD?wReserved; DWORD??dwPageSize; LPVOID?lpMinimumApplicationAddress; LPVOID?lpMaximumApplicationAddress; DWORD??dwActiveProcessorMask; DWORD??dwNumberOfProcessors; DWORD??dwProcessorType; DWORD??dwAllocationGranularity; WORD??wProcessorLevel;? WORD??wProcessorRevision; }?SYSTEM_INFO; ?
dwPageSize參數(shù)說明了微處理器頁面的大小,以字節(jié)為單位。知道這個(gè)值,將會(huì)在你直接處理虛擬內(nèi)存API的時(shí)候帶來方便,在此我只作簡短說明。lpMinimumApplication-Address和lpMaximumApplicationAddress參數(shù)說明了應(yīng)用程序可用到的最小和最大的虛擬內(nèi)存地址。dwActiveProcessorMask和dwNumberOfProcessors參數(shù)顯示被Window XP系統(tǒng)支持的多個(gè)處理器數(shù)量。因?yàn)閃indows CE 只支持一個(gè)處理器,所以你可以忽略這個(gè)參數(shù)。dwAllocationGranularity參數(shù)說明了一個(gè)完整的虛擬內(nèi)存區(qū)域分配的界限。像Windows XP,Windows CE 規(guī)定虛擬區(qū)為64-KB的界限(譯者注:作者此處64-KB的意思是即使你只分配一個(gè)字節(jié)的內(nèi)存,系統(tǒng)也將會(huì)保留64-KB的虛擬地址空間給它,這個(gè)值一般是由硬件代碼實(shí)現(xiàn)的,但是不同硬件可能不同值)。
第二個(gè)方便的檢測系統(tǒng)狀態(tài)的函數(shù)如下:
void?GlobalMemoryStatus(LPMEMORYSTATUS?lpmst);
它返回一個(gè)MEMORYSTATUS結(jié)構(gòu),定義為
typedef?struct?{?
DWORD?dwLength;? DWORD?dwMemoryLoad;? DWORD?dwTotalPhys;? DWORD?dwAvailPhys;? DWORD?dwTotalPageFile;? DWORD?dwAvailPageFile;? DWORD?dwTotalVirtual;? DWORD?dwAvailVirtual;? }?MEMORYSTATUS; ?
??? dwLength參數(shù)在調(diào)用這個(gè)函數(shù)之前必須初始化。dwMemoryLoad參數(shù)是一個(gè)不確定的值;這是一個(gè)可用的一般性的參數(shù)指示了當(dāng)前系統(tǒng)的內(nèi)存使用情況(譯者注:該參數(shù)是一個(gè)近似的百分比值,指明了物理內(nèi)存的使用情況)。dwTotalPhys和dwAvailPhys參數(shù)指明了RAM有多少頁被分配給了程序存儲(chǔ)區(qū)RAM,和還有多少可用(譯者注:實(shí)際上是以字節(jié)為單位)。這些值不包括分配對象存儲(chǔ)區(qū)的RAM。
dwTotalPageFile和dwAvailPageFile參數(shù)在Windows XP下和Windows Me下指明了當(dāng)前頁面文件(paging file)的狀態(tài)。因?yàn)閃indows CE不支持頁面文件,所以這些參數(shù)總是0。dwTotalVirtual和dwAvailVirtual參數(shù)指明了總共的和可用的可被應(yīng)用程序存取的虛擬內(nèi)存頁的數(shù)量(譯者注:參數(shù)都是以字節(jié)為單位,而不是指頁數(shù),dwAvailVirtual是指未保留和未提交的內(nèi)存)。
?????? 通過GlobalMemoryStatus返回的信息可以驗(yàn)證Windows CE內(nèi)存結(jié)構(gòu),通過在有32MBRAM的HP iPaq Pocket PC上調(diào)用函數(shù),返回值如下:
dwMemoryLoad???????0x18??????????(24)
dwTotalPhys????????0x011ac000????(18,530,304) dwAvailPhys????????0x00B66000????(11,952,128) dwTotalPageFile????0 dwAvailPageFile????0 dwTotalVirtual?????0x02000000????(33,554,432) dwAvailVirtual?????0x01e10000????(31,522,816) ?
?
dwTotalPhys參數(shù)表明了系統(tǒng)的32MB RAM,分配了18.5MB給程序存儲(chǔ)區(qū)RAM,其中12MB仍然可用。注意這對應(yīng)用程序來說,并不是通過這次調(diào)用,就知道了另外14MB的RAM是分配給對象存儲(chǔ)區(qū)的。要檢測分配給對象存儲(chǔ)區(qū)的RAM的大小,要使用GetStoreInformation。
dwTotalPageFile和dwAvailPageFile參數(shù)是0,表明頁面文件不被Windows CE所支持。dwTotalVirtual參數(shù)十分有趣,因?yàn)樗@示了Windows CE 強(qiáng)制給程序的32-MB的虛擬內(nèi)存限制。其間,dwAvailVirtual參數(shù)顯示了只使用了32MB虛擬內(nèi)存的一小部分(譯者注:即33,554,432-31,522,816=2,031,616)。
應(yīng)用程序的地址空間
盡管和Windows XP的應(yīng)用程序設(shè)計(jì)類似,但Windows CE應(yīng)用程序地址空間有一個(gè)巨大的不同影響到應(yīng)用程序。在Windows CE之下,一個(gè)應(yīng)用程序被限制在虛擬內(nèi)存空間中它自己的32MB slot(槽)和 32MB 的slot 1中,slot 1用來加載基于XIP的DLL(譯者注:Windows CE將虛擬地址空間分為33個(gè)slot,每個(gè)slot 32MB,序號(hào)從0-32 ,附插圖c-1,c-2)。當(dāng)系統(tǒng)只有4MB RAM的時(shí)候,分配給應(yīng)用程序32MB的虛擬地址空間看起來是比較合理的,Win32的程序員在使用這個(gè)2-GB的虛擬地址空間的時(shí)候,必須記住對Windows CE應(yīng)用程序的虛擬地址空間限制。
圖7-1展示了一個(gè)應(yīng)用程序的64-MB虛擬地址空間,其中包括32MB用于XIP的DLL空間。
圖7-1???? Windows CE的內(nèi)存映射圖
要注意的是應(yīng)用程序是以一個(gè)64-KB的內(nèi)存區(qū)域開始從0x10000映射,記得最低的64KB地址空間是由Windows為所有應(yīng)用程序保留的。文件映象包括代碼,靜態(tài)數(shù)據(jù)段和資源段。在實(shí)際過程中,當(dāng)應(yīng)用程序啟動(dòng)時(shí)代碼頁(code pages)不會(huì)載入進(jìn)來,只有在需要該頁面被載入的時(shí)候,代碼才被載入進(jìn)來。
只讀靜態(tài)數(shù)據(jù)段(read-only static data segment)和可讀寫靜態(tài)數(shù)據(jù)區(qū)(read/write static data areas)只占很少的頁面。這些段都是排列在一起的。如同代碼一樣,只有當(dāng)這些數(shù)據(jù)段被應(yīng)用程序讀或者寫的時(shí)候才會(huì)提交給RAM。應(yīng)用程序的資源將被載入到一些分離的頁面中,這些資源是只讀的,并且只有當(dāng)它們被應(yīng)用程序獲取的時(shí)候才會(huì)分頁進(jìn)入RAM。
應(yīng)用程序的棧(stack)被映射到資源段之上。棧的段位置很容易被找到,因?yàn)樗峤坏捻摼驮诒槐A舻膮^(qū)域的尾部。棧的表現(xiàn)是從高地址到低地址增長(譯者注:即從高到低填滿地址)。如果該應(yīng)用程序有超過一個(gè)線程,那么應(yīng)用程序的地址空間就會(huì)保留超過一個(gè)的棧的段。
緊接著棧的就是本地堆(local heap)。引導(dǎo)程序保留了大量的頁,大約有幾百K交給heap來使用,但是只提交滿足malloc,new,LocalAlloc函數(shù)調(diào)用分配的內(nèi)存(譯者注:這里是說,被分配多少內(nèi)存才可以提交多少內(nèi)存,沒被分配的不能用作提交)。從本地堆的保留頁尾部到non-XIP DLL開始的部分剩余保留頁面將被映射為自由的保留空間,如果RAM允許,應(yīng)用程序可以提交這些保留頁。Non-XIP DLLs 就是不能被在ROM中現(xiàn)場執(zhí)行的DLL將被從上至下載入到32MB的地址空間。Non-XIP DLLs 包括那些被壓縮存儲(chǔ)在ROM中的DLL。被壓縮的ROM 中的文件在被載入到RAM中執(zhí)行前必須先解壓縮。
被保留給XIP DLLs的32MB 應(yīng)用程序地址空間的較高位置。Windows CE 映射這些XIP DLLs的代碼進(jìn)入這個(gè)空間(譯者注:即較高空間),而可讀寫段被映射進(jìn)較低位置。從Windows CE 4.2開始,在ROM中的純資源的DLL將被載入到應(yīng)用程序64MB空間之外的虛擬內(nèi)存空間。
腳注
在PocketPC這樣的移動(dòng)式系統(tǒng)中,當(dāng)用戶按下關(guān)閉按鈕時(shí)系統(tǒng)將不會(huì)被真正的關(guān)閉,系統(tǒng)進(jìn)入一種低功耗的掛起狀態(tài)。
內(nèi)存分配的不同類型
一個(gè)Windows CE 應(yīng)用程序有許多不同的內(nèi)存分配方式。在內(nèi)存食物鏈的底端是Virtualxxx 函數(shù),它們直接保留,提交和釋放(free)虛擬內(nèi)存頁。接下來的是堆(heap) API。堆是系統(tǒng)為應(yīng)用程序保留的內(nèi)存區(qū)域。堆有兩種風(fēng)味:當(dāng)應(yīng)用程序啟動(dòng)時(shí)自動(dòng)默認(rèn)分配的本地堆(local heap),以及能夠由程序手動(dòng)創(chuàng)建的分離堆(separate heap)。在堆API之后是靜態(tài)數(shù)據(jù),數(shù)據(jù)塊是被編譯器定義好的或者由程序手動(dòng)創(chuàng)建的。最后,我們來看棧,這是程序?yàn)楹瘮?shù)存儲(chǔ)變量的區(qū)域。
一個(gè)Windows CE不支持的Win32 內(nèi)存API是全局堆(global heap)。全局堆API包括Global-Alloc,GlobalFree和GlobalRealloc,將不會(huì)出現(xiàn)在Windows CE中(譯者注:很奇怪,我在Windows CE 中仍然可以使用這幾個(gè)API,并且工作正常,好像Microsoft并沒有把它們完全去掉)。全局堆只是從Windows 3.x的Win16 時(shí)期繼承而來。在Win32中,全部和本地的堆很類似,全局內(nèi)存一個(gè)獨(dú)特用法是,為剪貼板的數(shù)據(jù)分配內(nèi)存,在Windows CE中已經(jīng)被本地堆替代并加上了句柄。
在Windows CE中最小化內(nèi)存使用的關(guān)鍵是選擇與內(nèi)存塊使用模型相匹配的恰當(dāng)?shù)膬?nèi)存分配策略。我將回顧一下這些內(nèi)存類型然后講述Windows CE應(yīng)用程序中的最小化內(nèi)存使用策略。
虛擬內(nèi)存
虛擬內(nèi)存是內(nèi)存類型中最基礎(chǔ)的。系統(tǒng)調(diào)用虛擬內(nèi)存API來為其他類型內(nèi)存分配內(nèi)存。包括堆和棧。虛擬內(nèi)存API,包括VirtualAlloc,VirtualFree和VirtualReSize函數(shù),這些可以直接操作應(yīng)用程序虛擬內(nèi)存空間的虛擬內(nèi)存頁面。頁面可以保留,提交給物理內(nèi)存,或使用這些函數(shù)釋放。
分配虛擬內(nèi)存
?????? 分配和保留虛擬內(nèi)存是同過這個(gè)函數(shù)完成的:
LPVOID?VirtualAlloc?(LPVOID?lpAddress,?DWORD?dwSize,
DWORD?flAllocationType, DWORD?flProtect); ?
VirtualAlloc 的第一個(gè)參數(shù)是要分配內(nèi)存區(qū)域的地址。當(dāng)你使用VirtualAlloc來提交一塊以前保留的內(nèi)存塊的時(shí)候,lpAddress參數(shù)可以用來識(shí)別以前保留的內(nèi)存塊。如果這個(gè)參數(shù)是NULL,系統(tǒng)將會(huì)決定分配內(nèi)存區(qū)域的位置,并且圍繞64-KB的范圍(譯者注:就是前面說提及的最小內(nèi)存分配尺寸)。第二個(gè)參數(shù)是dwSize,要分配或者保留的區(qū)域的大小。這個(gè)參數(shù)以字節(jié)為單位,而不是頁,系統(tǒng)會(huì)根據(jù)這個(gè)大小一直分配到下頁的邊界。
flAllocationType參數(shù)指定了分配的類型,你可以指定或者合并以下標(biāo)志:MEM_COMMIT,MEM_AUTO_COMMIT,MEM_RESERVE和MEM_TOP_DOWN。MEM_COMMIT標(biāo)志分配程序使用的內(nèi)存,MEM_RESERVE保留虛擬地址空間以便以后提交。保留的頁不能存取直到調(diào)用VirtualAlloc的時(shí)候再次指定了MEM_COMMIT標(biāo)志。第三個(gè)標(biāo)志,MEM_TOP_DOWN,告訴系統(tǒng)從最高可允許的虛擬地址開始映射應(yīng)用程序。
The MEM_AUTO_COMMIT標(biāo)志是唯一一個(gè)Windows CE最方便的標(biāo)志,當(dāng)這個(gè)參數(shù)被指定了之后,內(nèi)存塊立即被保留,當(dāng)其中的頁被第一次存取的時(shí)候,系統(tǒng)將自動(dòng)提交該頁。這允許你分配大塊的虛擬內(nèi)存而不需要顧及系統(tǒng)和實(shí)際RAM分配直到當(dāng)前頁被第一次使用。自動(dòng)提交內(nèi)存的缺點(diǎn)是,物理RAM需要退回當(dāng)頁面被第一次訪問時(shí)可能不可用的頁面。在這種情形下,系統(tǒng)將產(chǎn)生一個(gè)異常(exception)(譯者注:可能會(huì)出現(xiàn)因?yàn)闊o法訪問而出錯(cuò))。
VirtualAlloc可以通過并行多次調(diào)用提交一個(gè)區(qū)域的部分或全部來保留一個(gè)大的內(nèi)存區(qū)域。多重調(diào)用提交同一塊區(qū)域不會(huì)引起失敗。這使得一個(gè)應(yīng)用程序保留內(nèi)存后可以隨意提交將被寫的頁。當(dāng)這種方式不在有效的時(shí)候,它會(huì)釋放應(yīng)用程序通過檢測被保留頁的狀態(tài)看它是否在提交調(diào)用之前已經(jīng)被提交。
flProtect參數(shù)指定了被分配區(qū)域的訪問保護(hù)方式。這些不同的標(biāo)志被總結(jié)在下面的列表中:
PAGE_READONLY
該區(qū)域?yàn)橹蛔x。如果應(yīng)用程序試圖訪問區(qū)域中的頁的時(shí)候,將會(huì)被拒絕訪問。
PAGE_READWRITE
區(qū)域可被應(yīng)用程序讀寫。
PAGE_EXECUTE
區(qū)域包含可被系統(tǒng)執(zhí)行的代碼。試圖讀寫該區(qū)域的操作將被拒絕。
PAGE_EXECUTE_READ
區(qū)域包含可執(zhí)行代碼,應(yīng)用程序可以讀該區(qū)域。
PAGE_EXECUTE_READWRITE
區(qū)域包含可執(zhí)行代碼,應(yīng)用程序可以讀寫該區(qū)域。
PAGE_GUARD
區(qū)域第一次被訪問時(shí)進(jìn)入一個(gè)STATUS_GUARD_PAGE異常,這個(gè)標(biāo)志要和其他保護(hù)標(biāo)志合并使用,表明區(qū)域被第一次訪問的權(quán)限。
PAGE_NOACCESS
任何訪問該區(qū)域的操作將被拒絕。
PAGE_NOCACHE
RAM中的頁映射到該區(qū)域時(shí)將不會(huì)被微處理器緩存(cached)。
PAGE_GUARD和PAGE_NOCHACHE標(biāo)志可以和其他標(biāo)志合并使用以進(jìn)一步指定頁的特征。PAGE_GUARD標(biāo)志指定了一個(gè)防護(hù)頁(guard page),即當(dāng)一個(gè)頁被提交時(shí)會(huì)因第一次被訪問而產(chǎn)生一個(gè)one-shot異常,接著取得指定的訪問權(quán)限。PAGE_NOCACHE標(biāo)志可以和其他標(biāo)志合并使用以進(jìn)一步指定頁的特征。PAGE_GUARD標(biāo)志指定了一個(gè)防護(hù)頁(guard page),即當(dāng)一個(gè)頁被提交時(shí)會(huì)因第一次被訪問而產(chǎn)生一個(gè)one-shot異常,接著取得指定的訪問權(quán)限。PAGE_NOCACHE防止當(dāng)它映射到虛擬頁的時(shí)候被微處理器緩存。這個(gè)標(biāo)志方便設(shè)備驅(qū)動(dòng)使用直接內(nèi)存訪問方式(DMA)來共享內(nèi)存塊。
區(qū)域和頁 在我繼續(xù)談?wù)撎摂M內(nèi)存API之前,我需要說明一個(gè)比較細(xì)微的差異。虛擬內(nèi)存在區(qū)域內(nèi)被保留是以64KB為基礎(chǔ)的。在區(qū)域內(nèi)的頁面能夠一頁一頁地被提交(譯者注:前面說到在Windows CE中每頁是4096字節(jié)或1024字節(jié))。你可以直接提交一頁或者幾頁而不是保留區(qū)域的全部頁。但是對頁或幾頁來說,直接提交的仍是以64-KB為單位(譯者注:可以直到被提交的頁數(shù)量足夠填滿64KB才真正提交),因?yàn)檫@個(gè)原因,最好保留一塊64-KB的虛擬內(nèi)存,然后提交那些需要的頁到區(qū)域里。
?????? 因?yàn)閷γ總€(gè)進(jìn)程32MB虛擬內(nèi)存地址空間的限制,這就有了一個(gè)最大值 32MB/64KB-1=511,這是虛擬內(nèi)存在內(nèi)存溢出前能被保留的最大值。接下來,有個(gè)例子,代碼段如下:
#define?PAGESIZE?1024???//?Assume?we're?on?a?1-KB?page?machine
for?(i?=?0;?i?<?512;?i++)? pMem[i]?=?VirtualAlloc?(NULL,?PAGESIZE,?MEM_RESERVE?│?MEM_COMMIT,PAGE_READWRITE); ?
代碼分配512個(gè)單頁的虛擬內(nèi)存。甚至你系統(tǒng)還有一半的可用RAM,VirtualAlloc也會(huì)在完成分配前失敗。因?yàn)樗倪\(yùn)行已經(jīng)超出了應(yīng)用程序的虛擬地址空間。發(fā)生這種情況是因?yàn)槊?-KB的塊要占用64-KB的空間,接下來應(yīng)用程序的代碼,棧,和本地堆也要映射到同樣的32-MB虛擬地址空間,可用的虛擬分配區(qū)域通常不超過475個(gè)。
???? 一個(gè)比較好的分配512塊特殊內(nèi)存的方法是這樣做:
#define?PAGESIZE?1024???//?Assume?we're?on?a?1-KB?page?machine.
//?Reserve?a?region?first. pMemBase?=?VirtualAlloc?(NULL,?PAGESIZE?*?512,?MEM_RESERVE, PAGE_NOACCESS); for?(i?=?0;?i?<?512;?i++)? pMem[i]?=?VirtualAlloc?(pMemBase?+?(i*PAGESIZE),?PAGESIZE,? MEM_COMMIT,?PAGE_READWRITE); ?
???? 代碼首先保留了一塊區(qū)域,頁面將在以后被提交。因?yàn)閰^(qū)域已經(jīng)被先保留了,提交頁就不受64-KB限制(譯者注:只有保留頁最小值受64KB限制),等等,如果你系統(tǒng)中有512KB的可用內(nèi)存,分配將會(huì)成功。 盡管我剛才給你看的是一個(gè)人為的例子(還有比直接分配虛擬內(nèi)存更好的方法來分配1-KB的內(nèi)存塊),這中內(nèi)存分配方法驗(yàn)證了一個(gè)重要的不同(對于其他Windows系統(tǒng))。在桌面版本的Windows中,工作中的應(yīng)用程序有一個(gè)完全的2-GB的虛擬地址空間。在Windows CE中,一個(gè)程序員必須明白每個(gè)應(yīng)用程序只被保留了較小的32-MB虛擬地址空間。
釋放虛擬內(nèi)存 ?????? 你可以通過調(diào)用VirtualFree來取消提交,或釋放虛擬內(nèi)存。從物理RAM頁中取消提交或者取消映射,但是保持頁被保留的狀態(tài)。函數(shù)原型如下:
BOOL?VirtualFree?(LPVOID?lpAddress,?DWORD?dwSize, ??????????????????DWORD?dwFreeType);
?
??? lpAddress參數(shù)是一個(gè)指針,指向要被釋放或取消提交的虛擬內(nèi)存的區(qū)域。dwSize參數(shù)指明要取消提交區(qū)域的大小,以字節(jié)為單位。如果區(qū)域要被釋放,這個(gè)值必須是0,dwFreeType參數(shù)包含了操作類型標(biāo)志,MEM_DECOMMIT標(biāo)志指定了區(qū)域?qū)⒈蝗∠峤坏侨员槐A?#xff0c;MEM_RELEASE標(biāo)志說明區(qū)域要取消提交并且釋放。 在區(qū)域中的所有的頁通過VirtualFree被釋放必須處在同樣的情況下。更確切地說,區(qū)域中的全部頁要被釋放,那這些頁要么都是被提交的頁,要么都是被保留的頁。如果有些頁被提交,有些頁被保留,那么VirtualFree函數(shù)調(diào)用就會(huì)失敗。
改變和查詢權(quán)限 你可以通過調(diào)用VirtualProtect來修改最初通過VirtualAlloc指定的虛擬內(nèi)存區(qū)域的訪問權(quán)限。這個(gè)函數(shù)只能改變被提交的頁的訪問權(quán)限。函數(shù)的原型如下: BOOL?VirtualProtect?(LPVOID?lpAddress,?DWORD?dwSize,? DWORD?flNewProtect,?PDWORD?lpflOldProtect); 開始的兩個(gè)參數(shù)lpAddress和dwSize,指定了函數(shù)作用的塊的大小。flNewProtect參數(shù)包含區(qū)域的新的保護(hù)標(biāo)志。這些標(biāo)志和我前面提到的VirtualAlloc函數(shù)使用的一樣。lpflOldProtect參數(shù)指向一個(gè)DWORD,將返回舊的保護(hù)標(biāo)志(譯者注:如果此處為NULL或指向一個(gè)無效的變量,函數(shù)將會(huì)失敗)。 當(dāng)前區(qū)域的保護(hù)權(quán)限可用通過下面的調(diào)用查詢: DWORD?VirtualQuery?(LPCVOID?lpAddress,? PMEMORY_BASIC_INFORMATION?lpBuffer, DWORD?dwLength); lpAddress參數(shù)包含區(qū)域開始查詢的地址。lpBuffer指針指向我很快就要提到的一個(gè)PMEMORY_BASIC_INFORMATION結(jié)構(gòu)。第三個(gè)參數(shù)dwLength,必須包含PMEMORY_BASIC_INFORMATION結(jié)構(gòu)的大小。
?????? PMEMORY_BASIC_INFORMATION結(jié)構(gòu)被定義如下:
typedef?struct?_MEMORY_BASIC_INFORMATION?{?
PVOID?BaseAddress;? PVOID?AllocationBase;? DWORD?AllocationProtect;? DWORD?RegionSize;? DWORD?State;? DWORD?Protect;? DWORD?Type;? }?MEMORY_BASIC_INFORMATION; ?
MEMORY_BASIC_INFORMATION 結(jié)構(gòu)的第一個(gè)字段是BaseAddress,是傳遞給VirtualQuery函數(shù)的一個(gè)地址。AllocationBase字段包含使用 VirtualAlloc函數(shù)分配的區(qū)域的基地址,AllocationProtect字段包含區(qū)域原來被分配時(shí)的保護(hù)屬性。RegionSize字段包含從傳遞給VirtualQuery的指針開始到一系列具有相同屬性的頁為結(jié)尾的區(qū)域大小(譯者注:這里是從基地址開始)。State字段包含區(qū)域中頁的狀態(tài)-自由,保留,提交。Protect字段可以包含MEM_PRIVATE標(biāo)志,指明該區(qū)域包含應(yīng)用程序私有的數(shù)據(jù);MEM_MAPPED指明該區(qū)域被映射為一個(gè)內(nèi)存映射文件;MEM_IMAGE指明該區(qū)域被映射為一個(gè)EXE或DLL模塊。 理解VirtualQuery最好的方式是看例子,比方說一個(gè)應(yīng)用程序保留了16,384字節(jié)(在以頁面大小為1-KB的機(jī)器中占16頁)。系統(tǒng)從地址0xA0000開始保留這16-KB的塊。后來應(yīng)用程序從最初的區(qū)域中提交了從第2048字節(jié)(2頁)開始的9216字節(jié)(9頁)。圖7-2顯示了這個(gè)假設(shè)的情況。 圖7-2被保留的區(qū)域有9頁被提交
?????? 如果一個(gè)對VirtualQuery的調(diào)用中,lpAddress指向第四頁的區(qū)域(地址0xA1000),返回值如下:
BaseAddress??????????0xA1000
AllocationBase???????0xA0000 AllocationProtect????PAGE_NOACCESS RegionSize???????????0x1C00????(7,168?bytes?or?7?pages) State????????????????MEM_COMMIT Protect??????????????PAGE_READWRITE Type?????????????????MEM_PRIVATE ?
BaseAddress 字段包含傳遞給VirtualQuery的地址,值為0xA1000,在最初的區(qū)域中是第4096字節(jié)。AllocationBase字段包含最初區(qū)域的地址。當(dāng)AllocationProtect設(shè)為PAGE_NOACCESS時(shí),指明區(qū)域是最初被保留的,而不是直接提交。RegionSize字段包含傳遞給VirtualQuery的指針0xA1000開始,到被提交的頁結(jié)束地址0xA2C00的字節(jié)數(shù)。State和Protect字段包含的標(biāo)志表明當(dāng)前的頁狀態(tài)。Type字節(jié)表明區(qū)域被應(yīng)用程序分配給自己使用。
堆 很明顯,以頁為單位分配內(nèi)存對應(yīng)用程序來說效率是很低的。為了優(yōu)化內(nèi)存的使用,應(yīng)用程序需要以字節(jié)為單位分配和釋放內(nèi)存,或者至少以每8字節(jié)為單位。系統(tǒng)通過堆來實(shí)現(xiàn)這種分配方式。使用堆可以免去處理由Windows CE支持的不同微處理器的不同頁面大小。一個(gè)應(yīng)用程序可以簡單地在堆中分配一塊內(nèi)存,由系統(tǒng)來處理分配需要的頁數(shù)。 就像我前面提到的,堆是系統(tǒng)為應(yīng)用程序保留的虛擬內(nèi)存區(qū)域。系統(tǒng)提供大量的函數(shù)來在堆中分配和釋放內(nèi)存塊,并且間隔比頁要小(譯者注:例如每頁大小為4KB,而堆分配可以字節(jié)為單位)。當(dāng)內(nèi)存由應(yīng)用程序的堆分配時(shí),系統(tǒng)自動(dòng)分配調(diào)整堆大小來滿足需要,當(dāng)堆中的內(nèi)存塊被釋放時(shí),系統(tǒng)會(huì)查看是否整頁被釋放,如果是的話,那么該頁將被回收。 不同于Windows XP,Windows CE只支持在堆中分配固定(fixed)的塊。這簡化了內(nèi)存塊在堆中的處理,但是這使得堆在分配和釋放一段時(shí)間后會(huì)產(chǎn)生碎片。當(dāng)堆里已經(jīng)清空的時(shí)候,仍然會(huì)占用大量的虛擬內(nèi)存頁,因?yàn)橄到y(tǒng)不能在堆中內(nèi)存頁沒有完全釋放的時(shí)候回收這些頁(譯者注:因?yàn)槎岩宰止?jié)為單位,一頁中可能有的塊需要被釋放,其他的塊不需要,所以整頁都不會(huì)被釋放)。 當(dāng)應(yīng)用程序啟動(dòng)的時(shí)候,每個(gè)程序都會(huì)有一個(gè)由系統(tǒng)創(chuàng)建的默認(rèn)或本地堆。本地堆中的內(nèi)存塊,可以通過LocalAlloc,LocalFree和LocalRealloc來分配,釋放和改變大小。一個(gè)應(yīng)用程序也可以建立分離堆。這些堆和本地堆有著相同的屬性,但是是通過一組Heapxxxx函數(shù)來管理的。
本地堆 在默認(rèn)情況下,Windows CE最初會(huì)保留192,512字節(jié)給本地堆,但是只提交被分配的頁。如果應(yīng)用程序在本地堆中分配了超過188KB,系統(tǒng)將會(huì)分配更多的空間給本地堆。增加堆大小將需要一個(gè)分離的,不連續(xù)的保留地址空間作為堆的附加空間。應(yīng)用程序不應(yīng)該假設(shè)本地堆被包含在一塊虛擬地址空間里。因?yàn)閃indows CE 的堆只支持固定的塊,Windows CE執(zhí)行的只是Win32本地堆函數(shù)的子集,提供必要的分配,改變大小,釋放固定的本地堆內(nèi)存塊。
在本地堆中分配內(nèi)存 你可以通過一下調(diào)用在本地堆中分配一塊內(nèi)存: HLOCAL?LocalAlloc?(UINT?uFlags,?UINT?uBytes); 調(diào)用返回一個(gè)HLOCAL,這是本地內(nèi)存塊的句柄,但是由于內(nèi)存塊是固定分配的,所以返回值可以被簡單地看作是一個(gè)指向塊的指針。 uFlags參數(shù)描述了內(nèi)存塊的特征。標(biāo)志由于Windows CE被限制固定分配操作,只支持以下內(nèi)存: LMEM_FIXED 在本地堆中分配一個(gè)固定內(nèi)存塊,因?yàn)楸镜囟逊峙湟呀?jīng)固定,所以是多余的。 LMEM_ZEROINIT 初始化內(nèi)存內(nèi)容為0。 LPTR 合并LMEM_FIXED和LMEM_ZEROINIT標(biāo)志。 uBytes參數(shù)指定了要分配的內(nèi)存塊的大小,以字節(jié)為單位。塊大小要補(bǔ)齊,但是只針對后面8字節(jié)范圍。
釋放本地堆的內(nèi)存 你可以通過以下調(diào)用釋放內(nèi)存塊: HLOCAL?LocalFree?(HLOCAL?hMem); 函數(shù)需要本地堆內(nèi)存句柄,成功會(huì)返回NULL。如果調(diào)用失敗,會(huì)返回內(nèi)存塊的句柄。
改變和查詢本地堆內(nèi)存的大小 你可以通過調(diào)用改變本地堆的分配: HLOCAL?LocalReAlloc?(HLOCAL?hMem,?UINT?uBytes,?UINT?uFlag); hMem參數(shù)是一個(gè)由LocalAlloc返回的指針(句柄)。uBytes參數(shù)是內(nèi)存塊的新大小。uFlag參數(shù)包含給新內(nèi)存塊的標(biāo)志。在Windows CE中,有兩個(gè)新標(biāo)志與之相關(guān),LMEM_ZEROINIT和LMEM_MOVEABLE。LMEM_ZEROINIT表示調(diào)用函數(shù)后內(nèi)存塊中新增加的區(qū)域被初始化為0。LMEM_MOVEABLE標(biāo)志告訴Windows,當(dāng)內(nèi)存塊增加后,沒有合適的空間容納內(nèi)存塊時(shí),函數(shù)可以立即移動(dòng)內(nèi)存塊。如果沒有這個(gè)標(biāo)志,當(dāng)你沒有合適的空間來滿足需要的時(shí)候,LocalRealloc將會(huì)出現(xiàn)out-of-memory的錯(cuò)誤而失敗,如果你指定了LMEM_MOVEABLE標(biāo)志,調(diào)用將會(huì)返回句柄(實(shí)際是指向內(nèi)存塊的指針)。 內(nèi)存塊的大小可以通過以下調(diào)用查詢: UINT?LocalSize?(HLOCAL?hMem); 返回內(nèi)存塊最少需要的內(nèi)存大小。像我前面提到的,Windows CE本地堆自動(dòng)以8個(gè)字節(jié)來補(bǔ)齊(譯者注:就是分配1字節(jié)要占8字節(jié))。
分離堆 為了避免本地堆的碎片,并且如果你要分配連續(xù)的內(nèi)存塊,較好的辦法是建立分離堆,但將花費(fèi)一定的時(shí)間。一個(gè)例子就是,文本編輯器為要編輯的文件建立多個(gè)分離堆。當(dāng)文件被打開或者關(guān)閉的時(shí)候,堆隨之建立和銷毀。 在Windows CE下的堆和Windows XP下有著同樣的API。唯一值得注意的不同是缺少HEAP_GENERATE- _EXCEPTIONS標(biāo)志。在Windows XP下,該標(biāo)志表示系統(tǒng)在分配請求不合適的時(shí)候產(chǎn)生一個(gè)異常。
建立一個(gè)分離堆 你可以通過以下調(diào)用建立一個(gè)分離堆。 HANDLE?HeapCreate?(DWORD?flOptions,?DWORD?dwInitialSize, DWORD?dwMaximumSize); 在Windows CE中,第一個(gè)參數(shù)flOptions必須為空或包含HEAP_NO_SERIALIZE標(biāo)志。默認(rèn)情況下,Windows堆管理程序防止一個(gè)進(jìn)程中的兩個(gè)線程在同意時(shí)間訪問堆。這個(gè)串行參數(shù)防止系統(tǒng)用來跟蹤堆中內(nèi)存塊分配的堆指針被破壞。在其他版本的Windows中,當(dāng)你不需要這種保護(hù)時(shí)可以使用HEAP_NO_SERIALIZE標(biāo)志。在Windows CE中,該標(biāo)志是為了兼容性而提供的,所有的堆訪問都是串行的(譯者注:串行即非并行,只能依次訪問)。 其他兩個(gè)參數(shù),dwInitialSize和dwMaximumSize,指定了最初的大小和預(yù)期的堆最大值。dwMaximumSize的值確定虛擬內(nèi)存空間保留給堆多少頁。如果你想讓W(xué)indows來決定有多少頁可以保留,你可以把這個(gè)參數(shù)設(shè)為0。默認(rèn)一個(gè)堆的大小是188KB,dwInitialSize參數(shù)決定了有多少這些保留的頁將被提交。如果該參數(shù)為0,表示堆將一頁一頁提交。
在分離堆中分配內(nèi)存 你可以通過以下調(diào)用分配內(nèi)存 LPVOID?HeapAlloc?(HANDLE?hHeap,?DWORD?dwFlags,?DWORD?dwBytes); 注意,返回值是一個(gè)指針,而不是和LocalAlloc函數(shù)一樣的句柄。分離堆總是分配固定的內(nèi)存塊,甚至在Windows XP和Windows Me中也是一樣。第一個(gè)參數(shù)是通過HeapCreate調(diào)用返回的句柄。dwFlags參數(shù)可以是兩個(gè)自說明的(self-explanatory)標(biāo)志之一HEAP_NO_SERIALIZE和 HEAP_ZERO_MEMORY。最后一個(gè)參數(shù)dwBytes指定了要分配的內(nèi)存塊字節(jié)數(shù)。大小要和DWORD補(bǔ)齊。
釋放分離堆中的內(nèi)存 你可以通過以下調(diào)用釋放內(nèi)存塊: BOOL?HeapFree?(HANDLE?hHeap,?DWORD?dwFlags,?LPVOID?lpMem); dwFlags參數(shù)唯一的標(biāo)志是HEAP_NO_SERIALIZE,當(dāng)hHeap包含堆句柄時(shí),lpMem參數(shù)指向要釋放的內(nèi)存塊。 改變和查詢分離堆中內(nèi)存的大小: 你可以通過以下調(diào)用改變堆大小。 ??? LPVOID?HeapReAlloc?(HANDLE?hHeap,?DWORD?dwFlags,?LPVOID?lpMem, DWORD?dwBytes); dwFlags參數(shù)包含三種標(biāo)志的組合:HEAP_NO_SERIALIZE,HEAP_REALLOC_IN_PLACE_ONLY和HEAP_ZERO_ MEMORY。其中較新的標(biāo)志是HEAP_REALLOC_IN_PLACE_ONLY,這個(gè)參數(shù)告訴堆的管理者,找不到要分配的塊的空間,重分配操作失敗。這個(gè)標(biāo)志方便的地方在于當(dāng)你有了一些指向內(nèi)存數(shù)據(jù)塊的指針,并且你不想改變內(nèi)存塊。lpMem參數(shù)是一個(gè)指向要改變大小的內(nèi)存塊的指針,dwBytes參數(shù)是被請求的新內(nèi)存塊的大小。注意,HeapReAlloc中HEAP_REALLOC_IN_PLACE_ONLY標(biāo)志提供和LocalReAlloc中LMEM_MOVEABLE相反的作用。HEAP_REALLOC_IN_PLACE_ONLY防止在分離堆中對內(nèi)存塊默認(rèn)的移動(dòng)操作。而LMEM_MOVEABLE允許本地堆中對內(nèi)存塊的默認(rèn)移動(dòng)操作。如果HeapReAlloc成功,就返回一個(gè)指向內(nèi)存塊的指針,否則就返回NULL。除非你指定內(nèi)存塊不可重新定位,那么當(dāng)內(nèi)存塊因?yàn)槎阎锌臻g不足時(shí)將不得不重定位,因此造成返回指針的值將與原來不同。 要決定實(shí)際的內(nèi)存塊大小,你可以作以下調(diào)用: DWORD?HeapSize?(HANDLE?hHeap,?DWORD?dwFlags,?LPCVOID?lpMem); 參數(shù)就像你想象的:有堆的句柄,單選標(biāo)志HEAP_NO_SERIALIZE,和指向內(nèi)存塊的指針。
銷毀一個(gè)分離堆 你可以通過以下調(diào)用完全釋放一個(gè)堆: BOOL?HeapDestroy?(HANDLE?hHeap); 在堆中單個(gè)的內(nèi)存塊并不需要在銷毀堆前釋放。 最后一個(gè)是寫DLL時(shí)比較有價(jià)值的函數(shù): HANDLE?GetProcessHeap?(VOID); 返回的是調(diào)用DLL時(shí)進(jìn)程的本地堆的句柄。這個(gè)函數(shù)允許一個(gè)DLL在調(diào)用者進(jìn)程的本地堆中分配內(nèi)存。GetProcessHeap返回的句柄可以供其他堆調(diào)用使用,HeapDestroy除外。
棧 棧是Windows CE內(nèi)存類型中最容易使用的(自行管理)。在Windows CE中的棧像其它操作系統(tǒng)一樣,是被引用函數(shù)的臨時(shí)變量存儲(chǔ)區(qū)。操作系統(tǒng)也用棧來存儲(chǔ)函數(shù)的返回地址和在異常處理中微處理器寄存器的狀態(tài)。 在系統(tǒng)中,Windows CE給每個(gè)線程一個(gè)分離的棧。默認(rèn)情況下,系統(tǒng)中每個(gè)棧大小最大被限制為58KB。在一個(gè)進(jìn)程中,每個(gè)分離的線程可以增加棧的大小直到58-KB的限制。 這個(gè)限制使得要我們要知道Windows CE如何對棧管理。當(dāng)線程被建立的時(shí)候,Windows CE保留一個(gè)64-KB的區(qū)域給每個(gè)線程的棧。棧增加時(shí),提交虛擬內(nèi)存頁是從上至下的。當(dāng)棧減小時(shí),系統(tǒng)將處于的低內(nèi)存環(huán)境(low-memory),會(huì)回收在棧下面未使用但是仍然被提交的頁。58KB的限制來源于64-KB的區(qū)域減去用來防止棧的上溢和下溢的頁面數(shù)量。 當(dāng)一個(gè)應(yīng)用程序建立一個(gè)新的線程時(shí),棧的最大尺寸可以通過建立線程時(shí)CreateThread調(diào)用來指定。應(yīng)用程序的主線程的棧大小可以通過應(yīng)用程序被連接時(shí)的連接器開關(guān)(linker switch)來指定。同樣會(huì)有一些頁用作防護(hù),但是棧的大小可以指定至1MB。注意,這個(gè)指定大小同樣會(huì)被用作所有分離線程棧的默認(rèn)棧大小。那就是說,如果你指定主棧為128KB,程序中所有其他的線程棧大小也限制為128KB,除非在用CreateThread建立線程時(shí)指定一個(gè)不同的大小。 當(dāng)你計(jì)劃如何在應(yīng)用程序中使用棧的時(shí)候,另一個(gè)要值得考慮事情的是。當(dāng)應(yīng)用程序調(diào)用一個(gè)需要棧空間的函數(shù)時(shí),Windows CE會(huì)試圖立即提交滿足要求的當(dāng)前棧之下的頁面,如果沒有物理RAM可用,需要??臻g的線程將會(huì)暫時(shí)停止。如果請求在短時(shí)間內(nèi)得不到允許,可能產(chǎn)生一個(gè)異常。但是如果系統(tǒng)不發(fā)生異常的化,Windows CE將會(huì)最大限度釋放請求的頁。我將簡短地說明一下低內(nèi)存環(huán)境,但現(xiàn)在你只需要記住在的內(nèi)存環(huán)境中不要嘗試使用大量的??臻g。
靜態(tài)數(shù)據(jù) C和C++應(yīng)用程序有一個(gè)預(yù)先定義好的內(nèi)存塊,這是由應(yīng)用程序被裝載時(shí)自動(dòng)分配的。這些塊被用來存儲(chǔ)靜態(tài)分配的字符串,緩沖區(qū)和全局變量,同時(shí)也包括通過靜態(tài)連接到應(yīng)用程序的靜態(tài)庫函數(shù)中的緩沖區(qū)。這些對C程序員來說都不陌生,但是在Windows CE下,這是最后一塊可以在RAM之外壓縮的空間(譯者注:作者的意圖是盡可能壓縮內(nèi)存占有率)。 Windows CE分配給應(yīng)用程序兩塊RAM中的內(nèi)存塊存放靜態(tài)數(shù)據(jù),一個(gè)是可讀寫數(shù)據(jù)(read/write data)和只讀數(shù)據(jù)(read only data)。因?yàn)檫@些區(qū)域是基于頁分配的,所以你可以在一頁的靜態(tài)數(shù)據(jù)開始到下一頁開始之間找到一些剩余空間。細(xì)微調(diào)整Windows CE應(yīng)用程序就是要寫滿這些剩余的空間。如果你在靜態(tài)數(shù)據(jù)區(qū)有空間,最好把一個(gè)或兩個(gè)緩沖區(qū)放到靜態(tài)數(shù)據(jù)區(qū),避免動(dòng)態(tài)分配緩沖區(qū)。 另一個(gè)值得考慮的事情是你是否在寫一個(gè)基于ROM的應(yīng)用程序。你要把盡可能多的數(shù)據(jù)移到只讀靜態(tài)數(shù)據(jù)區(qū)。Windows CE不會(huì)分配只讀的RAM給基于ROM的應(yīng)用程序。并且,ROM頁會(huì)直接映射到虛擬地址空間。這實(shí)際上就給你了一個(gè)無限制的只讀空間,而且不會(huì)影響到應(yīng)用程序?qū)AM的需求。 確定靜態(tài)數(shù)據(jù)區(qū)大小的方法是查看連接器產(chǎn)生的映象(map)文件。映象文件主要用于調(diào)試(debug)目的來確定函數(shù)和數(shù)據(jù)的位置。但是如果你知道查看什么地方的話,它也可以用來顯示靜態(tài)數(shù)據(jù)的大小。列表7-1顯示了一個(gè)由Visual C++產(chǎn)生的示例映象文件的一部分。
列表7-1。映象文件的頂部顯示了應(yīng)用程序數(shù)據(jù)段的大小
memtest ? Timestamp?is?34ce4088?(Tue?Jan?27?12:16:08?1998) ? Preferred?load?address?is?00010000 ? Start?????????Length?????Name???????????????????Class 0001:00000000?00006100H?.text???????????????????CODE 0002:00000000?00000310H?.rdata??????????????????DATA 0002:00000310?00000014H?.xdata??????????????????DATA 0002:00000324?00000028H?.idata$2????????????????DATA 0002:0000034c?00000014H?.idata$3????????????????DATA 0002:00000360?000000f4H?.idata$4????????????????DATA 0002:00000454?000003eeH?.idata$6????????????????DATA 0002:00000842?00000000H?.edata??????????????????DATA 0003:00000000?000000f4H?.idata$5????????????????DATA 0003:000000f4?00000004H?.CRT$XCA????????????????DATA 0003:000000f8?00000004H?.CRT$XCZ????????????????DATA 0003:000000fc?00000004H?.CRT$XIA????????????????DATA 0003:00000100?00000004H?.CRT$XIZ????????????????DATA 0003:00000104?00000004H?.CRT$XPA????????????????DATA 0003:00000108?00000004H?.CRT$XPZ????????????????DATA 0003:0000010c?00000004H?.CRT$XTA????????????????DATA 0003:00000110?00000004H?.CRT$XTZ????????????????DATA 0003:00000114?000011e8H?.data???????????????????DATA 0003:000012fc?0000108cH?.bss????????????????????DATA 0004:00000000?000003e8H?.pdata??????????????????DATA 0005:00000000?000000f0H?.rsrc$01????????????????DATA 0005:000000f0?00000334H?.rsrc$02????????????????DATA ?Address?????????Publics?by?Value??????????????Rva+Base?????Lib:Object ? 0001:00000000???????_WinMain???????????????????00011000?f???memtest.obj 0001:0000007c???????_InitApp???????????????????0001107c?f???memtest.obj 0001:000000d4???????_InitInstance??????????????000110d4?f???memtest.obj 0001:00000164???????_TermInstance??????????????00011164?f???memtest.obj 0001:00000248???????_MainWndProc???????????????00011248?f???memtest.obj 0001:000002b0???????_GetFixedEquiv?????????????000112b0?f???memtest.obj 0001:00000350???????_DoCreateMain??????????????00011350?f???memtest.obj. ?
?在列表7-1中的映象文件指出了EXE文件有五個(gè)區(qū)。區(qū)0001是文本段,包含程序中可執(zhí)行的代碼。區(qū)0002包含只讀(read-only)靜態(tài)數(shù)據(jù)。區(qū)0003包含可讀寫(read/write)靜態(tài)數(shù)據(jù)。區(qū)0004包含調(diào)用其他DLL的固定表。最后,區(qū)0005是資源區(qū),包含應(yīng)用程序的資源,例如菜單和對話框模板。 讓我們來看看.data,.bss和.rdata行。.data區(qū)包含已初始化的可讀寫數(shù)據(jù)。如果你這樣初始化了一個(gè)全局變量: static?HINST?g_hLoadlib?=?NULL; g_loadlib變量將結(jié)束在.data段末尾。.bss段包含未初始化的可讀寫數(shù)據(jù)。一個(gè)緩沖被定義如下: static?BYTE?g_ucItems[256];
以.bss段為結(jié)尾。最后一個(gè)段.rdata,包含只讀數(shù)據(jù)。你使用const關(guān)鍵字定義的靜態(tài)數(shù)據(jù)結(jié)束在.rdata段。有一個(gè)結(jié)構(gòu)的例子,使我用來作消息查詢表的:
//?Message?dispatch?table?for?MainWindowProc
const?struct?decodeUINT?MainMessages[]?=?{ WM_CREATE,?DoCreateMain, WM_SIZE,?DoSizeMain, WM_COMMAND,?DoCommandMain, WM_DESTROY,?DoDestroyMain, }; ?
.data和.bss塊被折疊進(jìn)0003區(qū),如果你將第三區(qū)的所有塊大小加起來,總共為0x2274,或8820字節(jié)。為和下頁對齊,讀寫數(shù)據(jù)區(qū)將占9頁,那么就有396字節(jié)未使用(譯者注:1024*9-8820=396)。因此在這個(gè)例子中,把一個(gè)或者兩個(gè)緩沖區(qū)放入靜態(tài)數(shù)據(jù)區(qū)比較合適。只讀數(shù)據(jù)段0002區(qū),包括.rdata,占0x0842或2114字節(jié),占3頁,剩余958字節(jié),幾乎是一整頁。在這種情況下,移動(dòng)75字節(jié)的常量數(shù)據(jù)從只讀段到可讀寫段將在應(yīng)用程序加載時(shí)節(jié)約一頁的RAM。
字符串資源 有一個(gè)經(jīng)常忘記的只讀區(qū)域時(shí)應(yīng)用程序的資源段,像我前面在第四章提到的Windows CE的新特性有一個(gè)LoadString函數(shù),值得再次重復(fù)。如果你調(diào)用LoadString時(shí)指向緩沖區(qū)的指針寫0,函數(shù)將返回一個(gè)指向資源段中字符串的指針。例子如下: LPCTSTR?pString; pString??=?(LPCTSTR)LoadString?(hInst,?ID_STRING,?NULL,?0) 返回的字符串是只讀的,但是它允許你應(yīng)用字符串而不需要分配一個(gè)緩沖給字符串。這里警告一下,字符串不能以0結(jié)尾,除非你在資源編譯器命令行中加了-n開關(guān)。不管如何,單詞必須是先于字符串資源長度(譯者注:作者此處意思可能是說長度包含字符串資源的長度)。
選擇適當(dāng)?shù)膬?nèi)存類型 現(xiàn)在我們已經(jīng)看過了不同類型的內(nèi)存,是時(shí)候來考慮最好的使用辦法了。對大的內(nèi)存塊來說,直接分配虛擬內(nèi)存是最好的辦法,一個(gè)應(yīng)用程序可以保留很多的地址空間(直到應(yīng)用程序32MB的限制)但是只能在一個(gè)時(shí)間提交必須的頁。直接分配虛擬內(nèi)存是最靈活的內(nèi)存分配方式,它把頁間隔(granularity)的負(fù)擔(dān)以及對保留頁和提交頁都交由我們負(fù)擔(dān)。 本地堆是很方便的,它不需要?jiǎng)?chuàng)建并且會(huì)自動(dòng)隨著需求擴(kuò)大。但碎片是這里的問題。但是要考慮到Pocket PC的應(yīng)用程序可能會(huì)運(yùn)行幾星期或幾個(gè)月的時(shí)間。在Pocket PC上沒有關(guān)閉電源的按鈕,只有掛起命令。因此,你考慮內(nèi)存碎片的時(shí)候不要假設(shè)用戶會(huì)打開應(yīng)用程序,改變一個(gè)項(xiàng)目,然后關(guān)閉它。用戶可能打開程序然后讓它一直運(yùn)行以至于程序就像一個(gè)快捷方式(quick click away)。 分離堆的優(yōu)點(diǎn)是當(dāng)你不用時(shí)可以銷毀,把碎片消滅在萌芽狀態(tài)。有一點(diǎn)不好的就是分離堆需要手動(dòng)創(chuàng)建和銷毀。 靜態(tài)數(shù)據(jù)區(qū)是放置一兩個(gè)緩沖區(qū)的好地方,因?yàn)轫撁媸且呀?jīng)被分配的。管理靜態(tài)數(shù)據(jù)的關(guān)鍵是使靜態(tài)數(shù)據(jù)段大小盡可能地接近,但是要超過你目標(biāo)處理器的頁面的大小。當(dāng)常量數(shù)據(jù)在只讀段中,往往較好的辦法是把它移到可讀寫段中。但當(dāng)應(yīng)用程序被燒到ROM中時(shí),你不要這么做。常量數(shù)據(jù)越多會(huì)比較好,因?yàn)樗徽糝AM。只讀段方便應(yīng)用程序從對象存儲(chǔ)區(qū)啟動(dòng),因?yàn)橹蛔x頁能通過操作系統(tǒng)丟棄和重載。 棧用起來比較簡單而且到處存在。唯一要考慮的是棧的最大尺寸和在的內(nèi)存環(huán)境下擴(kuò)大棧的問題。確定你的應(yīng)用程序在關(guān)閉的時(shí)候不需要大量??臻g。當(dāng)程序被關(guān)閉時(shí),如果系統(tǒng)掛起你程序中的一個(gè)線程,用戶可能會(huì)丟失數(shù)據(jù)。這會(huì)使顧客不滿意。
低內(nèi)存環(huán)境 當(dāng)系統(tǒng)運(yùn)行在一個(gè)低RAM環(huán)境中,應(yīng)用程序?qū)⒄{(diào)整并最小化它們的內(nèi)存使用。Windows CE運(yùn)行在一個(gè)幾乎永久的低內(nèi)存環(huán)境中。Pocket PC被特意設(shè)計(jì)為運(yùn)行低內(nèi)存環(huán)境。在Pocket PC中的應(yīng)用程序沒有關(guān)閉按鈕,當(dāng)系統(tǒng)需要更多內(nèi)存時(shí),外殼(shell)自動(dòng)關(guān)閉這些程序。正因?yàn)槿绱?#xff0c;Windows CE有許多方法來管理運(yùn)行在低內(nèi)存系統(tǒng)中的程序。
WM_HIBERNATE 消息 Windows CE第一個(gè)最明顯的變化時(shí)是增加了WM_HIBERNATE消息。Windows CE的shell發(fā)送消息給最頂層的有WS_OVERLAPPED式樣(那就是說,既沒有WS_POPUP也沒有WS_CHILD式樣)和WS_VISIBLE式樣的窗口。這些限制將允許大多數(shù)程序至少有一個(gè)窗口可以接受WM_HIBERNATE消息。有一個(gè)例外就是,當(dāng)應(yīng)用程序不能真正結(jié)束程序而只是簡單隱藏所有窗口。這種方式允許應(yīng)用程序可以快速啟動(dòng),因?yàn)樗麓沃皇秋@示窗口。但是這就意味著,當(dāng)用戶想關(guān)閉它們的時(shí)候仍然占據(jù)著RAM。這對程序設(shè)計(jì)來說是正確的,但是不應(yīng)用在Windows CE中,這種方式會(huì)造成程序被隱藏時(shí)總處在冬眠(hibernate)模式,因?yàn)樗鼈冇肋h(yuǎn)接收不到WM_HIBERNATE消息。 Shell發(fā)送WM_HIBERNATE消息給最頂層的窗口在Z軸相反的位置(reverse Z-order)直到內(nèi)存被釋放,使可用內(nèi)存超過系統(tǒng)預(yù)先的限制。當(dāng)應(yīng)用程序接收到一個(gè)WM_HIBERNATE消息,它會(huì)盡可能減少內(nèi)存占有程度。這包括釋放被緩沖(cached)的數(shù)據(jù);釋放GDI對象,例如字體,位圖和畫刷;并銷毀任何窗口控件。從本質(zhì)上來說,應(yīng)用程序?qū)?huì)減少內(nèi)存到維持它內(nèi)部狀態(tài)的最小值。 如果發(fā)送WM_HIBERNATE消息給后臺(tái)的應(yīng)用程序不能釋放足夠的內(nèi)存以便使系統(tǒng)離開內(nèi)存被限制的狀態(tài)。WM_HIBERNATE消息將會(huì)發(fā)送給前臺(tái)程序。如果你正在冬眠的程序開始銷毀窗口的控件,你必須確保它不是前臺(tái)的程序,控件消失不會(huì)給用戶帶來興奮的感覺而是困惑。
內(nèi)存限度 Windows CE監(jiān)視系統(tǒng)自由的RAM,并對越來越少的RAM作出響應(yīng)。當(dāng)很少內(nèi)存可用時(shí),Windows CE首先發(fā)送WM_HIBERNATE消息,接下來會(huì)限制可能的內(nèi)存分配。下面的兩個(gè)表顯示了Explorer shell和Pocket PC引發(fā)的低內(nèi)存事件的自由內(nèi)存級(jí)別。Windows CE定義了是個(gè)內(nèi)存狀態(tài):normal,limited,low和critical。系統(tǒng)的內(nèi)存狀態(tài)依賴于整個(gè)系統(tǒng)有多少內(nèi)存可用。這些限制都比4-KB頁要高,因?yàn)橄到y(tǒng)具有內(nèi)存最小分配限制,就像7-1和7-2的表。 表7-1 Explorer Shell的內(nèi)存限度
事件 自由內(nèi)存 1024-Page Size 自由內(nèi)存 4096-Page Size 注解 Limited-memory state 128 KB 160 KB 發(fā)送 WM_HIBERNATE 消息給in reverse Z-order的應(yīng)用程序。釋放棧空間并回收利用。 Low-memory state 64 KB 96 KB 限制虛擬內(nèi)存分配為16 KB。 顯示Low-memory對話框。 Critical-memory state 16 KB 48 KB 限制虛擬內(nèi)存分配為8KB。
表7-2 Pocket PC的內(nèi)存限度
事件 自由內(nèi)存 1024-Page Size 自由內(nèi)存 4096-Page Size 注解 Hibernate threshold 200 KB 224 KB 發(fā)送 WM_HIBERNATE 消息給in reverse Z-order的應(yīng)用程序。 Limited-memory state 128 KB 160 KB 開始關(guān)閉在 reverse Z-order上的應(yīng)用程序。釋放棧空間并回收利用。 Low-memory state 64 KB 96 KB 限制虛擬內(nèi)存分配為16 KB。 Critical-memory state 16 KB 48 KB 限制虛擬內(nèi)存分配為8 KB。
這些內(nèi)存狀態(tài)的影響是共享剩余的財(cái)富。首先,WM_HIBERNATE消息被發(fā)送給應(yīng)用程序,并請求減少它們的內(nèi)存占有率,當(dāng)應(yīng)用程序被發(fā)送了一個(gè)WM_HIBERNATE消息后,系統(tǒng)將檢測內(nèi)存級(jí)別,確認(rèn)是否可用內(nèi)存在限度之上,如果可用內(nèi)存不足,WM_HIBERNATE消息將被發(fā)送給下一個(gè)程序。這會(huì)持續(xù)到所有程序被發(fā)送了WM_HIBERNATE消息。 Exlporer shell和Pocket PC的低內(nèi)存策略在這點(diǎn)上有區(qū)別。如果Explorer shell運(yùn)行時(shí),系統(tǒng)會(huì)顯示OOM(out of memory)對話框,并請用戶確認(rèn)是否關(guān)閉一個(gè)應(yīng)用程序或把對象存儲(chǔ)區(qū)的RAM重新劃分給程序內(nèi)存。如果用戶選擇了其中之一,仍然沒有足夠的內(nèi)存,out of memory對話框?qū)?huì)再次出現(xiàn),這個(gè)過程會(huì)重復(fù),直到H/PC有足夠的在限度之上的內(nèi)存。 對Pocket PC來說,操作稍微有些不同。Pocket PC shell自動(dòng)開始關(guān)閉最近最少使用的應(yīng)用程序,而不詢問用戶。如果關(guān)閉除了前臺(tái)程序和shell之外的所有程序,仍然沒有足夠內(nèi)存,系統(tǒng)將會(huì)使用其他的技術(shù)來從棧開始清理自由的頁,并限制虛擬內(nèi)存分配。 如果在任何一個(gè)系統(tǒng)上,應(yīng)用程序被請求關(guān)閉卻沒有關(guān)閉,系統(tǒng)在8秒鐘后將會(huì)清理該應(yīng)用程序。這就是一個(gè)應(yīng)用程序不要分配大量的??臻g的原因。如果應(yīng)用程序被關(guān)閉而導(dǎo)致低內(nèi)存環(huán)境,很可能是??臻g不能分配,應(yīng)用程序?qū)⒈粧炱?。如果發(fā)生在系統(tǒng)請求應(yīng)用程序關(guān)閉以后,可能是清除內(nèi)存以后沒有適當(dāng)?shù)幕謴?fù)狀態(tài)。 在low和critical-memory狀態(tài),應(yīng)用程序被限制了內(nèi)存分配的大小。在這些情況下,甚至還有可以滿足要求的內(nèi)存剩余情況下,請求分配大過允許限度的虛擬內(nèi)存將會(huì)被拒絕。記住,并不止是虛擬內(nèi)存分配被限制,堆分配和棧分配也被禁止,要滿足分配請求,那么分配時(shí)需要虛擬內(nèi)存在可允許的限制之上。 我這里要指出,發(fā)送WM_HIBERNATE消息和自動(dòng)關(guān)閉應(yīng)用程序是由系統(tǒng)的shell執(zhí)行的。在一個(gè)OEM自己可以編寫shell的嵌入式系統(tǒng)中,實(shí)現(xiàn)WM_HIBERNATE消息和其他內(nèi)存管理技術(shù)是OEM廠商的責(zé)任。幸運(yùn)的是,Microsoft Windows CE PlatForm Builder提供了Exlporer shell實(shí)現(xiàn)WM_HIBERNATE消息的源碼。 這里不言而喻,應(yīng)用程序要檢查任何內(nèi)存分配調(diào)用的返回代碼,但是因?yàn)檫@里還沒說,所以我還是要說。檢查內(nèi)存分配調(diào)用的返回代碼。在Windows CE中比在桌面版本的Windows中可能有更多的機(jī)會(huì)導(dǎo)致內(nèi)存分配失敗。應(yīng)用程序必須很好地實(shí)現(xiàn)拒絕內(nèi)存分配。 Windows CE不支持完全的Win32內(nèi)存管理API,但是很清楚這里有對WindowsCE設(shè)備受限制內(nèi)存的足夠支持。
總結(jié)
以上是生活随笔 為你收集整理的转载:Windows CE内存管理 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。