Windows下記憶體對映檔案的工作原理及使用方法

2021-06-18 11:00:06 字數 3832 閱讀 6308

一、引言

win32 api為我們提供了一種進行檔案操作的高效途徑,即記憶體對映檔案。記憶體對映檔案允許我們在win32程序的虛擬位址空間中保留一段記憶體區域,把目標檔案對映到這段虛擬記憶體之中。我們可以用訪問記憶體資料的方式直接操作檔案中的資料,就好像這些資料放在記憶體中一樣。而實際上,我們並沒有、也不需要呼叫api函式來讀寫檔案,更不需要自己提供任何緩衝演算法,作業系統將會為我們完成這些工作。使用記憶體對映檔案能給程式開發工作提供極大的方便,程式的執行效率也非常高。

記憶體對映檔案在windows nt和windows95中的實現機制略有不同,下面主要介紹windows95下記憶體對映檔案的工作原理及使用方法。

二、windows95如何管理win32程序的記憶體空間

記憶體對映檔案的實現與windows95的記憶體管理有密切的關係,因此先討論一下windows95在執行win32程序時的記憶體管理與劃分。

在windows3.x下,所有windows應用程式共享單一的位址空間,任何程序都能夠對這一空間中屬於其他程序的記憶體進行讀寫操作,甚至可以訪問作業系統本身的重要資料。在這種環境中,編寫不當的應用程式或者帶有惡意的應用程式,就可能破壞其他程式的資料或**,使得系統執行不正常,嚴重時甚至會導致系統崩潰。

在實現了win32的作業系統windows nt和windows95中,每個win32程序擁有自己的位址空間,乙個win32程序不能訪問另乙個程序位址空間的私有資料,兩個程序可以用具有相同值的指標定址,但所讀寫的只是它們各自的資料,這樣就大大減少了程序之間的相互干擾,增強了系統的健壯性;另一方面,每個win32程序擁有4gb的位址空間,但並不代表它真正擁有4gb的實際物理記憶體,而只是作業系統利用cpu的記憶體分頁功能提供的虛擬位址空間。在一般情況下,絕大多數虛擬位址並沒有物理記憶體與之對應,在真正可以使用這些位址空間之前,還要由作業系統提供實際的物理記憶體。為虛擬位址提供實際物理記憶體的過程叫做「提交」(commit)。在不同情況下,系統提交的物理記憶體的型別是不同的,可能是ram,也可能是硬碟模擬的虛擬記憶體。

windows95對win32程序位址空間的劃分如下:

位址空間底部的4mb由windows95用來維護與dos和16位windows的相容性。理想情況下,win32程序應該不能訪問這段記憶體,但由於實現上的困難,windows95只能保護低端從0x00000000到0x00000fff的4kb區域,這4kb區間用來捕獲null指標。從0x80000000到0xbfffffff的1gb空間由所有的win32程序共享,記憶體對映檔案就使用這段位址空間。高階的1gb空間由windows95自己使用,不像windows nt那樣,這段空間也沒有受到保護,任何程序都可能破壞其中的資料。

三、記憶體對映檔案的工作原理

記憶體對映檔案分三種情況,第一種是可執行檔案的記憶體對映,主要由windows95自身使用;第二種是資料檔案的記憶體對映;最後一種是借助於頁面交換檔案的記憶體對映。應用程式可以使用後面兩種記憶體對映檔案。

1、可執行檔案的記憶體對映

windows95在執行乙個win32應用程式時使用記憶體對映檔案,它為將要執行的exe檔案保留足夠大的位址空間。一般情況下,這段空間是從win32程序的載入位址0x00400000開始,系統給這段空間提交的物理儲存就是硬碟上的exe檔案本身。做好各項準備工作後,系統開始執行這個程式。剛開始,程式的**並不在ram中,執行程式入口的第一條指令時會產生乙個頁面異常,系統捕獲到這個異常後,分配一塊ram,將其對映到0x00400000處,並把實際**讀入其中,然後繼續執行。以後在執行到不在ram中的**時,同樣會產生頁面異常,從而系統有機會讀入這些**。系統以類似的方式處理win32dll,只是dll被對映到的位址空間是由所有win32程序共享的。

當使用者執行同乙個應用程式的第二個例項時,系統知道程式已經有乙個例項了,exe檔案的**和資料已經被讀到ram中,系統只需要把這段ram再對映到新程序的位址空間就行了,這就實現了共享ram中的**和資料。事實上,這種共享只是針對唯讀資料,一旦出現程序改寫自身**和資料,作業系統會把被修改資料所在頁面拷貝乙份,分配給執行寫操作的程序,從而避免了多個例項之間的相互干擾。

