記憶體對映檔案使用詳細

2021-05-23 05:04:41 字數 4493 閱讀 8269

摘要: 本文通過記憶體對映檔案的使用來對大尺寸檔案進行訪問操作,同時也對記憶體對映檔案的相關概念和一般程式設計過程作了較為詳細的介紹。

引言檔案操作是應用程式最為基本的功能之一,win32 api和mfc均提供有支援檔案處理的函式和類,常用的有win32 api的createfile()、writefile()、readfile()和mfc提供的cfile類等。一般來說,以上這些函式可以滿足大多數場合的要求,但是對於某些特殊應用領域所需要的動輒幾十gb、幾百gb、乃至幾tb的海量儲存,再以通常的檔案處理方法進行處理顯然是行不通的。目前,對於上述這種大檔案的操作一般是以記憶體對映檔案的方式來加以處理的,本文下面將針對這種windows核心程式設計技術展開討論。

記憶體對映檔案概述

記憶體檔案對映也是windows的一種記憶體管理方法,提供了乙個統一的記憶體管理特徵,使應用程式可以通過記憶體指針對磁碟上的檔案進行訪問,其過程就如同對載入了檔案的記憶體的訪問。通過檔案對映這種使磁碟檔案的全部或部分內容與程序虛擬位址空間的某個區域建立對映關聯的能力,可以直接對被對映的檔案進行訪問,而不必執行檔案i/o操作也無需對檔案內容進行緩衝處理。記憶體檔案對映的這種特性是非常適合於用來管理大尺寸檔案的。

在使用記憶體對映檔案進行i/o處理時,系統對資料的傳輸按頁面來進行。至於內部的所有記憶體頁面則是由虛擬記憶體管理器來負責管理,由其來決定記憶體頁面何時被分頁到磁碟,哪些頁面應該被釋放以便為其它程序提供空閒空間,以及每個程序可以擁有超出實際分配物理記憶體之外的多少個頁面空間等等。由於虛擬記憶體管理器是以一種統一的方式來處理所有磁碟i/o的(以頁面為單位對記憶體資料進行讀寫),因此這種優化使其有能力以足夠快的速度來處理記憶體操作。

使用記憶體對映檔案時所進行的任何實際i/o互動都是在記憶體中進行並以標準的記憶體位址形式來訪問。磁碟的週期性分頁也是由作業系統在後台隱蔽實現的,對應用程式而言是完全透明的。記憶體對映檔案的這種特性在進行大檔案的磁碟事務操作時將獲得很高的效益。

需要說明的是,在系統的正常的分頁操作過程中,記憶體對映檔案並非一成不變的,它將被定期更新。如果系統要使用的頁面目前正被某個記憶體對映檔案所占用,系統將釋放此頁面,如果頁面資料尚未儲存,系統將在釋放頁面之前自動完成頁面資料到磁碟的寫入。

對於使用頁虛擬儲存管理的windows作業系統,記憶體對映檔案是其內部已有的記憶體管理元件的乙個擴充。由可執行**頁面和資料頁面組成的應用程式可根據需要由作業系統來將這些頁面換進或換出記憶體。如果記憶體中的某個頁面不再需要,作業系統將撤消此頁面原擁用者對它的控制權,並釋放該頁面以供其它程序使用。只有在該頁面再次成為需求頁面時,才會從磁碟上的可執行檔案重新讀入記憶體。同樣地,當乙個程序初始化啟動時,記憶體的頁面將用來儲存該應用程式的靜態、動態資料,一旦對它們的操作被提交,這些頁面也將被備份至系統的頁面檔案,這與可執行檔案被用來備份執行**頁面的過程是很類似的。圖1展示了**頁面和資料頁面在磁碟儲存器上的備份過程:

圖1 程序的**頁、資料頁在磁碟儲存器上的備份

顯然,如果可以採取同一種方式來處理**和資料頁面,無疑將會提高程式的執行效率,而記憶體對映檔案的使用恰恰可以滿足此需求。

