程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 內存映射修改大文件

內存映射修改大文件

編輯:關於VC++

本文介紹利用內存映射文件修改大文件:在大文件內存前加入一段數據,若要使用內存映射文件,必須執行下列操作步驟:

創建或打開一個文件內核對象,該對象用於標識磁盤上你想用作內存映射文件的文件;

創建一個文件映射內核對象,告訴系統該文件的大小和你打算如何訪問該文件;

讓系統將文件映射對象的全部或一部分映射到你的進程地址空間中;

當完成對內存映射文件的使用時,必須執行下面這些步驟將它清除:

告訴系統從你的進程的地址空間中撤消文件映射內核對象的映像;

關閉文件映射內核對象;

關閉文件內核對象;

下面將用一個實例詳細介紹這些操作步驟,(本實例的目的就是將一個文件A其內容前面加入一些內容存入文件B,我想大家在程序開發當中會遇到這種情況的)。

一、我們打開關於A文件內核對象,並創建一個關於B文件的內核對象

若要創建或打開一個文件內核對象,總是要調用CreateFile函數:

HANDLE CreateFile(
PCSTR pszFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
PSECURITY_ATTRIBUTES psa,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);

CreateFile函數擁有好幾個參數,這裡只重點介紹前3個參數,即pszFileName,dwDesiredAccess和dwShareMode。

你可能會猜到,第一個參數pszFileName用於指明要創建或打開的文件的名字(包括一個選項路徑),第二個參數dwDesiredAccess用於設定如何訪問該文件的內容,可以設定下表所列的4個值中的一個。

值 含義 0 不能讀取或寫入文件的內容,當只想獲得文件的屬性時,請設定0 GENERIC_READ 可以從文件中讀取數據 GENERIC_WRITE 可以將數據寫入文件 GENERIC_READ|GENERIC_WRITE 可以從文件中讀取數據,也可以將數據寫入文件

當創建或打開一個文件,將它作為一個內存映射文件來使用時,請選定最有意義的一個或多個訪問標志,以說明你打算如何訪問文件的數據,對內存映射文件來說,必須打開用於只讀訪問或讀寫訪問的文件,因此,可以分別設定GENERIC_READ或GENERIC_READ|GENERIC_WRITE,

第三個參數dwShareMode告訴系統你想如何共享該文件,可以為dwShareMode設定下表所列的4個值之一:

值 含義 0 打開文件的任何嘗試均將失敗 FILE_SHARE_READ 使用GENERIC_WRITE打開文件的其他嘗試將會失敗 FILE_SHARE_WRITE 使用GENERIC_READ打開文件的其他嘗試將會失敗 FILE_SHARE_READFILE_SHARE_WRITE 打開文件的其他嘗試將會取得成功

 

如果CreateFile函數成功地創建或打開指定的文件,便返回一個文件內核對象的句柄,否則返回INVALID_HANDLE_VALUE,

注意能夠返回句柄的大多數Windows函數如果運行失敗,那麼就會返回NULL,但是,CreateFile函數將返回INVALID_HANDLE_VALUE,它定義為((HANDLE)-1),

