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