先不說記憶體對映檔案(即一塊記憶體和一個檔案相對映對應)是什麼。貼個程式碼先,。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#include <iostream> #include <fcntl.h> #include <io.h> #include <afxwin.h> using namespace std; int main() { //開始 //獲得檔案控制程式碼 HANDLE hFile=CreateFile( "c:\\test.dat", //檔名 GENERIC_READ|GENERIC_WRITE, //對檔案進行讀寫操作 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, //開啟已存在檔案 FILE_ATTRIBUTE_NORMAL, 0); //返回值size_high,size_low分別表示檔案大小的高32位/低32位 DWORD size_low,size_high; size_low= GetFileSize(hFile,&size_high); //建立檔案的記憶體對映檔案。 HANDLE hMapFile=CreateFileMapping( hFile, NULL, PAGE_READWRITE, //對對映檔案進行讀寫 size_high, size_low, //這兩個引數共64位,所以支援的最大檔案長度為16EB NULL); if(hMapFile==INVALID_HANDLE_VALUE) { AfxMessageBox("Can't create file mapping.Error%d:\n", GetLastError()); CloseHandle(hFile); return 0; } //把檔案資料對映到程式的地址空間 void* pvFile=MapViewOfFile( hMapFile, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, 0); unsigned char *p=(unsigned char*)pvFile; //至此,就獲得了外部檔案test.dat在記憶體地址空間的對映, //下面就可以用指標p"折磨"這個檔案了 CString s; p[size_low-1]='!'; p[size_low-2]='X'; //修改該檔案的最後兩個位元組(檔案大小<4GB高32位為0) s.Format("%s",p); //讀檔案的最後3個位元組 AfxMessageBox(s); //結束 //UnmapViewOfFile(pvFile); //撤銷對映 //CloseHandle(hFile); //關閉檔案 return 0; } |
忘小了說,只要你把這幾個API函式搞定了,一般的記憶體對映問題就可以解決了。。但是記憶體對映檔案到底是幹嘛的呢?讓我們先來思考一個
如果您想讀的內容大於系統分配的記憶體塊怎麼辦?如果您想搜尋的字串剛好超過記憶體塊的邊界又該如何處理?對於第一個問題,您也許會說,只要不斷地讀就不解決了嗎。至於第二個問題,您又會說在記憶體塊的邊界處做一些特別的處理,譬如放上一些標誌位就可以了。原理上確實是行得通,但是這隨問題複雜程度加深而顯得非常難以處理。其中的第二個問題是有名的邊界判斷問題,程式中許許多多的錯誤都是由此引起。想一想,如果我們能夠分配一個能夠容納整個檔案的大記憶體塊該多好啊,這樣這兩個問題不都迎刃而解了嗎?是的,WIN32的記憶體對映檔案確實允許我們分配一個裝得下現實中可能存在的足夠大的檔案的記憶體。
利用記憶體對映檔案您可以認為作業系統已經為您把檔案全部裝入了記憶體,然後您只要移動檔案指標進行讀寫即可了。這樣您甚至不需要呼叫那些分配、釋放記憶體塊和檔案輸入/輸出的API函式,另外您可以把這用作不同的程式之間共享資料的一種辦法。運用記憶體對映檔案實際上沒有涉及實際的檔案操作,它更象為每個程式保留一個看得見的記憶體空間。至於把記憶體對映檔案當成程式間共享資料的辦法來用,則要加倍小心,因為您不得不處理資料的同步問題,否則您的應用程式也許很可能得到過時或錯誤的資料甚至崩潰。本課中我們將主要講述記憶體對映檔案,將不涉及程式間的同步。WIN32中的記憶體對映檔案應用非常廣泛,譬如:即使是系統的核心模組—PE格式檔案裝載器也用到了記憶體對映檔案,因為PE格式的檔案並不是一次性載入到記憶體中來的,譬如他它在首次載入時只載入必需載入的部分,而其他部分在用到時再載入,這正好可以利用到記憶體對映檔案的長處。實際中的大多數檔案存取都和PE載入器類似,所以您在處理該類問題時也應該充分利用記憶體對映檔案。
記憶體對映檔案本身還是有一些侷限性的,譬如一旦您生成了一個記憶體對映檔案,那麼您在那個會話期間是不能夠改變它的大小的。所以記憶體對映檔案對於只讀檔案和不會影響其大小的檔案操作是非常有用的。當然這並不意味著對於會引起改變其大小的檔案操作就一定不能用記憶體影射檔案的方法,您可以事先估計操作後的檔案的可能大小,然後生成這麼大小一塊的記憶體對映檔案,然後檔案的長度就可以增長到這麼一個大小。
參考:
記憶體檔案對映 c++ 谷歌
以下介紹如何使用CreateFileMapping,MapViewOfFile建立記憶體對映檔案,如何向記憶體對映檔案中寫入資料,讀取資料。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
#include <iostream> #include <assert.h> #include <Windows.h> #include <WinBase.h> #define BAD_POS 0xFFFFFFFF // returned by SetFilePointer and GetFileSize #define SUCCESS 0 using namespace std; typedef DWORD mmf_share_mode; typedef DWORD mmf_access_mode; typedef DWORD mmf_flags; int main(){ cout<<"create memorymapfile..."<<endl; const char* shared_name = "testMmf"; const char* file_name = "d:\\testMmf.mmf"; const DWORD mmf_size = 512*1024; //存取模式 mmf_access_mode access_mode = (GENERIC_READ|GENERIC_WRITE); //共享模式 mmf_share_mode share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; //檔案屬性 mmf_flags flags = FILE_FLAG_SEQUENTIAL_SCAN;//|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING; DWORD error_code; //建立檔案 HANDLE mmHandle = CreateFile(file_name, access_mode, share_mode, NULL, OPEN_ALWAYS, flags, NULL); if (mmHandle == INVALID_HANDLE_VALUE) { error_code = GetLastError(); cout<<"建立mmf失敗:"<<error_code<<endl; }else{ DWORD high_size; DWORD file_size = GetFileSize(mmHandle, &high_size); if (file_size == BAD_POS && (error_code = GetLastError()) != SUCCESS) { CloseHandle(mmHandle); cout<<"error:"<<error_code<<endl; } cout<<"create mmf sucessfully"<<endl; //assert(file_size == 0); DWORD size_high = 0; //建立檔案對映,如果要建立記憶體頁面檔案的對映,第一個引數設定為INVALID_HANDLE_VALUE HANDLE mmfm = CreateFileMapping(mmHandle, NULL, PAGE_READWRITE, size_high, mmf_size, shared_name); error_code = GetLastError(); if(SUCCESS != error_code){ cout<<"createFileMapping error"<<error_code<<endl; }else{ if(mmfm == NULL){ if(mmHandle != INVALID_HANDLE_VALUE){ CloseHandle(mmHandle); } }else{ //char write_chars[] = "hello chars"; //size_t position = 0; //DWORD written = 0; //const size_t write_chars_size = sizeof(write_chars); //WriteFile(mmHandle,write_chars,write_chars_size,&written,NULL); size_t view_size = 1024*256; DWORD view_access = FILE_MAP_ALL_ACCESS; //獲得對映檢視 char* mmfm_base_address = (char*)MapViewOfFile(mmfm,view_access,0,0,view_size); if(mmfm_base_address == NULL){ error_code = GetLastError(); if(error_code != SUCCESS){ cout<<"error code "<<error_code<<endl; } }else{ char write_chars[] = "hello chars"; const size_t write_chars_size = sizeof(write_chars); //向記憶體對映檢視中寫資料 CopyMemory((PVOID)mmfm_base_address, write_chars, write_chars_size); //memcpy(mmfm_base_address,write_chars,write_chars_size); size_t position = 0; char read_chars[write_chars_size]; //讀資料 memcpy(read_chars,mmfm_base_address,write_chars_size); cout<<"read chars "<<read_chars<<endl; //解除安裝對映 UnmapViewOfFile(mmfm_base_address); //關閉記憶體對映檔案 CloseHandle(mmfm); //關閉檔案 CloseHandle(mmHandle); } } } } system("pause"); exit(0); return EXIT_SUCCESS; } |