HANDLEhFile=CreateFile(".\\first.txt",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
HANDLEhmyfile=CreateFile("E:\\my.txt",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

二、我們要分別創建兩個文件映射內核對象

調用CreateFile函數,就可以將文件映像的物理存儲器的位置告訴操作系統,你傳遞的路徑名用於指明支持文件映像的物理存儲器在磁盤(或網絡或光盤)上的確切位置,這時,必須告訴系統,文件映射對象需要多少物理存儲器,若要進行這項操作,可以調用CreateFileMapping函數:

HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD fdwProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);

第一個參數hFile用於標識你想要映射到進程地址空間中的文件句柄,該句柄由前面調用的CreateFile函數返回,psa參數是指向文件映射內核對象的SECURITY_ATTRIBUTES結構的指針,通常傳遞的值是NULL(它提供默認的安全特性,返回的句柄是不能繼承的)。

本章開頭講過,創建內存映射文件就像保留一個地址空間區域然後將物理存儲器提交給該區域一樣,因為內存映射文件的物理存儲器來自磁盤上的一個文件,而不是來自從系統的頁文件中分配的空間,當創建一個文件映射對象時,系統並不為它保留地址空間區域,也不將文件的存儲器映射到該區域(下一節將介紹如何進行這項操作),但是,當系統將存儲器映射到進程的地址空間中去時,系統必須知道應該將什麼保護屬性賦予物理存儲器的頁面,CreateFileMapping函數的fdwProtect參數使你能夠設定這些保護屬性,大多數情況下,可以設定下表中列出的3個保護屬性之一。

使用fdwProtect參數設定的部分保護屬性

保護屬性 含義 PAGE_READONLY 當文件映射對象被映射時,可以讀取文件的數據,必須已經將GENERIC_READ傳遞給CreateFile函數。 PAGE_READWRITE 當文件映射對象被映射時,可以讀取和寫入文件的數據,必須已經將GENERIC_READ|GENERIC_WRITE傳遞給CreateFile。 PAGE_WRITECOPY 當文件映射對象被映射時,可以讀取和寫入文件的數據,如果寫入數據,會導致頁面的私有拷貝得以創建,必須已經將GENERIC_READ或GENERIC_WRITE傳遞給CreateFile。

在Windows98下,可以將PAGE_WRITECOPY標志傳遞給CreateFileMapping,這將告訴系統從頁文件中提交存儲器,該頁文件存儲器是為數據文件的數據拷貝保留的,只有修改過的頁面才被寫入頁文件,你對該文件的數據所作的任何修改都不會重新填入原始數據文件,其最終結果是,PAGE_WRITECOPY標志的作用在Windows2000和Windows98上是相同的。

除了上面的頁面保護屬性外,還有4個節保護屬性,你可以用OR將它們連接起來放入CreateFileMapping函數的fdwProtect參數中,節只是用於內存映射的另一個術語。

節的第一個保護屬性是SEC_NOCACHE,它告訴系統,沒有將文件的任何內存映射頁面放入高速緩存,因此,當將數據寫入該文件時,系統將更加經常地更新磁盤上的文件數據,這個標志與PAGE_NOCACHE保護屬性標志一樣,是供設備驅動程序開發人員使用的,應用程序通常不使用,

Windows98將忽略SEC_NOCACHE標志。

節的第二個保護屬性是SEC_IMAGE,它告訴系統,你映射的文件是個可移植的可執行(PE)文件映像,當系統將該文件映射到你的進程的地址空間中時,系統要查看文件的內容,以確定將哪些保護屬性賦予文件映像的各個頁面,例如,PE文件的代碼節(.text)通常用PAGE_EXECUTE_READ屬性進行映射,而PE文件的數據節(.data)則通常用PAGE_READWRITE屬性進行映射,如果設定的屬性是SEC_IMAGE,則告訴系統進行文件映像的映射,並設置相應的頁面保護屬性。

Windows98將忽略SEC_IMAGE標志

最後兩個保護屬性是SEC_RESERVE和SEC_COMMIT,它們是兩個互斥屬性,當使用內存映射數據文件時,它們不能使用,這兩個標志將在本章後面介紹,當創建內存映射數據文件時,不應該設定這些標志中的任何一個標志,CreateFileMapping將忽略這些標志。

CreateFileMapping的另外兩個參數是dwMaximumSizeHigh和dwMaximumSizeLow,它們是兩個最重要的參數,CreateFileMapping函數的主要作用是保證文件映射對象能夠得到足夠的物理存儲器,這兩個參數將告訴系統該文件的最大字節數,它需要兩個32位的值,因為Windows支持的文件大小可以用64位的值來表示,dwMaximumSizeHigh參數用於設定較高的32位,而dwMaximumSizeLow參數則用於設定較低的32位值,對於4GB或小於4GB的文件來說,dwMaximumSizeHigh的值將始終是0。

使用64位的值,意味著Windows能夠處理最大為16EB(1018字節)的文件,如果想要創建一個文件映射對象,使它能夠反映文件當前的大小,那麼可以為上面兩個參數傳遞0,如果只打算讀取該文件或者訪問文件而不改變它的大小,那麼為這兩個參數傳遞0,如果打算將數據附加給該文件,可以選擇最大的文件大小,以便為你留出一些富裕的空間,如果當前磁盤上的文件包含0字節,那麼可以給CreateFileMapping函數的dwMaximumSizeHigh和dwMaximumSizeLow傳遞兩個0,這樣做就可以告訴系統,你要的文件映射對象裡面的存儲器為0字節,這是個錯誤,CreateFileMapping將返回NULL。

如果你對我們講述的內容一直非常關注,你一定認為這裡存在嚴重的問題,Windows支持最大為16EB的文件和文件映射對象,這當然很好,但是,怎樣將這樣大的文件映射到32位進程的地址空間(32位地址空間是4GB文件的上限)中去呢,下一節介紹解決這個問題的辦法,當然,64位進程擁有16EB的地址空間,因此可以進行更大的文件的映射操作,但是,如果文件是個超大規模的文件,仍然會遇到類似的問題。

若要真正理解CreateFile和CreateFileMapping兩個函數是如何運行的,建議你做一個下面的實驗,建立下面的代碼,對它進行編譯,然後在一個調試程序中運行它,當你一步步執行每個語句時,你會跳到一個命令解釋程序,並執行C:\目錄上的“dir”命令,當執行調試程序中的每個語句時,請注意目錄中出現的變化。

int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE,
  PTSTR pszCmdLine, int nCmdShow)
{
  //Before executing the line below, C:\ does not have
  //a file called "MMFTest.Dat."
  HANDLE hfile = CreateFile("C:\\MMFTest.dat",
   GENERIC_READ | GENERIC_WRITE,
   FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
   FILE_ATTRIBUTE_NORMAL, NULL);
  //Before executing the line below, the MMFTest.Dat
  //file does exist but has a file size of 0 bytes.
  HANDLE hfilemap = CreateFileMapping(hfile, NULL, PAGE_READWRITE,
   0, 100, NULL);
  //After executing the line above, the MMFTest.Dat
  //file has a size of 100 bytes.
  //Cleanup
  CloseHandle(hfilemap);
  CloseHandle(hfile);
  //When the process terminates, MMFTest.Dat remains
  //on the disk with a size of 100 bytes.
  return(0);
}

