新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > WinCE線程和內(nèi)存管理

WinCE線程和內(nèi)存管理

作者:付林林 時(shí)間:2008-08-25 來源:中國IT實(shí)驗(yàn)室 收藏

  同其它Windows操作系統(tǒng)一樣,.NET也支持32位虛擬內(nèi)存機(jī)制、按需分配內(nèi)存和內(nèi)存映射文件等。但是與其它Windows操作系統(tǒng)又有明顯的不同。畢竟是一種實(shí)時(shí)性的操作系統(tǒng),在方面必須要比其它Windows操作系統(tǒng)更節(jié)約物理內(nèi)存和虛擬地址空間。在API方面,為了便于移植程序,和其它Windows操作系統(tǒng)函數(shù)聲明基本一致,這使一個(gè)在其它Windows下開發(fā)的程序員可以直接使用早就熟悉的API函數(shù), 但是CE下的原理開發(fā)者還是應(yīng)該熟悉的。

本文引用地址:http://m.butianyuan.cn/article/87322.htm

  1、ROM和RAM

  對于早期采用的存儲設(shè)備一般采用ROM + RAM ,在ROM中存放的所有文件可以是壓縮的也可以是不壓縮的,這取決于OEM(原始設(shè)備制造商)。OEM在定制CE內(nèi)核時(shí)可以設(shè)置一個(gè)標(biāo)志告訴ROM鏡像制作工具(romimage.exe)是否壓縮文件。對于ROM中存放的模塊(DLL、EXE文件)來說,如果是壓縮的,模塊在運(yùn)行前先解壓并全部存放到RAM中。如果是不壓縮的,并且ROM介質(zhì)支持線性訪問(line-accessed),就可以本地執(zhí)行(executed in place,縮寫為XIP)。利用本地執(zhí)行方式運(yùn)行應(yīng)用程序、DLL的優(yōu)點(diǎn)是:采用這種技術(shù)在加載EXE或DLL時(shí),其中的代碼段數(shù)據(jù)不加載到物理內(nèi)存中,內(nèi)核只是分配虛擬地址空間給代碼段,當(dāng)執(zhí)行代碼時(shí)內(nèi)核會到實(shí)際存放在ROM存儲設(shè)備上的文件中尋找代碼并執(zhí)行。采用這樣的技術(shù)既可以節(jié)省可用內(nèi)存又可以減少加載的時(shí)間。但是這種技術(shù)有一定的局限性,如果要讓CPU到ROM中去尋找代碼執(zhí)行,那么ROM介質(zhì)必須支持線性訪問,這就要求ROM介質(zhì)支持線性訪問,而不是塊訪問。XIP這種加載方式的缺點(diǎn)就是執(zhí)行速度相對較慢,CPU訪問ROM的速度肯定遠(yuǎn)慢于訪問RAM的速度。

  基于Windows CE的產(chǎn)品開始采用FLASH、IDE等永久存儲設(shè)備時(shí),內(nèi)核鏡像(.bin)和其它應(yīng)用程序文件開始存放到永久存儲設(shè)備中而不是ROM中,這不僅僅是因?yàn)橛脖P或者FLASH的I/O速度比ROM快,更因?yàn)楝F(xiàn)在的內(nèi)核包含的功能多并且文件數(shù)量增加,因而需要的存儲空間很大,一般都在20MB左右。再加上其它開發(fā)商開發(fā)的應(yīng)用程序文件,要求的空間就更大了。CE啟動時(shí)內(nèi)核鏡像由加載程序解壓并將系統(tǒng)文件加載到RAM的NK,NK是在config.bib中定義的一段RAM區(qū)域,專用于保存內(nèi)核鏡像解壓出來的所有文件。Windows CE將NK看作是ROM,當(dāng)執(zhí)行一個(gè)應(yīng)用程序時(shí),CE內(nèi)核將這個(gè)應(yīng)用程序需要的系統(tǒng)DLL(在NK中保存)加載到Slot 1(地址范圍0x0200 0000-0x03FF FFFF,在Windows CE.NET中Slot 1專用于XIP DLL使用)。Slot 1是一段虛擬地址,當(dāng)CPU執(zhí)行DLL的代碼時(shí),CPU會根據(jù)地址映射關(guān)系到NK中尋找實(shí)際的代碼執(zhí)行,因?yàn)镹K是一段實(shí)際的物理內(nèi)存,I/O速度非???,所以相對于在ROM中執(zhí)行,DLL的運(yùn)行效率得到很大提高。


  非XIP DLL在加載時(shí)CE內(nèi)核會在調(diào)用DLL的進(jìn)程的地址空間中申請足夠大的地址空間,并且執(zhí)行代碼時(shí)按需提交物理內(nèi)存。

  RAM和ROM文件系統(tǒng)是Windows CE默認(rèn)的文件系統(tǒng)。RAM文件系統(tǒng)的優(yōu)點(diǎn)是支持文件壓縮、支持事務(wù)機(jī)制(和數(shù)據(jù)庫中的事務(wù)機(jī)制相似)、數(shù)據(jù)I/O較快。Windows CE.NET啟動時(shí)把除了NK以外的RAM分為對象存儲(object store)區(qū)域和應(yīng)用程序內(nèi)存(program memory)區(qū)域,并且默認(rèn)各使用一半RAM。在基于Windows CE的設(shè)備沒有采用永久存儲器之前,對象存儲的作用相當(dāng)于永久存儲器,對象存儲區(qū)域采用RAM文件系統(tǒng)來保存文件,對象存儲中可以存儲的對象類型有文件、目錄、數(shù)據(jù)庫、記錄、數(shù)據(jù)庫卷。默認(rèn)在對象存儲中存儲的對象全部是壓縮的。當(dāng)整個(gè)系統(tǒng)關(guān)閉時(shí),設(shè)備的電源還繼續(xù)提供電力給RAM,這樣對象存儲中保存的所有數(shù)據(jù)就不會丟失。應(yīng)用程序內(nèi)存區(qū)域留給所有應(yīng)用程序運(yùn)行時(shí)使用?;赪indows CE的設(shè)備采用永久存儲器后,對象存儲的作用就被永久存儲器替代了,所以采用永久存儲器后,應(yīng)該減小對象存儲區(qū)域的大小。如果定制的Windows CE的內(nèi)核包含了資源管理器(explorer.exe),那么打開“控制面板”,在“系統(tǒng)”-“內(nèi)存”中,可以調(diào)節(jié)這兩個(gè)存儲區(qū)域的比例?;瑝K向左,則釋放對象存儲區(qū)域的一些可用內(nèi)存并將這些內(nèi)存劃到應(yīng)用程序內(nèi)存區(qū)域中。滑塊向右則相反。

  2、內(nèi)存結(jié)構(gòu)

  Windows CE.NET只能管理512MB的物理內(nèi)存和4GB大小的虛擬地址空間。不同的CPU內(nèi)存管理方法也不同。對于MIPS和SHX系列CPU來說,物理地址映射是由CPU完成的,CE內(nèi)核可以直接訪問512MB的物理內(nèi)存。對于x86系列和ARM系列的CPU來說,在內(nèi)核啟動過程中它會將現(xiàn)有物理內(nèi)存地址全部映射到0x8000 0000以上的虛擬地址空間中供內(nèi)核以后使用。OEM可以通過OEMAddressTable來詳細(xì)定義虛擬地址和物理地址的映射關(guān)系。OEMAddressTable本身并不是一個(gè)文件,它只是存在于其它文件中描述虛擬地址和實(shí)際物理地址的映射關(guān)系的數(shù)據(jù)。比如文件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以上為內(nèi)核使用部分,0x8000 0000以下為應(yīng)用程序使用部分。詳細(xì)見下表:




圖1 Windows CE.NET內(nèi)存結(jié)構(gòu)


  3、進(jìn)程地址空間結(jié)構(gòu)

  進(jìn)程地址空間結(jié)構(gòu)如圖2所示。這個(gè)圖源至MSDN。Windows CE.NET同以前版本的Windows CE操作系統(tǒng)在進(jìn)程地址空間上有所不同,以前的Windows CE把XIP DLL也加載到進(jìn)程的32MB地址空間中,而Windows CE.NET把XIP DLL單獨(dú)加載到Slot 1中,這樣對于每個(gè)進(jìn)程來說,它總的地址空間就大了一倍,也就是64MB。這個(gè)問題我在講解進(jìn)程的時(shí)候提到過。

  當(dāng)一個(gè)應(yīng)用程序啟動時(shí),內(nèi)核為這個(gè)程序選擇一個(gè)空閑的槽(Slot),并且加載所有的代碼、資源,并分配堆棧,加載DLL等。當(dāng)這個(gè)進(jìn)程得到CPU使用權(quán)時(shí),它的整個(gè)地址空間被內(nèi)核映射到Slot 0,也就是當(dāng)前進(jìn)程使用的地址空間,然后開始運(yùn)行。圖中給出的地址實(shí)際上是經(jīng)過映射到Slot 0之后的結(jié)構(gòu)。從圖中可以看出,進(jìn)程首先加載代碼段,因?yàn)槊總€(gè)進(jìn)程最低部64KB作為保留區(qū)域,所以代碼段從0x0001 0000開始,內(nèi)核為代碼段分配足夠的虛擬地址空間后,接著分配空間為只讀數(shù)據(jù)和可讀/可寫數(shù)據(jù),接著分配空間為資源數(shù)據(jù),之后分配空間為默認(rèn)堆和棧。非XIP DLL從進(jìn)程最高地址向下開始加載。非XIP DLL的加載按如下規(guī)則:內(nèi)核先檢查要加載的DLL是否被其它進(jìn)程加載過,如果加載過,就做一個(gè)地址的重定位。這樣就避免了整個(gè)系統(tǒng)內(nèi)多次加載相同DLL。如果沒有加載過,就按照從槽的高地址到槽的低地址的順序查找空閑的地址空間。然后分配足夠的地址空間用于加載DLL。因?yàn)槊總€(gè)進(jìn)程在執(zhí)行前都要映射到Slot 0,而且進(jìn)程使用的所有DLL可能來自不同的槽(Slot),為避免所有使用的DLL在映射到Slot 0中出現(xiàn)地址空間沖突的現(xiàn)象,內(nèi)核的加載器(Loader)在加載DLL時(shí)會查找所有槽中加載的DLL的地址,保證在映射到Slot 0時(shí)不會發(fā)生地址沖突現(xiàn)象。假如系統(tǒng)內(nèi)有兩個(gè)進(jìn)程,進(jìn)程A只加載了DLL A,進(jìn)程B需要加載DLL A和DLL B,那么進(jìn)程B會留出DLL A的地址空間,然后加載DLL B,也就是說進(jìn)程B映射到Slot 0時(shí),DLL A的地址空間和DLL B的地址空間是相鄰的,不會發(fā)生沖突。好在Windows CE下DLL都很小,而且一個(gè)應(yīng)用程序使用的DLL多數(shù)是系統(tǒng)的DLL(存在于Slot 1)。所以目前來看進(jìn)程的地址空間還夠用。


