Win 95下記憶體對映檔案的工作原理及使用方法 (轉)

worldblog發表於2007-12-06
Win 95下記憶體對映檔案的工作原理及使用方法 (轉)[@more@] 

Win 95下對映的工作原理及使用方法


  一、引言
   為我們提供了一種進行檔案操作的高效途徑,即記憶體對映檔案。記憶體對映檔案允許我們在WIN32程式的虛擬地址空間中保留一段記憶體區域,把目標檔案對映到這段虛擬記憶體之中。我們可以用存取記憶體資料的方式直接操作檔案中的資料,就好像這些資料放在記憶體中一樣。而實際上,我們並沒有、也不需要API來讀寫檔案,更不需要自己提供任何緩衝演算法,操作將會為我們完成這些工作。使用記憶體對映檔案能給開發工作提供極大的方便,程式的執行也非常高。
  記憶體對映檔案在 NT和Windows95中的實現機制略有不同,下面主要介紹Windows95下記憶體對映檔案的工作原理及使用方法。
  二、Windows95如何管理WIN32程式的記憶體空間
  記憶體對映檔案的實現與Windows95的記憶體管理有密切的關係,因此先討論一下Windows95在執行WIN32程式時的記憶體管理與劃分。
  在Windows3.x下,所有Windows應用程式共享單一的地址空間,任何程式都能夠對這一空間中屬於其他程式的記憶體進行讀寫操作,甚至可以存取本身的重要資料。在這種環境中,編寫不當的應用程式或者帶有惡意的應用程式,就可能破壞其他程式的資料或程式碼,使得系統執行不正常,嚴重時甚至會導致系統崩潰。
  在實現了WIN32的作業系統和Windows95中,每個WIN32程式擁有自己的地址空間,一個WIN32程式不能存取另一個程式地址空間的私有資料,兩個程式可以用具有相同值的指標定址,但所讀寫的只是它們各自的資料,這樣就大大減少了程式之間的相互干擾,增強了系統的健壯性;另一方面,每個WIN32程式擁有4GB的地址空間,但並不代表它真正擁有4GB的實際實體記憶體,而只是作業系統利用的記憶體分頁功能提供的虛擬地址空間。在一般情況下,絕大多數虛擬地址並沒有實體記憶體與之對應,在真正可以使用這些地址空間之前,還要由作業系統提供實際的實體記憶體。為虛擬地址提供實際實體記憶體的過程叫做“提交”(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()函式,以適當的方式建立或開啟一個檔案核心;
  步驟二:把CreateFile()函式返回的檔案控制程式碼作為引數,傳給CreateFileMap()函式,由CreateFileMapping()函式建立一個檔案對映核心物件的適當屬性;
  步驟三:建立了檔案對映核心物件後,呼叫MapViewOfFile()函式,告訴系統把檔案的哪一部分對映到程式的地址空間中,以何種方式對映;
  步驟四:利用MapViewOfFile()函式返回的指標來使用檔案資料;
  步驟五:操作完畢後,呼叫UnmapViewOfFile()函式,告訴系統撤銷對檔案對映核心物件的對映;
  步驟六:使用CloseHandle()函式關閉檔案對映核心物件;
  步驟七:使用CloseHandle()函式關閉檔案核心物件;
  各個API函式的詳細說明請參考Windows95SDK或一些工具的聯機幫助。
  2、利用記憶體對映檔案實現WIN32程式間的通訊
  在Windows95下,一個程式開啟的檔案對映物件的對映區對所有的WIN32程式都是可視的,並且對映區的地址對所有WIN32程式都是一樣的。一個程式可以開啟一個檔案,建立檔案對映核心物件,用MapViewOfFile()函式開啟檔案檢視,然後將檔案對映的地址傳給另一個程式,第二個程式就可以讀出檔案中的資料。這種方法需要進行各程式間的同步,實現起來較困難。並且在Windows NT中,一個對映區在不同的WIN32程式空間中對應的地址不同,因此為了與Windows NT相容,儘量不要使用這種方法。
  第二種方法是兩個程式使用同一檔案對映核心物件,開啟各自的檢視,或者父程式把自己建立的檔案對映核心物件繼承給子程式使用。這種方法比較有效。
  第三種方法是建立基於頁面交換檔案的記憶體對映物件。在呼叫CreateFileMapping()函式時,傳遞檔案控制程式碼為0xFFFFFFFF,系統就從頁面交換檔案中提交物理儲存,然後程式之間按照第二種方法進行通訊。這種方法不用事先準備一個特殊的檔案,非常方便。(廣西 王有明)


 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988834/,如需轉載,請註明出處,否則將追究法律責任。

相關文章