如果調用CreateFileMapping函數,傳遞PAGE_READWRITE標志,那麼系統將設法確保磁盤上的相關數據文件的大小至少與dwMaximumSizeHigh和dwMaximumSizeLow參數中設定的大小相同,如果該文件小於設定的大小,CreateFileMapping函數將擴展該文件的大小,使磁盤上的文件變大,這種擴展是必要的,這樣,當以後將該文件作為內存映射文件使用時,物理存儲器就已經存在了,如果正在用PAGE_READONLY或PAGE_WRITECOPY標志創建該文件映射對象,那麼CreateFileMapping特定的文件大小不得大於磁盤文件的物理大小,這是因為你無法將任何數據附加給該文件。

CreateFileMapping函數的最後一個參數是pszName,它是個以0結尾的字符串,用於給該文件映射對象賦予一個名字,該名字用於與其他進程共享文件映射對象(本章後面展示了它的一個例子,第3章詳細介紹了內核對象的共享操作),內存映射數據文件通常並不需要被共享,因此這個參數通常是NULL。

系統創建文件映射對象,並將用於標識該對象的句柄返回該調用線程,如果系統無法創建文件映射對象,便返回一個NULL句柄值,記住,當CreateFile運行失敗時,它將返回INVALID_HANDLE_VALUE(定義為-1),當CreateFileMapping運行失敗時,它返回NULL,請不要混淆這些錯誤值。

