Windows CE 進(jìn)程、線(xiàn)程和內存管理(三)
同其它Windows操作系統一樣,Windows CE.NET也支持32位虛擬內存機制、按需分配內存和內存映射文件等。但是與其它Windows操作系統又有明顯的不同。畢竟Windows CE是一種嵌入式實(shí)時(shí)性的操作系統,在內存管理方面必須要比其它Windows操作系統更節約物理內存和虛擬地址空間。在內存管理API方面,為了便于移植程序,Windows CE和其它Windows操作系統函數聲明基本一致,這使一個(gè)在其它Windows下開(kāi)發(fā)的程序員可以直接使用早就熟悉的API函數, 但是CE下內存管理的原理開(kāi)發(fā)者還是應該熟悉的。
1、ROM和RAM
最早的基于Windows CE的民用產(chǎn)品,采用的存儲設備都是ROM + RAM ,ROM保存CE內核文件、應用程序,而RAM用于內核、所有應用程序運行時(shí)使用,關(guān)閉電源時(shí)必須給RAM提供電力來(lái)保存系統配置信息、用戶(hù)產(chǎn)生的文件等。為了適應這樣的存儲硬件,CE采用了ROM文件系統和RAM文件系統。在ROM中存放的模塊可以是壓縮的,也可以是不壓縮的,這取決于OEM。OEM在定制內核時(shí)可以設置是否壓縮模塊。如果是壓縮的,模塊在運行前先解壓并全部存放到RAM中。如果是不壓縮的,就本地執行(XIP,executed in place)。本地執行和其它Windows操作系統下執行應用程序、DLL方式一致,也就是應用了內存映射文件技術(shù)。在這里我順便講一下。在啟動(dòng)時(shí)應用程序或DLL的代碼段不加載到物理內存中,內核只是分配虛擬地址空間給代碼段,當執行代碼時(shí)內核會(huì )到實(shí)際存放在硬盤(pán)上的文件中尋找代碼并執行。采用這樣的技術(shù)既可以節省可用內存又可以減少加載的時(shí)間。請注意,操作系統首先會(huì )到為硬盤(pán)準備的緩沖區里讀取代碼數據,如果沒(méi)有就命令硬盤(pán)讀取應用程序文件數據到緩沖區。所以緩沖區設置大點(diǎn)是有好處的。Windows CE的本地執行就是采用這樣的技術(shù)來(lái)加載ROM內的應用程序和DLL的。所以Windows CE的DLL分為XIP DLL和非XIP DLL。這種加載方式的缺點(diǎn)就是執行相對較慢一點(diǎn),如果用PB創(chuàng )建一個(gè)具有實(shí)時(shí)性特點(diǎn)的內核,一定不能選用XIP技術(shù)。
到后來(lái)基于Windows CE的產(chǎn)品開(kāi)始采用FLASH、IDE等永久存儲設備時(shí),文件系統又加了個(gè)FAT。內核文件和其它應用程序也可以存放到永久存儲設備中,內核由加載程序解壓并加載到RAM的對象存儲區域(object store),包含在內核中的所有系統應用程序文件和DLL文件都存放到這個(gè)區域。當執行一個(gè)應用程序時(shí),內核將這個(gè)應用程序調用的系統DLL加載到Slot 1(0x0200 0000-0x03FF FFFF)。在Windows CE.NET中Slot 1專(zhuān)用于XIP DLL使用。
RAM文件系統專(zhuān)用于對象存儲。在以前的文章中曾經(jīng)講過(guò),它和ROM文件系統是Windows CE默認的文件系統。Windows CE啟動(dòng)后把RAM分為對象存儲區域(object store)和應用程序內存區域(program memory)。對象存儲區域采用RAM文件系統來(lái)保存文件,一般用于保存內核解開(kāi)的所有文件。應用程序內存區域留給所有應用程序運行時(shí)使用。在Windows CE下控制面板-系統-內存中,可以調節這兩個(gè)存儲區域的比例,滑塊向左,則釋放對象存儲區域的一些內存并將這些內存劃到應用程序內存區域中?;瑝K向右則相反。
2、內存結構
Windows CE.NET只能管理512MB的物理內存和4GB大小的虛擬地址空間。不同的CPU內存管理方法也不同。對于MIPS和SHX系列CPU來(lái)說(shuō),物理地址映射是由CPU完成的,CE內核可以直接訪(fǎng)問(wèn)512MB的物理內存。對于x86系列和ARM系列的CPU來(lái)說(shuō),在內核啟動(dòng)過(guò)程中它會(huì )將現有物理內存地址全部映射到0x8000 0000以上的虛擬地址空間中供內核以后使用。OEM可以通過(guò)OEMAddressTable來(lái)詳細定義虛擬地址和物理地址的映射關(guān)系。OEMAddressTable本身并不是一個(gè)文件,它只是存在于其它文件中描述虛擬地址和實(shí)際物理地址的映射關(guān)系的數據。比如文件oem init.asm中包含一段代碼:dd 80000000h, 0, 04000000h 。它表示將整個(gè)物理地址(0x0400 0000=64MB)共64MB映射到虛擬地址從0x8000 0000到0x8400 0000中。關(guān)于OEMAddressTable我將在以后關(guān)于PB的文章中講述。
整個(gè)4GB虛擬地址空間主要劃分為兩部分,從0x8000 0000以上為內核使用部分,0x8000 0000以下為應用程序使用部分。詳細見(jiàn)下表:
地址范圍 | 用途 |
0x0000 0000到0x41FF FFFF | 由所有應用程序使用。共33個(gè)槽,每個(gè)槽占32MB。槽0(Slot 0)由當前占有CPU的進(jìn)程使用。槽1由XIP DLL使用。其它槽用于進(jìn)程使用,每個(gè)進(jìn)程占用一個(gè)槽。 |
0x4200 0000到0x7FFF FFFF | 由所有應用程序共享的區域。32MB地址空間有時(shí)不能夠滿(mǎn)足一些進(jìn)程的需求。那么進(jìn)程可以使用這個(gè)范圍的地址空間。在這個(gè)區域里應用程序可以建堆、創(chuàng )建內存映射文件、分配大的地址空間等。 |
0xA000 0000到0xBFFF FFFF | 在這個(gè)范圍內核重復定義0x8000 0000到0x9FFF FFFF之間定義的物理地址映射空間。區別是在這范圍映射的虛擬地址空間不能夠用于緩沖。 我舉例來(lái)說(shuō)明:假設一個(gè)產(chǎn)品有64MB物理內存。如上文所述定義好OEMAddressTable后。內核啟動(dòng)后一個(gè)物理地址映射空間范圍在0x8000 0000到0x8400 0000,那么內核會(huì )從0xA000 0000到0xA400 0000定義一個(gè)同樣范圍的地址空間,這個(gè)地址空間和0x8000 0000到0x8400 0000映射到相同的物理地址。但這個(gè)虛擬地址空間不能夠用于緩沖。 |
0xC000 0000到0xC1FF FFFF | 系統保留空間 |
0xC200 0000到0xC3FF FFFF | 內核程序nk.exe使用的地址空間。 |
0xC400 0000到0xDFFF FFFF | 這個(gè)范圍為用戶(hù)定義的靜態(tài)虛擬地址空間,但這個(gè)地址空間只能用于非緩沖使用。 利用OEMAddressTable定義物理地址映射空間后,每次內核啟動(dòng)時(shí)這個(gè)范圍都不改變了,除非產(chǎn)品包含的物理內存容量發(fā)生變化。假如增加到128MB物理內存,那么物理地址映射空間也向后擴大了一倍。Windows CE.NET也允許用戶(hù)創(chuàng )建靜態(tài)的物理地址映射空間。用戶(hù)可以調用CreateStaticMapping函數或者NKCreateStaticMapping函數來(lái)映射某一段物理地址到0xC400 0000和0xE000 0000之間的某一個(gè)范圍。需要注意的是用這個(gè)函數創(chuàng )建的靜態(tài)虛擬地址只能夠由內核訪(fǎng)問(wèn),而且不能用于緩沖。 |
0xE000 0000到0xFFFF FFFF | 內核使用的虛擬地址。當內核需要大的虛擬地址空間時(shí),會(huì )在這個(gè)范圍內分配。 |
![]() |
圖1 Windows CE.NET內存結構 本文引用地址:http://dyxdggzs.com/article/258283.htm |
3、進(jìn)程地址空間結構
進(jìn)程地址空間結構如圖2所示。這個(gè)圖源至MSDN。Windows CE.NET同以前版本的Windows CE操作系統在進(jìn)程地址空間上有所不同,以前的Windows CE把XIP DLL也加載到進(jìn)程的32MB地址空間中,而Windows CE.NET把XIP DLL單獨加載到Slot 1中,這樣對于每個(gè)進(jìn)程來(lái)說(shuō),它總的地址空間就大了一倍,也就是64MB。這個(gè)問(wèn)題我在講解進(jìn)程的時(shí)候提到過(guò)。
當一個(gè)應用程序啟動(dòng)時(shí),內核為這個(gè)程序選擇一個(gè)空閑的槽(Slot),并且加載所有的代碼、資源,并分配堆棧,加載DLL等。當這個(gè)進(jìn)程得到CPU使用權時(shí),它的整個(gè)地址空間被內核映射到Slot 0,也就是當前進(jìn)程使用的地址空間,然后開(kāi)始運行。圖中給出的地址實(shí)際上是經(jīng)過(guò)映射到Slot 0之后的結構。從圖中可以看出,進(jìn)程首先加載代碼段,因為每個(gè)進(jìn)程最低部64KB作為保留區域,所以代碼段從0x0001 0000開(kāi)始,內核為代碼段分配足夠的虛擬地址空間后,接著(zhù)分配空間為只讀數據和可讀/可寫(xiě)數據,接著(zhù)分配空間為資源數據,之后分配空間為默認堆和棧。非XIP DLL從進(jìn)程最高地址向下開(kāi)始加載。非XIP DLL的加載按如下規則:內核先檢查要加載的DLL是否被其它進(jìn)程加載過(guò),如果加載過(guò),就做一個(gè)地址的重定位。這樣就避免了整個(gè)系統內多次加載相同DLL。如果沒(méi)有加載過(guò),就按照從槽的高地址到槽的低地址的順序查找空閑的地址空間。然后分配足夠的地址空間用于加載DLL。因為每個(gè)進(jìn)程在執行前都要映射到Slot 0,而且進(jìn)程使用的所有DLL可能來(lái)自不同的槽(Slot),為避免所有使用的DLL在映射到Slot 0中出現地址空間沖突的現象,內核的加載器(Loader)在加載DLL時(shí)會(huì )查找所有槽中加載的DLL的地址,保證在映射到Slot 0時(shí)不會(huì )發(fā)生地址沖突現象。假如系統內有兩個(gè)進(jìn)程,進(jìn)程A只加載了DLL A,進(jìn)程B需要加載DLL A和DLL B,那么進(jìn)程B會(huì )留出DLL A的地址空間,然后加載DLL B,也就是說(shuō)進(jìn)程B映射到Slot 0時(shí),DLL A的地址空間和DLL B的地址空間是相鄰的,不會(huì )發(fā)生沖突。好在Windows CE下DLL都很小,而且一個(gè)應用程序使用的DLL多數是系統的DLL(存在于Slot 1)。所以目前來(lái)看進(jìn)程的地址空間還夠用。
![]() |
圖2 進(jìn)程地址空間結構 |
堆是一段連續的較大的虛擬地址空間。應用程序在堆中可以動(dòng)態(tài)地分配、釋放所需大小的內存塊。利用堆的優(yōu)點(diǎn)是在一定范圍內減小了內存碎塊。而且開(kāi)發(fā)者分配內存塊前不必去了解CPU的類(lèi)型。因為不同的CPU分頁(yè)大小不相同,每個(gè)內存頁(yè)可能是1KB、4KB或更多。在堆內分配內存塊可以是任意大小的,而直接分配內存就必須以?xún)却骓?yè)為單位。當一個(gè)應用程序啟動(dòng)時(shí),內核在進(jìn)程所在的地址空間中為進(jìn)程分配一個(gè)默認192KB大小的虛擬地址空間,但是并不立刻提交物理內存。如果在運行當中192KB不能滿(mǎn)足需求,那么內核會(huì )在進(jìn)程地址空間中重新查找一個(gè)足夠大小的空閑的地址空間,然后復制原來(lái)堆的數據,最后釋放原來(lái)的堆所占的地址空間。這是因為默認的堆的高地址處還有棧,所以必須重新分配一個(gè)。Windows CE.NET的堆有明顯的缺點(diǎn),不同于其它Windows操作系統下的堆管理,在Windows CE.NET創(chuàng )建的堆中創(chuàng )建的內存塊不能夠移動(dòng),多次創(chuàng )建內存塊、釋放內存塊會(huì )產(chǎn)生內存碎塊,這樣的話(huà)當需要分配一個(gè)大一點(diǎn)的連續的內存塊時(shí),本來(lái)空閑的內存塊加起來(lái)足夠用,但是這些內存塊是分隔的,不符合要求。像Windows 2000或98的內核會(huì )頻繁的移動(dòng)分散的正使用的內存塊,使它們聚集在一起。這也是為什么有時(shí)需要句柄而不用指針的原因。由于Windows CE.NET的堆的缺點(diǎn),開(kāi)發(fā)者如果要頻繁的在堆中創(chuàng )建、釋放內存塊的話(huà),最好自己創(chuàng )建一個(gè)單獨的堆,而不用默認的堆。而且我還建議最好直接在全局地址空間中(0x4200 0000到0x7FFF FFFF)分配所需地址空間。因為進(jìn)程地址空間可用的實(shí)在太小了。關(guān)于堆函數我在這就不多說(shuō)了,和其它Windows操作系統堆API基本一致。請參考幫助文檔。
棧也是一段連續的虛擬地址空間,和堆相比空間要小的多,它是專(zhuān)為函數使用的。當調用一個(gè)函數時(shí)(包括線(xiàn)程),內核會(huì )產(chǎn)生一個(gè)默認的棧,并且內核會(huì )立刻提交少量的物理內存(也可以禁止內核立刻提交物理內存)。棧的大小和CPU有關(guān),一般為64KB,并且保留頂部2KB為了防止溢出??梢孕薷臈5拇笮?,具體修改方法在講解線(xiàn)程的時(shí)候已經(jīng)說(shuō)過(guò)了,這里就不再重復了。修改棧的大小一般時(shí)候不會(huì )發(fā)生,如果采用在編譯鏈接時(shí)修改大小,那么所有棧的大小都會(huì )改變,這不太合理。實(shí)際開(kāi)發(fā)中最好不要在棧中分配很大、很多的內存塊,如果分配的內存塊超過(guò)了默認棧的限制,那么會(huì )引起訪(fǎng)問(wèn)非法并且內核會(huì )立刻終止進(jìn)程。最好在進(jìn)程的堆中分配大的內存塊并且在函數返回前釋放,或者在創(chuàng )建線(xiàn)程時(shí)指定棧的大小。
5、內存映射文件
與虛擬內存一樣,內存映射文件用來(lái)保留一個(gè)地址空間,并提交物理存儲器。早期的內存映射文件并不是提交物理內存供調用者使用,而是提交永久存儲器上的文件數據。當然操作系統會(huì )為永久存儲器保留一個(gè)讀緩沖區,這樣讀取文件數據就快多了。內存映射文件的特點(diǎn)使它很適合于加載EXE或DLL文件。這樣可以節省內存又減少了加載所需時(shí)間。還可以使用它來(lái)映射大容量的文件,這樣就不必在讀取文件數據前設置很大的緩沖區。另外內存映射文件常用于進(jìn)程間通信,也是進(jìn)程間通信的主要手段,其它進(jìn)程之間通信機制都是基于內存映射文件來(lái)實(shí)現。為了更快的在進(jìn)程之間通信,現在的內存映射文件也可以提交物理內存,這樣內存映射文件既可以提交物理內存又可以提交文件。
Windows CE.NET同樣支持無(wú)名和有名的內存映射文件。我建議在開(kāi)發(fā)軟件的過(guò)程中,如果需要讀寫(xiě)大容量的文件,或者需要在不同進(jìn)程內的線(xiàn)程之間通信,最好采用內存映射文件,而且最好在全局地址空間內(0x4200 0000到0x7FFF FFFF)分配。這會(huì )使我們事半功倍。
5.1 映射數據文件
第一步:調用CreateFileForMapping函數。在Windows CE.NET中推薦使用這個(gè)函數替代CreateFile函數。CreateFileForMapping函數由內核執行并創(chuàng )建文件,它也可以打開(kāi)由CreateFile函數創(chuàng )建的文件。其參數同CreateFile相似。參數1指定文件路徑,注意文件路徑的格式是沒(méi)有盤(pán)符的,參數2指定訪(fǎng)問(wèn)方式(讀或寫(xiě)),參數3指定共享模式,參數4指定安全屬性(必須設置為NULL),參數5指定是創(chuàng )建還是打開(kāi)文件,參數6指定文件屬性,參數7忽略。具體參數細節參見(jiàn)Windows CE.NET幫助。函數返回創(chuàng )建或者打開(kāi)的文件的句柄。
第二步:調用CreateFileMapping函數。這個(gè)函數創(chuàng )建一個(gè)無(wú)名的或者有名的內存映射文件對象。參數1為文件句柄。這個(gè)值由CreateFileForMapping函數返回。參數2為安全屬性(必須設置為NULL),參數3指定要映射的文件的保護屬性(只讀或者讀寫(xiě)),參數4和參數5共同用于指定要映射的文件的大小。文件的容量過(guò)大將導致32位整數也不能表示,所以這里用64位變量表示,其中參數4為高32位數,參數5為低32位數。最后一個(gè)參數指定內存映射文件的名稱(chēng)。這里可以設置為NULL,表示不需要名字。
第三步:調用MapViewOfFile函數。這個(gè)函數用于保留一段足夠的地址空間,并且將永久存儲器上的文件數據映射到這個(gè)地址空間。映射后這段地址空間又叫做文件視圖,映射范圍可以是全部文件,也可以是部分文件。這里需要注意的是如果文件很大,那這個(gè)函數將在全局地址空間內分配地址空間。參數1指定內存映射文件對象的句柄,這個(gè)值由CreateFileMapping函數返回。參數2和CreateFileMapping函數中參數3很相似,都是用于限定訪(fǎng)問(wèn)權限。參數3和參數4共同用于指定映射區域的開(kāi)始位置。其中參數3為高32位數,參數4為低32位數。參數5指定映射區域的大小。需要注意的是參數3和參數4指定的64位數開(kāi)始位置可以不是64KB的倍數。而其它Windows操作系統就必須限制以64KB為單位。另外還要注意的是幫助文檔中說(shuō)不能保證一個(gè)文件的映射視圖是連續的,并建議為了防止訪(fǎng)問(wèn)非法,應該加入結構化異常處理機制。這個(gè)可能性我認為很小,一般對于大于2MB的虛擬地址空間的申請,內核都會(huì )在全局地址空間中分配。全局地址空間(0x4200 0000到0x7FFF FFFF)近1GB的空間應該足夠用了。畢竟Windows CE下的文件都很小。不過(guò)在代碼中加入結構化異常處理也不是壞事。我們應該養成凡是讀寫(xiě)文件數據時(shí)都加入結構化異常處理的習慣。
第四步:進(jìn)行讀/寫(xiě)操作。MapViewOfFile函數如果成功執行,那么返回映射視圖的首地址。這時(shí)就可以把視圖當成是一個(gè)緩沖區,開(kāi)始讀或寫(xiě)操作了。
第五步:執行結束工作。先調用UnmapViewOfFile函數撤銷(xiāo)文件映射視圖。參數只有一個(gè),指定視圖首地址。然后調用CloseHandle函數關(guān)閉內存映射文件對象,參數為句柄。最后再次調用CloseHandle函數,關(guān)閉打開(kāi)的文件的句柄。
5.2 進(jìn)程之間通信
進(jìn)程之間有時(shí)需要通信。系統提供的進(jìn)程之間的通信機制比如COM、剪貼板等,在底層實(shí)現上都是利用內存映射文件技術(shù)。其實(shí)進(jìn)程之間通信的思路很簡(jiǎn)單,在這里我順便講一下。在其它Windows操作系統中,每個(gè)進(jìn)程獨自占有4GB的地址空間,高2GB是內核的地址空間,而低2GB是進(jìn)程的地址空間。一個(gè)進(jìn)程所能訪(fǎng)問(wèn)的所有低2GB地址都是自己的地址空間,當訪(fǎng)問(wèn)內核地址空間時(shí)就會(huì )受到內核的限制。這樣一個(gè)進(jìn)程當然無(wú)法訪(fǎng)問(wèn)其它進(jìn)程了。為解決進(jìn)程間通信的問(wèn)題,內存映射文件技術(shù)被利用作為解決方案。原來(lái)內存映射文件只映射類(lèi)似磁盤(pán)一類(lèi)的存儲器上的文件。而為了更快速地在進(jìn)程之間通信,內存映射文件還可以提交物理內存。實(shí)現方法是通過(guò)訪(fǎng)問(wèn)同一個(gè)內存映射文件對象(映射到物理內存),兩個(gè)進(jìn)程或多個(gè)進(jìn)程就能夠訪(fǎng)問(wèn)到同一塊物理內存,這樣一個(gè)進(jìn)程寫(xiě)到物理內存的數據,其它進(jìn)程就能夠看到了。而Windows CE雖然每個(gè)進(jìn)程只占有32MB的地址空間,而且所有進(jìn)程全部處于4GB的地址空間中,但是彼此還是不能夠隨意訪(fǎng)問(wèn)的。在Windows CE下除了使用內存映射文件技術(shù)外,還有一種方法也很適合使用,就是利用對象存儲。對象存儲本身使用RAM文件系統,用普通的操作文件的API就可以創(chuàng )建、讀取存在于對象存儲區域內的文件。Windows 目錄就存在于對象存儲區域內。我們可以利用在Windows目錄下創(chuàng )建文件來(lái)實(shí)現進(jìn)程間通信。這種方法既實(shí)現簡(jiǎn)單,只需調用幾個(gè)文件API函數,又可以減少通信時(shí)間,因為Windows目錄存在于物理內存中,數據I/O當然很快了。利用對象存儲來(lái)實(shí)現進(jìn)程之間的通信是我自己想出來(lái)的,MSDN或其它文檔并沒(méi)有這方面的說(shuō)明。需要注意的就是對象存儲區域的大小。另外從實(shí)現的代碼量上看也不如內存映射文件技術(shù)。
下面講解如何利用內存映射文件實(shí)現進(jìn)程之間的通信。假設進(jìn)程A和進(jìn)程B需要通信,那么進(jìn)程A需要先創(chuàng )建一個(gè)內存映射文件(之前不必調用CreateFileForMapping函數來(lái)創(chuàng )建文件,因為不需要創(chuàng )建文件)。這個(gè)內存映射文件可以是在永久存儲器中,也可以是在內存中。為了減小通信時(shí)間,最好提交物理內存。進(jìn)程A在調用CreateFileMapping函數時(shí),參數1指定為INVALID_HANDLE_VALUE,這表示這個(gè)內存映射文件對象將要把物理內存提交到地址空間中。最后一個(gè)參數一定要指定一個(gè)名字。進(jìn)程B也同樣調用CreateFileMapping函數,而且參數相同。內核會(huì )根據名字來(lái)判斷是否已經(jīng)存在一個(gè)內存映射文件對象,如果創(chuàng )建了就返回原來(lái)的對象的句柄。接下去就不用細說(shuō)了。參照5.1去執行就可以了。要注意的是進(jìn)程B調用CreateFileMapping函數后要按如下代碼檢驗函數執行結果:
HANDLE hMap;
hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
1000,
Labc);
if (hMap == NULL || GetLastError() != ERROR_ALREADY_EXISTS)
{
MessageBox(Lcreate file mapping fail);
return;
}
6、分配大的虛擬地址空間
可以用內存映射文件來(lái)分配大的虛擬地址空間。也可以直接調用VirtualAlloc函數來(lái)分配。VirtualAlloc函數是最底層的分配虛擬地址空間的函數。它會(huì )在調用進(jìn)程內分配符合條件的地址空間并且自動(dòng)用0初始化提交的存儲器。傳遞一個(gè)你希望的虛擬地址空間的首地址給參數1(如果為0,那么內核自動(dòng)查找一個(gè)符合條件的空間),參數2為大?。▎挝唬鹤止潱?,參數3為分配類(lèi)型(提交還是保留),參數4為保護標志(只讀、讀寫(xiě)、執行等)。函數返回分配的地址空間的首地址。在進(jìn)程地址空間中每個(gè)分配的塊有三種狀態(tài):可用、保留、提交。參數3就是指明塊的狀態(tài)。我在做實(shí)驗時(shí)發(fā)現,給參數1傳遞非0值均不成功,即使傳遞0給參數1讓內核自動(dòng)查找,得到的返回值再次用于參數1也不成功。釋放這個(gè)虛擬地址空間調用VirtualFree函數。VirtualFree函數參數1指定首地址,參數2指定大小,參數3指定釋放類(lèi)型(撤銷(xiāo)提交、釋放)。函數成功返回真,失敗返回假。參數3有兩個(gè)標志,并且不能復合。當指定撤銷(xiāo)提交標志(MEM_DECOMMIT)時(shí),函數將取消這個(gè)虛擬地址空間的物理內存的映射,但是保留這塊虛擬地址空間。如果這個(gè)虛擬地址空間沒(méi)有提交函數也不會(huì )失敗返回。當指定釋放標志(MEM_RELEASE)時(shí),如果這塊虛擬地址空間含有同樣的標志(保留或者提交)。函數將釋放這塊虛擬地址空間。如果這個(gè)虛擬地址空間有一部分提交了,其它部分沒(méi)有提交,那么必須先調用此函數,并傳遞撤銷(xiāo)提交標志,先將提交的這部分取消物理內存映射。然后再次調用此函數,傳遞釋放標志。這樣整個(gè)虛擬地址空間就都能夠釋放了。關(guān)于虛擬地址空間還有其它函數,比如VirtualQuery、VirtualProtect。在這里就不介紹了,請參見(jiàn)Windows CE.NET幫助。
作者注:
《進(jìn)程、線(xiàn)程和內存管理》講解的內容是我根據以前在PC機Windows操作系統中掌握的相關(guān)知識,又查看了Windows CE.NET的幫助文檔和MSDN中Technical Articles和knowledge Base而得出的結論。遺憾的是Windows CE.NET的幫助文檔介紹的太簡(jiǎn)單,我只能把掌握的知識和查看到的知識相結合,另外我還做了一些實(shí)驗。我感謝瀏覽此文章的各位Windows CE下開(kāi)發(fā)者,如果你們認為有哪些地方說(shuō)的不正確的,希望指出來(lái)讓我改正錯誤。讓更多的人看到的是準確無(wú)誤的文章。
評論