對大檔案的管理

記憶體對映檔案物件在關閉物件之前並沒有必要撤銷記憶體對映檔案的所有檢視。在物件被釋放之前,所有的髒頁面將自動寫入磁碟。通過closehandle()關閉記憶體對映檔案物件,只是釋放該物件,如果記憶體對映檔案代表的是磁碟檔案,那麼還需要呼叫標準檔案i/o函式來將其關閉。在處理大檔案處理時,記憶體對映檔案將表示出卓越的優勢,只需要消耗極少的物理資源,對系統的影響微乎其微。下面先給出記憶體對映檔案的一般程式設計流程框圖:

圖2 使用記憶體對映檔案的一般流程

而在某些特殊行業,經常要面對十幾gb乃至幾十gb容量的巨型檔案,而乙個32位程序所擁有的虛擬位址空間只有232 = 4gb,顯然不能一次將檔案映像全部對映進來。對於這種情況只能依次將大檔案的各個部分對映到程序中的乙個較小的位址空間。這需要對上面的一般流程進行適當的更改:

1)對映檔案開頭的映像。

2)對該映像進行訪問。

3)取消此映像

4)對映乙個從檔案中的乙個更深的位移開始的新映像。

5)重複步驟2,直到訪問完全部的檔案資料。

下面給出一段根據此描述而寫出的對大於4gb的檔案的處理**:

下面分別對這些關鍵函式進行說明:

1)createfile():createfile()函式是乙個用途非常廣泛的函式, 在這裡的用法並沒有什麼特殊的地方,但有幾點需要注意:一是訪問模式引數dwdesiredaccess。該引數設定了對檔案核心物件的訪問型別,其允許設定的許可權可以為讀許可權generic_read、寫許可權generic_write、讀寫許可權generic_read | generic_write和裝置查詢許可權0。在使用對映檔案時,只能開啟那些具有可讀訪問許可權的檔案,即只能應用generic_read和generic_read | generic_write這兩種組合;另一點需要注意的是共享模式引數dwsharemode。該引數定義了對檔案核心物件的共享方式,其可能的設定為file_share_read、file_share_write和0,並可對其組合使用。其中,設定為0時不允許共享物件;file_share_read和file_share_write分別為在要求唯讀、只寫訪問的情況下才允許物件的共享。

由於通過記憶體對映檔案可以在多個程序間共享資料,因此在進行這種應用時應當考慮dwsharemode引數設定對執行結果的影響。

函式第乙個引數hfile為標識要對映到程序的位址空間的檔案的控制代碼。雖然由於記憶體對映檔案的物理儲存器是來自於磁碟上的檔案,而非系統的頁檔案,使建立記憶體對映檔案就像保留乙個位址空間區域並將物理儲存器提交給該區域一樣。第二個引數為指向檔案對映核心物件的security_attributes結構的指標,由此來決定子程序能否繼承得到返回的控制代碼。通常為其傳遞null值,以預設的安全屬性來禁止返回控制代碼的被繼承。

區保護屬性

說明sec_commit

為區中的所有頁面在記憶體中或磁碟頁面檔案中分配物理儲存器

sec_image

告知系統,對映的檔案是乙個可移植的exe檔案映像

sec_nocache

告知系統,未將檔案的任何記憶體對映檔案放入快取記憶體,多供硬體裝置驅動程式開發人員使用

sec_reserve

對乙個區的所有頁面進行保留而不分配物理儲存器

在本節開始也曾提到過,建立檔案對映物件是不需要花費什麼系統資源的,因此遵循"寧多勿缺"的原則,一般應將檔案對映物件的大小設定為檔案大小的相同值。函式最後的引數將可以為對映物件命名。如果想開啟乙個已存在的檔案對映物件,該物件必須要命名。對該名字字串的要求僅限於未被其它物件使用過的名字即可。

