VC中用記憶體對映檔案處理大檔案

2021-06-05 19:52:28 字數 3695 閱讀 5897

檔案操作是應用程式最為基本的功能之一,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的檔案的處理**:

檢視的偏移

dwblockbytes);//檢視的大小

if (lpbmapaddress == null)

// 對對映的檢視進行訪問

for(dword i = 0; i < dwblockbytes; i++)

byte temp = *(lpbmapaddress + i);//改為指標訪問

// 撤消檔案映像

unmapviewoffile(lpbmapaddress);

//修正引數

qwfileoffset += dwblockbytes;

qwfilesize -= dwblockbytes;

}// 關閉檔案對映物件控制代碼

closehandle(hfilemap);

afxmessagebox("成功完成對檔案的訪問");}

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

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

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

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

VC中用記憶體對映檔案處理大檔案

引言 檔案操作是應用程式最為基本的功能之一,win32 api和mfc均提供有支援檔案處理的函式和類,常用的有win32 api的createfile writefile readfile 和mfc提供的cfile類等。一般來說,以上這些函式可以滿足大多數 場合的要求,但是對於某些特殊應用領域所需要...

VC中用記憶體對映檔案處理大檔案

檔案操作是應用程式最為基本的功能之一,win32 api和mfc均提供有支援檔案處理的函式和類,常用的有win32 api的createfile writefile readfile 和mfc提供的cfile類等。一般來說,以上這些函式可以滿足大多數場合的要求,但是對於某些特殊應用領域所需要的動輒幾...

VC中用記憶體對映檔案處理大檔案

引言 檔案操作是應用程式最為基本的功能之一,win32 api和mfc均提供有支援檔案處理的函式和類,常用的有win32 api的createfile writefile readfile 和mfc提供的cfile類等。一般來說,以上這些函式可以滿足大多數場合的要求,但是對於某些特殊應用領域所需要的...