在本實例中創建文件映射內核對象代碼如下:

HANDLE hFileMapping = CreateFileMapping(hFile, NULL,
   PAGE_READONLY, 0, 0, NULL);
  DWORD dwFileSizeHigh;
  __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
  qwFileSize += (((__int64) dwFileSizeHigh) << 32);
  char AddMsg[]="Girl,you love me?, I love you very much!"; //加入的文件內容
  __int64 myFilesize=qwFileSize+sinf.dwAllocationGranularity; //合並後的文件大小
 HANDLE hmyfilemap = CreateFileMapping(hmyfile, NULL, PAGE_READWRITE,  //合並文件大小的內存映射對象
   (DWORD)(myFilesize>>32), (DWORD)(myFilesize& 0xFFFFFFFF), NULL);

三、將文件數據映射到地址空間

當創建了一個文件映射對象後,仍然必須讓系統為文件的數據保留一個地址空間區域,並將文件的數據作為映射到該區域的物理存儲器進行提交,可以通過調用MapViewOfFile函數來進行這項操作:

PVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);

參數hFileMappingObject用於標識文件映射對象的句柄,該句柄是前面調用CreateFileMapping或OpenFileMapping(本章後面介紹)函數返回的,參數dwDesiredAccess用於標識如何訪問該數據,不錯,必須再次設定如何訪問文件的數據,可以設定下表所列的4個值中的一個。

表17-6值及其含義

值 含義 FILE_MAP_WRITE 可以讀取和寫入文件數據,CreateFileMapping函數必須通過傳遞PAGE_READWRITE標志來調用 FILE_MAP_READ 可以讀取文件數據,CreateFileMapping函數可以通過傳遞下列任何一個保護屬性來調用:PAGE_READONLY,PAGE_READWRITE或PAGE_WRITECOPY。 FILE_MAP_ALL_ACCESS 與FILE_MAP_WRITE相同 FILE_MAP_COPY 可以讀取和寫入文件數據,如果寫入文件數據,可以創建一個頁面的私有拷貝,在Windows2000中,CreateFileMapping函數可以用PAGE_READONLY,PAGE_READWRITE或PAGE_WRITECOPY等保護屬性中的任何一個來調用,在Windows98中,CreateFileMapping必須用PAGE_WRITECOPY來調用。

Windows要求所有這些保護屬性一次又一次地重復設置,這當然有些奇怪和煩人,我認為這樣做可以使應用程序更多地對數據保護屬性進行控制,

剩下的3個參數與保留地址空間區域及將物理存儲器映射到該區域有關,當你將一個文件映射到你的進程的地址空間中時,你不必一次性地映射整個文件,相反,可以只將文件的一小部分映射到地址空間,被映射到進程的地址空間的這部分文件稱為一個視圖,這可以說明MapViewOfFile是如何而得名的,

當將一個文件視圖映射到進程的地址空間中時,必須規定兩件事情,首先,必須告訴系統,數據文件中的哪個字節應該作為視圖中的第一個字節來映射,你可以使用dwFileOffsetHigh和dwFileOffsetLow參數來進行這項操作,由於Windows支持的文件最大可達16EB,因此必須用一個64位的值來設定這個字節的位移值,這個64位值中,較高的32位傳遞給參數dwFileOffsetHigh,較低的32位傳遞給參數dwFileOffsetLow,注意,文件中的這個位移值必須是系統的分配粒度的倍數(迄今為止,Windows的所有實現代碼的分配粒度均為64KB),第14章介紹了如何獲取某個系統的分配粒度。

第二,必須告訴系統,數據文件有多少字節要映射到地址空間,這與設定要保留多大的地址空間區域的情況是相同的,可以使用dwNumberOfBytesToMap參數來設定這個值,如果設定的值是0,那麼系統將設法把從文件中的指定位移開始到整個文件的結尾的視圖映射到地址空間。