圖2 進(jìn)程地址空間結(jié)構(gòu)

   4、堆和棧

  堆是一段連續(xù)的較大的虛擬地址空間。應(yīng)用程序在堆中可以動態(tài)地分配、釋放所需大小的內(nèi)存塊。利用堆的優(yōu)點(diǎn)是在一定范圍內(nèi)減小了內(nèi)存碎塊。而且開發(fā)者分配內(nèi)存塊前不必去了解CPU的類型。因?yàn)椴煌腃PU分頁大小不相同,每個(gè)內(nèi)存頁可能是1KB、4KB或更多。在堆內(nèi)分配內(nèi)存塊可以是任意大小的,而直接分配內(nèi)存就必須以內(nèi)存頁為單位。當(dāng)一個(gè)應(yīng)用程序啟動時(shí),內(nèi)核在進(jìn)程所在的地址空間中為進(jìn)程分配一個(gè)默認(rèn)192KB大小的虛擬地址空間,但是并不立刻提交物理內(nèi)存。如果在運(yùn)行當(dāng)中192KB不能滿足需求,那么內(nèi)核會在進(jìn)程地址空間中重新查找一個(gè)足夠大小的空閑的地址空間,然后復(fù)制原來堆的數(shù)據(jù),最后釋放原來的堆所占的地址空間。這是因?yàn)槟J(rèn)的堆的高地址處還有棧,所以必須重新分配一個(gè)。Windows CE.NET的堆有明顯的缺點(diǎn),不同于其它Windows操作系統(tǒng)下的堆管理,在Windows CE.NET創(chuàng)建的堆中創(chuàng)建的內(nèi)存塊不能夠移動,多次創(chuàng)建內(nèi)存塊、釋放內(nèi)存塊會產(chǎn)生內(nèi)存碎塊,這樣的話當(dāng)需要分配一個(gè)大一點(diǎn)的連續(xù)的內(nèi)存塊時(shí),本來空閑的內(nèi)存塊加起來足夠用,但是這些內(nèi)存塊是分隔的,不符合要求。像Windows 2000或98的內(nèi)核會頻繁的移動分散的正使用的內(nèi)存塊,使它們聚集在一起。這也是為什么有時(shí)需要句柄而不用指針的原因。由于Windows CE.NET的堆的缺點(diǎn),開發(fā)者如果要頻繁的在堆中創(chuàng)建、釋放內(nèi)存塊的話,最好自己創(chuàng)建一個(gè)單獨(dú)的堆,而不用默認(rèn)的堆。而且我還建議最好直接在全局地址空間中(0x4200 0000到0x7FFF FFFF)分配所需地址空間。因?yàn)檫M(jìn)程地址空間可用的實(shí)在太小了。關(guān)于堆函數(shù)我在這就不多說了,和其它Windows操作系統(tǒng)堆API基本一致。請參考幫助文檔。

  棧也是一段連續(xù)的虛擬地址空間,和堆相比空間要小的多,它是專為函數(shù)使用的。當(dāng)調(diào)用一個(gè)函數(shù)時(shí)(包括),內(nèi)核會產(chǎn)生一個(gè)默認(rèn)的棧,并且內(nèi)核會立刻提交少量的物理內(nèi)存(也可以禁止內(nèi)核立刻提交物理內(nèi)存)。棧的大小和CPU有關(guān),一般為64KB,并且保留頂部2KB為了防止溢出??梢孕薷臈5拇笮?,具體修改方法在講解的時(shí)候已經(jīng)說過了,這里就不再重復(fù)了。修改棧的大小一般時(shí)候不會發(fā)生,如果采用在編譯鏈接時(shí)修改大小,那么所有棧的大小都會改變,這不太合理。實(shí)際開發(fā)中最好不要在棧中分配很大、很多的內(nèi)存塊,如果分配的內(nèi)存塊超過了默認(rèn)棧的限制,那么會引起訪問非法并且內(nèi)核會立刻終止進(jìn)程。最好在進(jìn)程的堆中分配大的內(nèi)存塊并且在函數(shù)返回前釋放,或者在創(chuàng)建時(shí)指定棧的大小。

  5、內(nèi)存映射文件

  與虛擬內(nèi)存一樣,內(nèi)存映射文件用來保留一個(gè)地址空間,并提交物理存儲器。早期的內(nèi)存映射文件并不是提交物理內(nèi)存供調(diào)用者使用,而是提交永久存儲器上的文件數(shù)據(jù)。當(dāng)然操作系統(tǒng)會為永久存儲器保留一個(gè)讀緩沖區(qū),這樣讀取文件數(shù)據(jù)就快多了。內(nèi)存映射文件的特點(diǎn)使它很適合于加載EXE或DLL文件。這樣可以節(jié)省內(nèi)存又減少了加載所需時(shí)間。還可以使用它來映射大容量的文件,這樣就不必在讀取文件數(shù)據(jù)前設(shè)置很大的緩沖區(qū)。另外內(nèi)存映射文件常用于進(jìn)程間通信,也是進(jìn)程間通信的主要手段,其它進(jìn)程之間通信機(jī)制都是基于內(nèi)存映射文件來實(shí)現(xiàn)。為了更快的在進(jìn)程之間通信,現(xiàn)在的內(nèi)存映射文件也可以提交物理內(nèi)存,這樣內(nèi)存映射文件既可以提交物理內(nèi)存又可以提交文件。
  Windows CE.NET同樣支持無名和有名的內(nèi)存映射文件。我建議在開發(fā)軟件的過程中,如果需要讀寫大容量的文件,或者需要在不同進(jìn)程內(nèi)的線程之間通信,最好采用內(nèi)存映射文件,而且最好在全局地址空間內(nèi)(0x4200 0000到0x7FFF FFFF)分配。這會使我們事半功倍。

  5.1 映射數(shù)據(jù)文件

  第一步:調(diào)用CreateFileForMapping函數(shù)。在Windows CE.NET中推薦使用這個(gè)函數(shù)替代CreateFile函數(shù)。CreateFileForMapping函數(shù)由內(nèi)核執(zhí)行并創(chuàng)建文件,它也可以打開由CreateFile函數(shù)創(chuàng)建的文件。其參數(shù)同CreateFile相似。參數(shù)1指定文件路徑,注意文件路徑的格式是沒有盤符的,參數(shù)2指定訪問方式(讀或?qū)懀瑓?shù)3指定共享模式,參數(shù)4指定安全屬性(必須設(shè)置為NULL),參數(shù)5指定是創(chuàng)建還是打開文件,參數(shù)6指定文件屬性,參數(shù)7忽略。具體參數(shù)細(xì)節(jié)參見Windows CE.NET幫助。函數(shù)返回創(chuàng)建或者打開的文件的句柄。

  第二步:調(diào)用CreateFileMapping函數(shù)。這個(gè)函數(shù)創(chuàng)建一個(gè)無名的或者有名的內(nèi)存映射文件對象。參數(shù)1為文件句柄。這個(gè)值由CreateFileForMapping函數(shù)返回。參數(shù)2為安全屬性(必須設(shè)置為NULL),參數(shù)3指定要映射的文件的保護(hù)屬性(只讀或者讀寫),參數(shù)4和參數(shù)5共同用于指定要映射的文件的大小。文件的容量過大將導(dǎo)致32位整數(shù)也不能表示,所以這里用64位變量表示,其中參數(shù)4為高32位數(shù),參數(shù)5為低32位數(shù)。最后一個(gè)參數(shù)指定內(nèi)存映射文件的名稱。這里可以設(shè)置為NULL,表示不需要名字。

  第三步:調(diào)用MapViewOfFile函數(shù)。這個(gè)函數(shù)用于保留一段足夠的地址空間,并且將永久存儲器上的文件數(shù)據(jù)映射到這個(gè)地址空間。映射后這段地址空間又叫做文件視圖,映射范圍可以是全部文件,也可以是部分文件。這里需要注意的是如果文件很大,那這個(gè)函數(shù)將在全局地址空間內(nèi)分配地址空間。參數(shù)1指定內(nèi)存映射文件對象的句柄,這個(gè)值由CreateFileMapping函數(shù)返回。參數(shù)2和CreateFileMapping函數(shù)中參數(shù)3很相似,都是用于限定訪問權(quán)限。參數(shù)3和參數(shù)4共同用于指定映射區(qū)域的開始位置。其中參數(shù)3為高32位數(shù),參數(shù)4為低32位數(shù)。參數(shù)5指定映射區(qū)域的大小。需要注意的是參數(shù)3和參數(shù)4指定的64位數(shù)開始位置可以不是64KB的倍數(shù)。而其它Windows操作系統(tǒng)就必須限制以64KB為單位。另外還要注意的是幫助文檔中說不能保證一個(gè)文件的映射視圖是連續(xù)的,并建議為了防止訪問非法,應(yīng)該加入結(jié)構(gòu)化異常處理機(jī)制。這個(gè)可能性我認(rèn)為很小,一般對于大于2MB的虛擬地址空間的申請,內(nèi)核都會在全局地址空間中分配。全局地址空間(0x4200 0000到0x7FFF FFFF)近1GB的空間應(yīng)該足夠用了。畢竟Windows CE下的文件都很小。不過在代碼中加入結(jié)構(gòu)化異常處理也不是壞事。我們應(yīng)該養(yǎng)成凡是讀寫文件數(shù)據(jù)時(shí)都加入結(jié)構(gòu)化異常處理的習(xí)慣。

  第四步:進(jìn)行讀/寫操作。MapViewOfFile函數(shù)如果成功執(zhí)行,那么返回映射視圖的首地址。這時(shí)就可以把視圖當(dāng)成是一個(gè)緩沖區(qū),開始讀或?qū)懖僮髁恕?/p>

  第五步:執(zhí)行結(jié)束工作。先調(diào)用UnmapViewOfFile函數(shù)撤銷文件映射視圖。參數(shù)只有一個(gè),指定視圖首地址。然后調(diào)用CloseHandle函數(shù)關(guān)閉內(nèi)存映射文件對象,參數(shù)為句柄。最后再次調(diào)用CloseHandle函數(shù),關(guān)閉打開的文件的句柄。

  5.2 進(jìn)程之間通信

  進(jìn)程之間有時(shí)需要通信。系統(tǒng)提供的進(jìn)程之間的通信機(jī)制比如COM、剪貼板等,在底層實(shí)現(xiàn)上都是利用內(nèi)存映射文件技術(shù)。其實(shí)進(jìn)程之間通信的思路很簡單,在這里我順便講一下。在其它Windows操作系統(tǒng)中,每個(gè)進(jìn)程獨(dú)自占有4GB的地址空間,高2GB是內(nèi)核的地址空間,而低2GB是進(jìn)程的地址空間。一個(gè)進(jìn)程所能訪問的所有低2GB地址都是自己的地址空間,當(dāng)訪問內(nèi)核地址空間時(shí)就會受到內(nèi)核的限制。這樣一個(gè)進(jìn)程當(dāng)然無法訪問其它進(jìn)程了。為解決進(jìn)程間通信的問題,內(nèi)存映射文件技術(shù)被利用作為解決方案。原來內(nèi)存映射文件只映射類似磁盤一類的存儲器上的文件。而為了更快速地在進(jìn)程之間通信,內(nèi)存映射文件還可以提交物理內(nèi)存。實(shí)現(xiàn)方法是通過訪問同一個(gè)內(nèi)存映射文件對象(映射到物理內(nèi)存),兩個(gè)進(jìn)程或多個(gè)進(jìn)程就能夠訪問到同一塊物理內(nèi)存,這樣一個(gè)進(jìn)程寫到物理內(nèi)存的數(shù)據(jù),其它進(jìn)程就能夠看到了。而Windows CE雖然每個(gè)進(jìn)程只占有32MB的地址空間,而且所有進(jìn)程全部處于4GB的地址空間中,但是彼此還是不能夠隨意訪問的。在Windows CE下除了使用內(nèi)存映射文件技術(shù)外,還有一種方法也很適合使用,就是利用對象存儲。對象存儲本身使用RAM文件系統(tǒng),用普通的操作文件的API就可以創(chuàng)建、讀取存在于對象存儲區(qū)域內(nèi)的文件。Windows 目錄就存在于對象存儲區(qū)域內(nèi)。我們可以利用在Windows目錄下創(chuàng)建文件來實(shí)現(xiàn)進(jìn)程間通信。這種方法既實(shí)現(xiàn)簡單,只需調(diào)用幾個(gè)文件API函數(shù),又可以減少通信時(shí)間,因?yàn)閃indows目錄存在于物理內(nèi)存中,數(shù)據(jù)I/O當(dāng)然很快了。利用對象存儲來實(shí)現(xiàn)進(jìn)程之間的通信是我自己想出來的,MSDN或其它文檔并沒有這方面的說明。需要注意的就是對象存儲區(qū)域的大小。另外從實(shí)現(xiàn)的代碼量上看也不如內(nèi)存映射文件技術(shù)。

  下面講解如何利用內(nèi)存映射文件實(shí)現(xiàn)進(jìn)程之間的通信。假設(shè)進(jìn)程A和進(jìn)程B需要通信,那么進(jìn)程A需要先創(chuàng)建一個(gè)內(nèi)存映射文件(之前不必調(diào)用CreateFileForMapping函數(shù)來創(chuàng)建文件,因?yàn)椴恍枰獎?chuàng)建文件)。這個(gè)內(nèi)存映射文件可以是在永久存儲器中,也可以是在內(nèi)存中。為了減小通信時(shí)間,最好提交物理內(nèi)存。進(jìn)程A在調(diào)用CreateFileMapping函數(shù)時(shí),參數(shù)1指定為INVALID_HANDLE_VALUE,這表示這個(gè)內(nèi)存映射文件對象將要把物理內(nèi)存提交到地址空間中。最后一個(gè)參數(shù)一定要指定一個(gè)名字。進(jìn)程B也同樣調(diào)用CreateFileMapping函數(shù),而且參數(shù)相同。內(nèi)核會根據(jù)名字來判斷是否已經(jīng)存在一個(gè)內(nèi)存映射文件對象,如果創(chuàng)建了就返回原來的對象的句柄。接下去就不用細(xì)說了。參照5.1去執(zhí)行就可以了。要注意的是進(jìn)程B調(diào)用CreateFileMapping函數(shù)后要按如下代碼檢驗(yàn)函數(shù)執(zhí)行結(jié)果:

HANDLE hMap;
hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
1000,
L"abc");
if (hMap == NULL || GetLastError() != ERROR_ALREADY_EXISTS)
{
 MessageBox(L"create file mapping fail");
 return;

  6、分配大的虛擬地址空間

  可以用內(nèi)存映射文件來分配大的虛擬地址空間。也可以直接調(diào)用VirtualAlloc函數(shù)來分配。VirtualAlloc函數(shù)是最底層的分配虛擬地址空間的函數(shù)。它會在調(diào)用進(jìn)程內(nèi)分配符合條件的地址空間并且自動用0初始化提交的存儲器。傳遞一個(gè)你希望的虛擬地址空間的首地址給參數(shù)1(如果為0,那么內(nèi)核自動查找一個(gè)符合條件的空間),參數(shù)2為大?。▎挝唬鹤止?jié)),參數(shù)3為分配類型(提交還是保留),參數(shù)4為保護(hù)標(biāo)志(只讀、讀寫、執(zhí)行等)。函數(shù)返回分配的地址空間的首地址。在進(jìn)程地址空間中每個(gè)分配的塊有三種狀態(tài):可用、保留、提交。參數(shù)3就是指明塊的狀態(tài)。我在做實(shí)驗(yàn)時(shí)發(fā)現(xiàn),給參數(shù)1傳遞非0值均不成功,即使傳遞0給參數(shù)1讓內(nèi)核自動查找,得到的返回值再次用于參數(shù)1也不成功。釋放這個(gè)虛擬地址空間調(diào)用VirtualFree函數(shù)。VirtualFree函數(shù)參數(shù)1指定首地址,參數(shù)2指定大小,參數(shù)3指定釋放類型(撤銷提交、釋放)。函數(shù)成功返回真,失敗返回假。參數(shù)3有兩個(gè)標(biāo)志,并且不能復(fù)合。當(dāng)指定撤銷提交標(biāo)志(MEM_DECOMMIT)時(shí),函數(shù)將取消這個(gè)虛擬地址空間的物理內(nèi)存的映射,但是保留這塊虛擬地址空間。如果這個(gè)虛擬地址空間沒有提交函數(shù)也不會失敗返回。當(dāng)指定釋放標(biāo)志(MEM_RELEASE)時(shí),如果這塊虛擬地址空間含有同樣的標(biāo)志(保留或者提交)。函數(shù)將釋放這塊虛擬地址空間。如果這個(gè)虛擬地址空間有一部分提交了,其它部分沒有提交,那么必須先調(diào)用此函數(shù),并傳遞撤銷提交標(biāo)志,先將提交的這部分取消物理內(nèi)存映射。然后再次調(diào)用此函數(shù),傳遞釋放標(biāo)志。這樣整個(gè)虛擬地址空間就都能夠釋放了。關(guān)于虛擬地址空間還有其它函數(shù),比如VirtualQuery、VirtualProtect。在這里就不介紹了,請參見Windows CE.NET幫助。

  作者注:

  《進(jìn)程、線程和內(nèi)存管理》講解的內(nèi)容是我根據(jù)以前在PC機(jī)Windows操作系統(tǒng)中掌握的相關(guān)知識,又查看了Windows CE.NET的幫助文檔和MSDN中Technical Articles和knowledge Base而得出的結(jié)論。遺憾的是Windows CE.NET的幫助文檔介紹的太簡單,我只能把掌握的知識和查看到的知識相結(jié)合,另外我還做了一些實(shí)驗(yàn)。我感謝瀏覽此文章的各位Windows CE下開發(fā)者,如果你們認(rèn)為有哪些地方說的不正確的,希望指出來讓我改正錯(cuò)誤。讓更多的人看到的是準(zhǔn)確無誤的文章。

  付林林:畢業(yè)時(shí)間:2001年,專業(yè):計(jì)算機(jī)。從畢業(yè)起一直從事軟件開發(fā)工作。目前從事 Windows CE 下操作系統(tǒng)內(nèi)核定制和應(yīng)用程序開發(fā)。在兩年的時(shí)間里積累了CE下開發(fā)的一點(diǎn)點(diǎn)經(jīng)驗(yàn)。希望和 CE 下開發(fā)者交流、探討,更希望你們能不吝賜教。

linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)


評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