WINDOWS為任何一個進程都賦予了4G的獨立的虛擬位址空間,然而學過OS的都曉得這個只是邏輯上的虛擬地址空間,進程實際擁有的遠小於4GB的物理地址空間。並且進程的虛擬位址空間是每個進程私人的,有作業系統分配,本質上不過是實體記憶體地址的映像罷了。因此可以得出結論:在進程內啟動並執行線程只能訪問其所處進程的記憶體空間,即不同進程中可以用相同地址的指標用來指向屬於各自進程中的內容,互不干擾,因為彼此都是以進程中的虛地址去訪問記憶體的,作業系統再將虛擬位址轉換為真正的實體記憶體地址。這樣減少了程式員很多的麻煩事,使得我們而已在一個足夠大的空間內自由隨便的去做我們自己的功能,不必考慮會與其他進程發生衝突等。正是由於OS有如此完美的功能,我們才得以在我們自己的代碼中不用顧忌太多,想申請空間就申請,不用關心這個空間是否被其他進程所佔用?是否實體記憶體空間不夠用(自古實體記憶體都是兵家必爭之地嘛)等等一些讓人頭痛的問題。
其實上述所說的就是作業系統的記憶體管理員模式,也是OS的一個核心功能吧。上面簡單的幾句只是概括,想弄的清清楚楚,那就得去看看作業系統原理方面的書嘍,個人看的是電腦的心智---作業系統之哲學原理(簡單評論一下此書:每篇都有一個簡單的哲學引子,內容還算湊合吧)。
記憶體對應檔的使用
檔案操作是一個準系統,當然WINDOWS提供了很多具備這些功能的API,CreateFile(),WriteFile()等一大坨,對於小型體積的檔案來說沒什麼問題,對於幾十M,幾百M甚至幾G又或者更多的檔案,這些API就顯得力不從心了,好比那個東西。。。大家懂的。因此微軟給我們提供了記憶體對應檔的技術。至於優點嗎,肯定是速度快了,最後寫個例子來證明,當然其他優點也很多了,可以去看看核心編程那本書。
使用流程
1
HANDLE WINAPI CreateFile(
__in LPCTSTR lpFileName,
__in DWORD dwDesiredAccess,
__in DWORD dwShareMode,
__in LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in DWORD dwCreationDisposition,
__in DWORD dwFlagsAndAttributes,
__in HANDLE hTemplateFile
);
參數說明:大家都懂,MSDN。
2
HANDLE WINAPI CreateFileMapping( __in HANDLE hFile, __in LPSECURITY_ATTRIBUTES lpAttributes, __in DWORD flProtect, __in DWORD dwMaximumSizeHigh, __in DWORD dwMaximumSizeLow, __in LPCTSTR lpName);
參數還是看MSDN吧
這個函數的作用其實是建立一個檔案對應核心對象,參數中指定了要佔用多少空間,至於這個大家可以這麼理解,這個函數的作用其實就是在進程的虛擬位址空間中分配一個指定大小的空間,這個空間的作用下面會說明。返回是一個控制代碼。
3
LPVOID WINAPI MapViewOfFile(
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
參數:MSDN
函數作用:將檔案中的資料(可以理解成CreateFile開啟或者建立的那個檔案中的資料)映射到進程地址空間中,疑問:映射多少個空間呢?這個問題在第二部已經解決了,也就是上面說的那個空間,其實就是為這步打基礎的。這個函數執行之後,我們可以想象一下是什麼情況:
進程的地址空間中現在已經有了一個檔案的映射內容,大家都叫這個VIEW視圖,這個視圖跟在磁碟上的檔案建立了映射關係,所以我們就不需要再開啟磁碟檔案,讀取檔案。。。。等一系列麻煩的操作了,直接操作這個視圖完全OK了,因此此時的情況已經相當於把檔案放到了進程地址空間中,如何操作呢,看看此函數的傳回值就曉得了,返回的正好就是這個空間中的一個指標。是不是很巧妙。。。。當然了此時對這個視圖操作會非常方便,非常簡單,速度非常快。。。。。
關鍵的步驟就是上面三步,每步之間都有關係的,整體理解起來也不是很費勁吧。當然需要一點基礎就是進程的虛擬位址空間的理解,這個是一個抽象,其實說真的也不太好說,意會吧。
4
BOOL WINAPI UnmapViewOfFile(
__in LPCVOID lpBaseAddress
);
解除映射關係
5
CloseHandle()
關閉控制代碼
6
CloseHandle()
同樣關閉控制代碼
因為返回了2次handle。
關於效率問題,下面這個例子可以反映的很透徹的
用記憶體檔案對應時間開啟是47ms左右(我就運行了2次,系統是XP SP3 環境 vc 6)
普通的檔案操作大概在6593ms左右(也是測試了2次,同上)
效率差別之明顯,不亞於國內的貧富差距吧。呵呵,不談政治問題。
test code:
#include<windows.h><br />#include<tchar.h><br />#include<stdio.h><br />#include<iostream><br />using namespace std;<br />int main()<br />{<br />const long lCount=256000;<br />const long lTimes=3000;<br />//first test<br />/*const long lStartTime=GetTickCount();<br />for(int i=0;i<lTimes;i++)<br />{<br />HANDLE hFileMap=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,lCount*4,NULL);<br />PVOID lpFileView=MapViewOfFile(hFileMap,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);<br />UnmapViewOfFile(lpFileView);<br />CloseHandle(hFileMap);</p><p>}<br />const longlEndTime=GetTickCount();<br />cout<<"/nTimeis:"<<lEndTime-lStartTime;*/<br />//second test<br />const lStartTime=GetTickCount();<br />for(int i=0;i<lTimes;i++)<br />{<br />int *lpArray=new int [lCount];<br />delete lpArray;<br />}<br />const long lEndTime=GetTickCount();<br />cout<<"/nTime is:"<<lEndTime-lStartTime<<endl;</p><p>return 0;<br />}