在Windows98中,如果MapViewOfFile無法找到足夠大的區域來存放整個文件映射對象,那麼無論需要的視圖是多大,MapViewOfFile均將返回NULL。

在Windows2000中,MapViewOfFile只需要為必要的視圖找到足夠大的一個區域,而不管整個文件映射對象是多大。

如果在調用MapViewOfFile函數時設定了FILE_MAP_COPY標志,系統就會從系統的頁文件中提交物理存儲器,提交的地址空間數量由dwNumberOfBytesToMap參數決定,只要你不進行其他操作,只是從文件的映像視圖中讀取數據,那麼系統將決不會使用頁文件中的這些提交的頁面,但是,如果進程中的任何線程將數據寫入文件的映像視圖中的任何內存地址,那麼系統將從頁文件中抓取已提交頁面中的一個頁面,將原始數據頁面拷貝到該頁交換文件中,然後將該拷貝的頁面映射到你的進程的地址空間,從這時起,你的進程中的線程就要訪問數據的本地拷貝,不能讀取或修改原始數據。

當系統制作原始頁面的拷貝時,系統將把頁面的保護屬性從PAGE_WRITECOPY改為PAGE_READWRITE,下面這個代碼段就說明了這個情況:

// Open the file that we want to map.
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
  OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Create a file-mapping object for the file.
HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY,
  0, 0, NULL);
// Map a copy-on-write view of the file; the system will commit
// enough physical storage from the paging file to accommodate
// the entire file. All pages in the view will initially have
// PAGE_WRITECOPY access.
PBYTE pbFile = (PBYTE) MapViewOfFile(hFileMapping, FILE_MAP_COPY,
  0, 0, 0);
// Read a byte from the mapped view.
BYTE bSomeByte = pbFile[0];
// When reading, the system does not touch the committed pages in
// the paging file. The page keeps its PAGE_WRITECOPY attribute.
// Write a byte to the mapped view.
pbFile[0] = 0;
// When writing for the first time, the system grabs a committed
// page from the paging file, copies the original contents of the
// page at the accessed memory address, and maps the new page
// (the copy) into the process's address space. The new page has
// an attribute of PAGE_READWRITE.
// Write another byte to the mapped view.
pbFile[1] = 0;
// Because this byte is now in a PAGE_READWRITE page, the system
// simply writes the byte to the page (backed by the paging file).
// When finished using the file's mapped view, unmap it.
// UnmapViewOfFile is discussed in the next section.
UnmapViewOfFile(pbFile);
// The system decommits the physical storage from the paging file.
// Any writes to the pages are lost.
// Clean up after ourselves.
CloseHandle(hFileMapping);
CloseHandle(hFile);
Windows98前面講過,Windows98必須預先為內存映射文件提交頁文件中的存儲器,然而,它只有在必要時才將修改後的頁面寫入頁文件,

四、從進程的地址空間撤消文件數據的映射

當不再需要保留映射到你的進程地址空間區域中的文件數據時,可以通過調用下面的函數將它釋放:

BOOLUnmapViewOfFile(PVOIDpvBaseAddress);

該函數的唯一的參數pvBaseAddress用於設定返回區域的基地址,該值必須與調用MapViewOfFile函數返回的值相同,必須記住要調用UnmapViewOfFile函數,如果沒有調用這個函數,那麼在你的進程終止運行前,保留的區域就不會被釋放,每當你調用MapViewOfFile時,系統總是在你的進程地址空間中保留一個新區域,而以前保留的所有區域將不被釋放。

為了提高速度,系統將文件的數據頁面進行高速緩存,並且在對文件的映射視圖進行操作時不立即更新文件的磁盤映像,如果需要確保你的更新被寫入磁盤,可以強制系統將修改過的數據的一部分或全部重新寫入磁盤映像中,方法是調用FlushViewOfFile函數:

BOOLFlushViewOfFile(
PVOIDpvAddress,
SIZE_TdwNumberOfBytesToFlush);

第一個參數是包含在內存映射文件中的視圖的一個字節的地址,該函數將你在這裡傳遞的地址圓整為一個頁面邊界值,第二個參數用於指明你想要刷新的字節數,系統將把這個數字向上圓整,使得字節總數是頁面的整數,如果你調用FlushViewOfFile函數並且不修改任何數據,那麼該函數只是返回,而不將任何信息寫入磁盤。

對於存儲器是在網絡上的內存映射文件來說,FlushViewOfFile能夠保證文件的數據已經從工作站寫入存儲器,但是FlushViewOfFile不能保證正在共享文件的服務器已經將數據寫入遠程磁盤,因為服務器也許對文件的數據進行了高速緩存,若要保證服務器寫入文件的數據,每當你為文件創建一個文件映射對象並且映射該文件映射對象的視圖時,應該將FILE_FLAG_WRITE_THROUGH標志傳遞給CreateFile函數,如果你使用該標志打開該文件,那麼只有當文件的全部數據已經存放在服務器的磁盤驅動器中的時候,FlushViewOfFile函數才返回。

記住UnmapViewOfFile函數的一個特殊的特性,如果原先使用FILE_MAP_COPY標志來映射視圖,那麼你對文件的數據所作的任何修改,實際上是對存放在系統的頁文件中的文件數據的拷貝所作的修改,在這種情況下,如果調用UnmapViewOfFile函數,該函數在磁盤文件上就沒有什麼可以更新,而只會釋放頁文件中的頁面,從而導致數據丟失。

如果想保留修改後的數據,必須采用別的措施,例如,你可以用同一個文件創建另一個文件映射對象(使用PAGE_READWRITE),然後使用FILE_MAP_WRITE標志將這個新文件映射對象映射到進程的地址空間,之後,你可以掃描第一個視圖,尋找帶有PAGE_READWRITE保護屬性的頁面,每當你找到一個帶有該屬性的頁面時,可以查看它的內容,並且確定是否將修改了的數據寫入該文件,如果不想用新數據更新該文件,那麼繼續對視圖中的剩余頁面進行掃描,直到視圖的結尾,但是,如果你確實想要保存修改了的數據頁面,那麼只需要調用MoveMemory函數,將數據頁面從第一個視圖拷貝到第二個視圖,由於第二個視圖是用PAGE_READWRITE保護屬性映射的,因此MoveMemory函數將更新磁盤上的實際文件內容,可以使用這種方法來確定文件的變更並保存你的文件的數據。

Windows98不支持copy-on-write(寫入時拷貝)保護屬性,因此,當掃描內存映射文件的第一個視圖時,無法測試用PAGE_READWRITE標志做上標記的頁面,你必須設計一種方法來確定第一個視圖中的哪些頁面已經做了修改。

五、關閉文件映射對象和文件對象

不用說,你總是要關閉你打開了的內核對象,如果忘記關閉,在你的進程繼續運行時會出現資源洩漏的問題,當然,當你的進程終止運行時,系統會自動關閉你的進程已經打開但是忘記關閉的任何對象,但是如果你的進程暫時沒有終止運行,你將會積累許多資源句柄,因此你始終都應該編寫清楚而又“正確的”代碼,以便關閉你已經打開的任何對象,若要關閉文件映射對象和文件對象,只需要兩次調用CloseHandle函數,每個句柄調用一次:

讓我們更加仔細地觀察一下這個進程,下面的偽代碼顯示了一個內存映射文件的例子:

HANDLEhFile=CreateFile(...);
HANDLEhFileMapping=CreateFileMapping(hFile,...);
PVOIDpvFile=MapViewOfFile(hFileMapping,...);
//Usethememory-mappedfile.
UnmapViewOfFile(pvFile);
CloseHandle(hFileMapping);
CloseHandle(hFile);