當然,作業系統執行乙個win32應用程式的實際過程非常複雜,上面所描述的只是工作原理。我們可以用softicefor windows95來驗證作業系統是以對映檔案的方式來執行乙個應用程式的:使用wldr第一次調入乙個應用程式如notepad時,softice被啟用。它所列出的程式的入口**處全是invalid(無效),這表明將要執行的**所在頁面並不在ram之中。按下f8,單步執行一條指令,螢幕上立刻列出了真正的程式指令,這是因為指令執行時首先產生了乙個頁面異常,作業系統在處理頁面異常時,將**讀入ram之中。softice再次被啟用時,就能看見剛讀入的指令了。進一步檢查還可以發現,系統每次唯讀乙個頁面(4kb)到ram中,以便盡量節約記憶體。我們再用wldr調入notepad的第二個例項,這一次softice被啟用後列出的入口**不再是invalid,而是真正的程式指令。由於softice是系統級的偵錯程式,用它修改記憶體中的應用程式時,作業系統並不做頁面拷貝。我們將notepad的入口**做一點改動,然後再用wldr調入第三個例項。這次可以發現,列出的入口**是剛剛修改過的,而實際的exe檔案並無任何變化,這表明,作業系統把同一塊ram中的程式**對映到多個程序的位址空間中,從而實現了共享ram中的程式**。

2、資料檔案的記憶體對映

資料檔案記憶體對映的工作原理與可執行檔案的記憶體對映原理是一樣的。首先把資料檔案的一部分對映到虛擬位址空間(對映到的區域是在0x80000000-0xbfffffff內),但不提交ram,訪問這段記憶體的指令同樣會產生頁面異常。作業系統捕獲到這個異常後,分配一頁ram,並把它對映到當前程序發生異常的位址處,然後系統把檔案中相應的資料讀到這個頁面中,繼續執行剛才產生異常的指令。這就是應用程式自己不需要呼叫檔案i/o函式的原因。

3、基於頁面交換檔案的記憶體對映

記憶體對映檔案的第三種情況是基於頁面交換檔案的。乙個win32程序可以利用記憶體對映檔案在win32程序共享的位址空間中保留一塊區域,這塊區域與系統的頁面交換檔案相聯絡。我們可以用它來儲存臨時資料,但更常見的用法是,利用它與其他win32程序進行通訊。事實上,win32實現多程序間通訊的各種方法都是通過記憶體對映檔案來實現的,例如postmessage()函式或sendmessage()函式,在內部都使用了記憶體對映檔案。

四、使用記憶體對映檔案的方法

1、利用記憶體對映檔案進行檔案i/o操作,進行檔案i/o操作需要下面幾個步驟:

步驟一:呼叫createfile()函式,以適當的方式建立或開啟乙個檔案核心物件;

步驟三:建立了檔案對映核心物件後,呼叫mapviewoffile()函式,告訴系統把檔案的哪一部分對映到程序的位址空間中,以何種方式對映;

步驟四:利用mapviewoffile()函式返回的指標來使用檔案資料;

步驟五:操作完畢後,呼叫unmapviewoffile()函式,告訴系統撤銷對檔案對映核心物件的對映;

步驟六:使用closehandle()函式關閉檔案對映核心物件;

步驟七:使用closehandle()函式關閉檔案核心物件;

各個api函式的詳細說明請參考windows95sdk或一些程式設計工具的聯機幫助。

2、利用記憶體對映檔案實現win32程序間的通訊

在windows95下,乙個程序開啟的檔案對映物件的對映區對所有的win32程序都是可視的,並且對映區的位址對所有win32程序都是一樣的。乙個程序可以開啟乙個檔案,建立檔案對映核心物件,用mapviewoffile()函式開啟檔案檢視,然後將檔案對映的位址傳給另乙個程序,第二個程序就可以讀出檔案中的資料。這種方法需要進行各程序間的同步,實現起來較困難。並且在windows nt中,乙個對映區在不同的win32程序空間中對應的位址不同,因此為了與windows nt相容,盡量不要使用這種方法。

第二種方法是兩個程序使用同一檔案對映核心物件,開啟各自的檢視,或者父程序把自己建立的檔案對映核心物件繼承給子程序使用。這種方法比較安全有效。

windows 核心程式設計下的記憶體對映檔案

虛擬記憶體實現的硬體基礎是分頁機制,關於分頁機制本文再此不做介紹。虛擬記憶體實現的另外乙個重要基礎是區域性性原理。區域性性是指程式總是趨向於使用最近使用過的資料和指令,也就是說程式執行時所訪問的儲存器位址分布是相對集中的。區域性性原理是應用虛擬記憶體提公升效能的主要原因,也是虛擬記憶體卻別與記憶體對...

使用windows記憶體 記憶體對映檔案

和虛擬記憶體一樣,記憶體對映檔案可以用來保留乙個程序位址區域 但是,與虛擬記憶體不同,它提交的不是物理記憶體或是虛擬頁檔案,而是硬碟上的檔案。將檔案對映成記憶體,我們可以像使用內 存一樣使用檔案.使用場合 它有三個主要用途 系統載入exe和dll檔案 作業系統就是用它來載入exe和dll檔案建立程序...

windows記憶體對映

為了說明記憶體對映的使用方式,下面是個人寫的例子 首先建立了乙個大於2gb的檔案,並寫入一定量地測試資料,如下 然後讀取檔案中的資料測試,如下 最後採用記憶體對映的方式對這個大檔案中的字串翻轉,並讀取字串測試,如下 在記憶體對映時,採用多次mapviewoffile將大檔案分開多次對映到記憶體中,這...