3)mapviewoffile():當建立了乙個記憶體對映檔案物件並得到其有效控制代碼後,該控制代碼即可用來在程序的虛擬位址空間中對映檔案的乙個映像。在記憶體對映檔案物件已經存在的情況下,映像可被任意對映或取消對映。在檔案映像被對映時,仍然必須由系統來為檔案的資料保留乙個位址空間區域,並將檔案的資料作為對映到該區域的物理儲存器進行提交。在程序的位址空間中,乙個足夠大的連續位址空間(通常足以覆蓋整個檔案映像)將被指定給此檔案映像。儘管如此,記憶體的物理頁面還是根據在實際使用中的需求而進行分配的。真正分配乙個對應於記憶體對映檔案映像頁面的物理記憶體頁面是在發生該頁的缺頁中斷時進行的,這將在第一次讀寫記憶體頁面中的任一位址時自動完成。mapviewoffile()即負責對映記憶體對映檔案的乙個映像,

4)unmapviewoffile():當不再需要保留對映到程序位址空間區域中的檔案映像資料時,可通過呼叫unmapviewoffile()函式將其釋放。該函式結構非常簡單,只需要提供映像在程序中的起始位址(區域的基位址)作為引數即可。該函式的輸入引數為呼叫mapviewoffile()時所返回的指向檔案映像在程序的位址空間中的起始位址的指標。在呼叫mapviewoffile()後,必須確保在程序退出之前能夠執行unmapviewoffile()函式,否則在程序終止之後先前保留的區域將得不到釋放,即使再次啟動程序重複呼叫mapviewoffile()系統也總是在程序的位址空間中保留乙個新的區域,而此前保留的所有區域將得不到釋放。

一種比較特殊的情況是,對同乙個記憶體對映檔案映**兩個相同的映像的撤消。前面曾經提到過,對於同乙個記憶體對映檔案可以有多個映像,這些映像也可以重疊,因此這種情況的存在是合法的。對於這種情況,雖然從表面看上去在單程序的位址空間內是不可能存在兩個基位址完全相同的映像的,這將導致無法對這它們的區分。但是事實上,由mapviewoffile()所返回得到的基位址只是檔案映像在程序位址空間中的起始基位址,因此在對映同一記憶體對映檔案的兩個相同映像時將會產生對記憶體對映檔案同一部分的兩個不同基位址的相同映像,可以用同樣的方法呼叫unmapviewoffile()將其從程序的位址空間中予以撤消。

5)closehandle(): 與win32的大多數物件一樣,在使用完畢之後總是要通過closehandle()函式將已開啟的核心物件關閉。如果忘記關閉物件,在程式繼續執行時將會出現資源洩漏。雖然在程式退出執行時,作業系統會自動關閉在程序中已經開啟但未關閉的任何物件。但是在程序的執行過程中,勢必會積累過多的資源控制代碼。因此在不再需要使用物件的時候通過closehandle()將其予以關閉是有意義的。

小結本文對記憶體對映檔案在大檔案處理中的應用作了較為詳細的闡述。經實際測試,記憶體對映檔案在處理大資料量檔案時表現出了良好的效能,比通常使用cfile類和readfile()和writefile()等函式的檔案處理方式具有明顯的優勢。本文所述程式**在windows 2000 professional下由microsoft visual c++ 6.0編譯通過。

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

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

使用記憶體檔案對映實現共享記憶體

不使用ipc中的共享記憶體 shm 使用記憶體檔案對映的方式來實現共享記憶體 共享記憶體寫入者 使用檔案記憶體對映進行記憶體共享 include include include include include include include include include include using...

記憶體對映檔案

記憶體對映檔案是利用虛擬記憶體把檔案對映到程序的位址空間中去,在此之後程序操作文 件,就像操作程序空間裡的位址一樣了,比如使用 memcpy 等記憶體操作的函式。這種方法能 夠很好的應用在需要頻繁處理乙個檔案或者是乙個大檔案的場合,這種方式處理 io效率比 普通 io效率要高。另外,unix 把它做...