上面的代碼顯示了對內存映射文件進行操作所用的“預期”方法,但是,它沒有顯示,當你調用MapViewOfFile時系統對文件對象和文件映射對象的使用計數的遞增情況,這個副作用是很大的,因為它意味著我們可以將上面的代碼段重新編寫成下面的樣子:

HANDLEhFile=CreateFile(...);
HANDLEhFileMapping=CreateFileMapping(hFile,...);
CloseHandle(hFile);
PVOIDpvFile=MapViewOfFile(hFileMapping,...);
CloseHandle(hFileMapping);
//Usethememory-mappedfile.
UnmapViewOfFile(pvFile);

當對內存映射文件進行操作時,通常要打開文件,創建文件映射對象,然後使用文件映射對象將文件的數據視圖映射到進程的地址空間,由於系統遞增了文件對象和文件映射對象的內部使用計數,因此可以在你的代碼開始運行時關閉這些對象,以消除資源洩漏的可能性,

如果用同一個文件來創建更多的文件映射對象,或者映射同一個文件映射對象的多個視圖,那麼就不能較早地調用CloseHandle函數——以後你可能還需要使用它們的句柄,以便分別對CreateFileMapping和MapViewOfFile函數進行更多的調用,

本實例中第三到第六步代碼如下:

CloseHandle(hFile); //Wenolongerneedaccesstothefileobject'shandle.
CloseHandle(hmyfile);
PBYTEpbmyFile=(PBYTE)MapViewOfFile(hmyfilemap,FILE_MAP_WRITE,//內存映射視圖
0,//Startingbyte
0,//infile
sizeof(AddMsg));
memcpy(pbmyFile,AddMsg,sizeof(AddMsg)); //加入內容
UnmapViewOfFile(pbmyFile);
__int64qwFileOffset=0; //A文件視圖的偏移量
__int64qwmyFileOffset=sinf.dwAllocationGranularity; //合並文件視圖的遍移量
while(qwFileSize>0)
{
//Determinethenumberofbytestobemappedinthisview
DWORDdwBytesInBlock=sinf.dwAllocationGranularity;
if(qwFileSize<sinf.dwAllocationGranularity)//文件小於系統分配粒度
dwBytesInBlock=(DWORD)qwFileSize; //偏移量為文件大小
PBYTEpbFile=(PBYTE)MapViewOfFile(hFileMapping,FILE_MAP_READ,
(DWORD)(qwFileOffset>>32),//Startingbyte
(DWORD)(qwFileOffset&0xFFFFFFFF),//infile
dwBytesInBlock); //#ofbytestomap
PBYTEpbmyFile=(PBYTE)MapViewOfFile(hmyfilemap,FILE_MAP_WRITE,
(DWORD)(qwmyFileOffset>>32),//Startingbyte
(DWORD)(qwmyFileOffset&0xFFFFFFFF),//infile
dwBytesInBlock);
memcpy(pbmyFile,pbFile,dwBytesInBlock);
//Unmaptheview;wedon'twantmultipleviews
//inouraddressspace.
UnmapViewOfFile(pbFile);
UnmapViewOfFile(pbmyFile);
//Skiptothenextsetofbytesinthefile.
qwmyFileOffset+=dwBytesInBlock;
qwFileOffset+=dwBytesInBlock;
qwFileSize-=dwBytesInBlock;
}
CloseHandle(hFileMapping);
CloseHandle(hmyfilemap);

參考資料:《Windows核心編程》

Richter是一位讓人佩服的前輩,本文99%是引用前輩原文,原創部份甚少,請各位莫見笑(本人純屬初學者),者作深厚的技術功底與精湛的語言,讓我對內存映射文件操作有了清晰的認識,若大家有什麼不清楚之處,可以聯系我,小弟願與你們探討,

QQ:357545146

Email:[email protected]

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved