在做科研,實現一些大資料的演算法的時候,經常要呼叫一些檔案的i/o函式,在資料量很大的時候,除了設計的演算法和資料結構的耗時以外,其實主要的耗時還是檔案的i/o。因為一般常規的方法就是先讀出磁碟檔案的內容到記憶體中,然後修改,最後寫回到磁碟上。讀磁碟檔案是要經過一次系統呼叫,先將檔案的內容從磁碟拷貝到核心空間的乙個緩衝區,然後再將這些資料拷貝到使用者空間,實際上是兩次資料拷貝。寫回同樣也需要經過兩次資料拷貝。所以整個過程基本上會有至少四次資料的拷貝,檔案稍微大一點,i/o的開銷還是很大的。因此有必要採取一定的措施來減少這方面的耗時。記憶體對映檔案是作業系統的提供的一種機制,作業系統將乙個資料檔案的位址對映到程序的位址空間中,即記憶體對映資料檔案,在對大量的資料進行操作時,這樣做會非常方便。
使用記憶體對映檔案一方面可以用來載入和執行. exe和dll檔案,大大節省頁檔案空間和應用程式啟動執行所需的時間,另一方面,可以用來訪問磁碟上的資料檔案,不必對檔案執行i/o操作,並且可以不必對檔案內容進行快取。此外,使用記憶體對映檔案,可以使同一臺機器上執行的多個執行緒之間共享資料。windows也提供了其他在程序之間通訊的一些方法,以便在程序之間進行資料通訊,但這些方法都是使用記憶體對映檔案來實現的,這使得記憶體對映檔案成為單台機器上的多個程序之間進行通訊的最有效的方法。
使用記憶體對映檔案的步驟如下:
(1) 建立或開啟乙個檔案核心物件,用這個物件來標識磁碟上需要用作記憶體對映檔案的檔案。
(2) 建立乙個檔案對映核心物件,告訴系統該檔案的大小以及這個檔案的訪問方式。
(3) 讓系統將檔案對映物件的全部或一部分對映到程序的位址空間中。
當完成對記憶體對映檔案的使用時,必須執行下面這些步驟將它清除:
(1) 告訴系統從程序的位址空間中撤消檔案對映核心物件的映像。
(2) 關閉檔案對映核心物件。
(3) 關閉檔案核心物件。
步驟1:建立或開啟檔案核心物件,可以呼叫createfile函式:
[cpp]view plain
copy
print?
handle
createfile(
lpctstr
lpfilename,
// 指向檔名的指標
dword
dwdesiredaccess,
// 訪問模式(寫 / 讀)
dword
dwsharemode,
// 共享模式
lpsecurity_attributes lpsecurityattributes, // 指向安全屬性的指標
dword
dwcreationdisposition,
// 如何建立
dword
dwflagsandattributes,
// 檔案屬性
handle
htemplatefile
// 用於複製檔案控制代碼
);
呼叫createfile函式,就可以告訴作業系統檔案映像的物理儲存器的位置,傳遞的路徑名用於指明支援檔案映像的物理儲存器在磁碟(或網路或光碟)上的確切位置。這時,還必須告
訴作業系統,檔案對映物件需要多少物理儲存器,需要呼叫下面的函式來完成這個操作。
[cpp]view plain
copy
print?
handle
handle
hfile,
//物理檔案控制代碼
lpsecurity_attributes lpattributes, //安全設定
dword
flprotect,
//保護設定
dword
dwmaximumsizehigh,
//高位檔案大小
dword
dwmaximumsizelow,
//低位檔案大小
lpctstr
pszname
//共享記憶體名稱
);
第乙個引數hfile用來標識需要對映到程序位址空間中的檔案控制代碼,這個控制代碼由前面呼叫的createfile函式返回。
在建立乙個檔案對映物件之後,系統依然需要為檔案的資料保留乙個位址空間區域,並將檔案的資料作為對映到這個區域的物理儲存器進行提交,呼叫下面的函式可以完成這個操作。
[cpp]view plain
copy
print?
lpvoid
mapviewoffile(
handle
// 已建立的檔案對映物件控制代碼
dword
dwdesiredaccess,
// 訪問模式
dword
dwfileoffsethigh,
// 檔案偏移的高32位
dword
dwfileoffsetlow,
// 檔案偏移的低32位
dword
dwnumberofbytestomap
// 對映檢視的大小
);
將乙個檔案對映到程序的位址空間中時,不必一次性地對映整個檔案,可以只將檔案的一小部分對映到位址空間,被對映到程序的位址空間的這部分檔案稱為乙個檢視。當將乙個檔案檢視對映到程序的位址空間中時,必須做兩件事情。首先,必須告訴系統資料檔案中的哪個位元組應該作為檢視中的第乙個位元組來對映,可以使用dwfileoffsethigh和dwfileoffsetlow這兩個引數來完成。其次,必須告訴系統,資料檔案有多少位元組要對映到位址空間,可以使用dwnumberofbytestomap這個引數進行設定。
bool unmapviewoffile(lpcvoid lpbaseaddress);
lpbaseaddress指定了返回區域的基位址,必須將這個值設定與mapviewoffile()的返回值相同。如果沒有呼叫這個函式,那麼在程序終止執行前,保留的區域就不會被釋放。每當呼叫mapviewoffile函式時,系統就會在程序位址空間中保留乙個新區域,而以前保留的所有區域將不被釋放。
具體的程式設計中,大致的呼叫過程如下:
[cpp]view plain
copy
print?
handle
hfile=createfile(...);
handle
closehandle(hfile);
pvoid
//使用記憶體對映檔案
...
...
...
unmapviewoffile(pvfile);
如果用同乙個檔案來建立更多的檔案對映物件,或者對映同乙個檔案對映物件的多個檢視,
使用記憶體對映處理大檔案,比如說把16tb的檔案對映到乙個較小的記憶體對映空間,直接全部對映是無法實現的,必須對映乙個只包含一小部分檔案資料的檔案檢視。可以這樣考慮,首先對映乙個檔案開始的檢視,在完成對檔案的第乙個檢視的訪問後,可以取消對這一部分的對映,然後對映檔案中更後面的位置開始的檢視。重複進行這個操作,直到訪問完整個檔案為止。
此外,記憶體對映檔案具有一致性。系統允許將乙個檔案的相同資料對映到多個檢視,比如說將乙個檔案開頭的10kb對映到乙個檢視,然後將這個檔案開頭的4kb對映到另乙個程序。只要對映相同的檔案對映物件,系統就會確保對映的檢視資料的一致性。比如說,如果應用程式改變了乙個檢視中的檔案內容,其他檢視中的資料也會相應地改變。
參考了書籍《windows核心程式設計》,例子是書中例子的變體。
在乙個8gb的二進位制檔案和32位的位址空間中,計算該二進位制檔案中所有0位元組的數目。
[cpp]view plain
copy
print?
#include
#include
#include
using
namespace
std;
__int64
count()
handle
hfile,
null,
page_readonly,
0,
0,
null);
if
dword
dwfilesizehigh;
__int64
qwfilesize=getfilesize(hfile,&dwfilesizehigh);
qwfilesize+=(((__int64
)dwfilesizehigh)<<32);
closehandle(hfile);
__int64
qwfileoffset=0,qwnumof0s=0;
while
(qwfilesize>0)
} unmapviewoffile(pbfile);
qwfileoffset+=dwbytesinblock;
qwfilesize-=dwbytesinblock;
} return
qwnumof0s;
} int
main()
記憶體對映檔案
記憶體對映檔案是利用虛擬記憶體把檔案對映到程序的位址空間中去,在此之後程序操作文 件,就像操作程序空間裡的位址一樣了,比如使用 memcpy 等記憶體操作的函式。這種方法能 夠很好的應用在需要頻繁處理乙個檔案或者是乙個大檔案的場合,這種方式處理 io效率比 普通 io效率要高。另外,unix 把它做...
記憶體對映檔案
記憶體對映檔案有三種,第一種是可執行檔案的對映,第二種是資料檔案的對映,第三種是借助頁面交換檔案的記憶體對映.應用程式本身可以使用後兩種記憶體對映.1.可執行檔案對映 windows在執行乙個win32應用程式時使用的是記憶體對映檔案技術.系統先在程序位址空間的0x00400000以上保留乙個足夠大...
記憶體檔案對映
一直都對記憶體對映檔案這個概念很模糊,不知道它和虛擬記憶體有什麼區別,而且對映這個詞也很讓人迷茫,今天終於搞清楚了。下面,我先解釋一下我對對映這個詞的理解,再區分一下幾個容易混淆的概念,之後,什麼是記憶體對映就很明朗了。首先,對映 這個詞,就和數學課上說的 一一對映 是乙個意思,就是建立一種一一對應...