內存映射文件你們都時不時聽過,但它究竟是個什麼?趕忙來看看吧ios
內存映射文件究竟是幹嗎的呢?讓咱們先來思考下面幾個問題:app
若是您想讀的內容大於系統分配的內存塊怎麼辦?若是您想搜索的字符串恰好超過內存塊的邊界又該如何處理?對於第一個問題,您也許會說,只要不斷地讀就不解決了嗎。至於第二個問題,您又會說在內存塊的邊界處作一些特別的處理,譬如放上一些標誌位就能夠了。原理上確實是行得通,可是這隨問題複雜程度加深而顯得很是難以處理。其中的第二個問題是有名的邊界判斷問題,程序中許許多多的錯誤都是由此引發。想想,若是咱們可以分配一個可以容納整個文件的大內存塊該多好啊,這樣這兩個問題不都迎刃而解了嗎?是的,WIN32的內存映射文件確實容許咱們分配一個裝得下現實中可能存在的足夠大的文件的內存。函數
#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; }
#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函數搞定了,通常的內存映射問題就能夠解決了。。學習
利用內存映射文件您能夠認爲操做系統已經爲您把文件所有裝入了內存,而後您只要移動文件指針進行讀寫便可了。這樣您甚至不須要調用那些分配、釋放內存塊和文件輸入/輸出的API函數,另外您能夠把這用做不一樣的進程之間共享數據的一種辦法。運用內存映射文件實際上沒有涉及實際的文件操做,它更象爲每一個進程保留一個看得見的內存空間。spa
至於把內存映射文件當成進程間共享數據的辦法來用,則要加倍當心,由於您不得不處理數據的同步問題,不然您的應用程序也許極可能獲得過期或錯誤的數據甚至崩潰。本課中咱們將主要講述內存映射文件,將不涉及進程間的同步。WIN32中的內存映射文件應用很是普遍,譬如:即便是系統的核心模塊—PE格式文件裝載器也用到了內存映射文件,由於PE格式的文件並非一次性加載到內存中來的,譬如他它在首次加載時只加載必需加載的部分,而其餘部分在用到時再加載,這正好能夠利用到內存映射文件的長處。實際中的大多數文件存取都和PE加載器相似,因此您在處理該類問題時也應該充分利用內存映射文件。操作系統
如下介紹如何使用CreateFileMapping,MapViewOfFile建立內存映射文件,如何向內存映射文件中寫入數據,讀取數據。指針
#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; }
內存映射文件自己仍是有一些侷限性的,譬如一旦您生成了一個內存映射文件,那麼您在那個會話期間是不可以改變它的大小的。因此內存映射文件對於只讀文件和不會影響其大小的文件操做是很是有用的。固然這並不意味着對於會引發改變其大小的文件操做就必定不能用內存影射文件的方法,您能夠事先估計操做後的文件的可能大小,而後生成這麼大小一塊的內存映射文件,而後文件的長度就能夠增加到這麼一個大小。code
#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; }
最後仍是要推薦下小編的C/C++學習羣:710520381,邀請碼(柳貓),無論你是小白仍是大牛,小編我都歡迎,不按期分享乾貨,包括小編本身整理的一份2018最新的C/C++和0基礎入門教程,歡迎初學和進階中的小夥伴。orm