一、概念
? 內(nèi)存映射文件是一塊預(yù)訂的地址空間區(qū)域并給區(qū)域調(diào)撥物理存儲(chǔ)器。它的作用與虛擬內(nèi)存相似,但不同的是,內(nèi)存映射文件是磁盤上的文件,而虛擬內(nèi)存是來自系統(tǒng)的頁交換文件。
二、用途
1、使用內(nèi)存映射文件載入并運(yùn)行.exe文件和動(dòng)態(tài)鏈接庫(DLL)文件。
2、可以用來訪問磁盤上的數(shù)據(jù)文件,避免直接對文件進(jìn)行I/O操作和對文件內(nèi)容進(jìn)行緩存
3、可以在同一臺(tái)機(jī)器上的不同進(jìn)程間共享數(shù)據(jù),這也是最高效的方法。
三、映射到內(nèi)存的可執(zhí)行文件和DLL
當(dāng)線程調(diào)用C r e a t e P r o c e s s時(shí),系統(tǒng)將執(zhí)行下列操作步驟:
1) 系統(tǒng)找出在調(diào)用C r e a t e P r o c e s s時(shí)設(shè)定的. e x e文件。如果找不到這個(gè). e x e文件,進(jìn)程將無法創(chuàng)建,C r e a t e P r o c e s s將返回FA L S E。
2) 系統(tǒng)創(chuàng)建一個(gè)新進(jìn)程內(nèi)核對象。
3) 系統(tǒng)為這個(gè)新進(jìn)程創(chuàng)建一個(gè)私有地址空間。
4) 系統(tǒng)保留一個(gè)足夠大的地址空間區(qū)域,用于存放該. e x e文件。該區(qū)域需要的位置在. e x e文件本身中設(shè)定。按照默認(rèn)設(shè)置, . e x e文件的基地址是0 x 0 0 4 0 0 0 0 0(這個(gè)地址可能不同于在6 4位Windows 2000上運(yùn)行的6 4位應(yīng)用程序的地址),但是,可以在創(chuàng)建應(yīng)用程序的. e x e文件時(shí)重載這個(gè)地址,方法是在鏈接應(yīng)用程序時(shí)使用鏈接程序的/ B A S E選項(xiàng)。
5) 系統(tǒng)注意到支持已保留區(qū)域的物理存儲(chǔ)器是在磁盤上的. e x e文件中,而不是在系統(tǒng)的頁文件中。
當(dāng). e x e文件被映射到進(jìn)程的地址空間中之后,系統(tǒng)將訪問. e x e文件的一個(gè)部分,該部分列出了包含. e x e文件中的代碼要調(diào)用的函數(shù)的D L L文件。然后,系統(tǒng)為每個(gè)D L L文件調(diào)用L o a d L i b r a r y函數(shù),如果任何一個(gè)D L L需要更多的D L L,那么系統(tǒng)將調(diào)用L o a d L i b r a r y函數(shù),以便加載這些D L L。每當(dāng)調(diào)用L o a d L i b r a r y來加載一個(gè)D L L時(shí),系統(tǒng)將執(zhí)行下列操作步驟,它們均類似上面的第4和第5個(gè)步驟:
1) 系統(tǒng)保留一個(gè)足夠大的地址空間區(qū)域,用于存放該D L L文件。該區(qū)域需要的位置在D L L文件本身中設(shè)定。按照默認(rèn)設(shè)置, M i c r o s o f t的Visual C++ 建立的D L L文件基地址是0 x 1 0 0 0 0 0 0 0(這個(gè)地址可能不同于在6 4位Windows 2000上運(yùn)行的6 4位D L L的地址)但是,你可以在創(chuàng)建D L L文件時(shí)重載這個(gè)地址,方法是使用鏈接程序的/ B A S E選項(xiàng)。Wi n d o w s提供的所有標(biāo)準(zhǔn)系統(tǒng)D L L都擁有不同的基地址,這樣,如果加載到單個(gè)地址空間,它們就不會(huì)重疊。
2) 如果系統(tǒng)無法在該D L L的首選基地址上保留一個(gè)區(qū)域,其原因可能是該區(qū)域已經(jīng)被另一個(gè)D L L或. e x e占用,也可能是因?yàn)樵搮^(qū)域不夠大,此時(shí)系統(tǒng)將設(shè)法尋找另一個(gè)地址空間的區(qū)域來保留該D L L。
3) 系統(tǒng)會(huì)注意到支持已保留區(qū)域的物理存儲(chǔ)器位于磁盤上的D L L文件中,而不是在系統(tǒng)的頁文件中。
如果由于某個(gè)原因系統(tǒng)無法映射. e x e和所有必要的D L L文件,那么系統(tǒng)就會(huì)向用戶顯示一個(gè)消息框,并且釋放進(jìn)程的地址空間和進(jìn)程對象。
當(dāng)所有的. e x e和D L L文件都被映射到進(jìn)程的地址空間之后,系統(tǒng)就可以開始執(zhí)行. e x e文件的啟動(dòng)代碼。當(dāng). e x e文件被映射后,系統(tǒng)將負(fù)責(zé)所有的分頁、緩沖和高速緩存的處理.
由于exe文件和DLL映射到進(jìn)程空間當(dāng)中,所以它們最終是用虛擬存儲(chǔ)器進(jìn)行管理的。
四、同一個(gè)可執(zhí)行文件或 DLL 的多個(gè)實(shí)例之間的數(shù)據(jù)共享
1 、防止共享數(shù)據(jù)
如果一個(gè)應(yīng)用程序已經(jīng)運(yùn)行一個(gè)實(shí)例了,當(dāng)我們再為這個(gè)應(yīng)用程序創(chuàng)建一個(gè)新的進(jìn)程時(shí),系統(tǒng)只不過是打開另一個(gè)內(nèi)存映射視圖,創(chuàng)建一個(gè)新的進(jìn)程對象,并創(chuàng)建一個(gè)新的線程對象。
在編譯和鏈接程序的時(shí)候,所有的代碼和數(shù)據(jù)都被放在一個(gè)大的實(shí)體中,即 exe 文件或 DLL 庫。
在 .exe 文件中,數(shù)據(jù)位于代碼之后。
當(dāng)應(yīng)用程序的第二個(gè)實(shí)例運(yùn)行時(shí),系統(tǒng)只不過是把包含應(yīng)用程序代碼和數(shù)據(jù)的虛擬內(nèi)存頁面映射到第二個(gè)實(shí)例的地址空間中。
如果某一個(gè)應(yīng)用程序?qū)嵗枰薷臄?shù)據(jù)頁面的一些全局變量等時(shí),系統(tǒng)是怎樣進(jìn)行管理的呢?
系統(tǒng)通過內(nèi)存管理系統(tǒng)的寫時(shí)復(fù)制特性來防止這種情況發(fā)生。
當(dāng)應(yīng)用程序嘗試寫入內(nèi)存映射文件的時(shí)候,系統(tǒng)會(huì)首先截獲此類嘗試,然后為應(yīng)用程序試圖寫入的內(nèi)存頁面分配一塊新的內(nèi)存,然后復(fù)制頁面內(nèi)容,最后讓應(yīng)用程序?qū)懭氲絼偡峙涞膬?nèi)存塊。
2 、共享數(shù)據(jù)
在多個(gè)實(shí)例之間共享靜態(tài)數(shù)據(jù)。
在編譯程序的時(shí)候,編譯器會(huì)將代碼放在一個(gè)名叫 .text 的段中。將未經(jīng)初始化的數(shù)據(jù)放在 .bss 段中,已初始化的數(shù)據(jù)放在 .data 段中。
而每個(gè)段都有一些與之相關(guān)聯(lián)的屬性
READ ??? 可以從該段讀取數(shù)據(jù)
WRITE?? 可以從該段寫入數(shù)據(jù)
EXECUTE ? 可以執(zhí)行該段內(nèi)容
SHARED?? 該段的內(nèi)容為多個(gè)實(shí)例所共享 ( 即關(guān)閉了寫時(shí)復(fù)制機(jī)制 )
#pragma data_seg("Shared")?? // 創(chuàng)建一個(gè)名為shared的段,并將pragma指示符之間所有帶初始值的變量放到這個(gè)新的段中。
volatile LONG g_lApplicationInstances = 0;? // 該變量必須初始化
#pragma data_seg()// 告訴編譯器停止把已初始化的變量放到shared段中,
#pragma comment(linker, "/Section:Shared,S") // 設(shè)置該shared段的屬性, S為共享 R 為只讀 W為寫入
雖然可以創(chuàng)建共享段,但并不鼓勵(lì)使用共享段。原因一是可能會(huì)導(dǎo)致潛在的安全漏洞。二是共享變量意味著一個(gè)應(yīng)用程序中的錯(cuò)誤可能會(huì)影響到另一個(gè)程序。
?