17.8 稀疏调拨的内存映射文件
17.8.1 稀疏文件简介
(1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间。NTFS文件系统对此进行了优化,那些无用的0字节被用一定的算法压缩起来。例如声明一个很大的稀疏文件(如100GB),这个文件实际上并不需要占用那么大的空,内部都是一些无用的0数据,那么NTFS就会利用算法释放这些无用的0字节空间,这是对磁盘占用空间的一种优化。但要注意FAT32并不支持稀疏文件的压缩。
(2)与稀疏文件操作有关的函数
①判断系统是否支持稀疏文件:GetVolumeInformation,通过传出的参数lpFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES判断结果是否为FILE_SUPPORTS_SPARSE_FILES。
②判断一个文件是否是稀疏文件:GetFileInformationByHandle
BY_HANDLE_FILE_INFORMATION stFileInfo;
GetFileInformationByHandle(hFile, &stFileInfo);
当stFileInfo.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE为TRUE时表示稀疏文件。
③产生一个稀疏文件:DeviceIoControl(hFile,FSCTL_SET_SPARSE,…);
大部分文件,在改变它的EndOfFile的时候,中间的空白会被操作系统填0,也就是说,如果用SetFilePointer和SetEndOfFile来产生一个很大的文件,那么这个文件它占用的是真正的磁盘空间,即使里面全是0,系统默认的也会在DeviceIoControl()中的ControlCode里用FSCTL_SET_ZERO_DATA标记,这个标记使得那些文件空洞被0所填充。为了节省磁盘空间,我们必须把一个文件声明为稀疏文件,以便让系统把那些无用的0字节压缩,并释放相应的磁盘空间,要将标记改为FSCTL_SET_SPARSE。
④查找稀疏文件中包含非零数据的范围
DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES,…);
17.8.2 以稀疏文件为后备存储器的内存映射文件(一般的使用步骤)
(1)创建文件:hFile = CreateFile(…);
(2)产生稀疏文件:
DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL));
(3)创建内存映射文件对象:
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE,…);
(4)映射到进程的地址空间
pvFile = MapViewOfFile(hFileMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
(5)读、写文件(像直接读写内存一样的读写文件)
(6)撤消映射:UnmapViewOfFile(pvFile);
(7)关闭句柄:CloseHandle(hFileMap);CloseHandle(hFile);
【MMFSparse程序】创建一个NTFS稀疏文件为后备存储器的内存映射文件
//MMFSparse.cpp
/************************************************************************ Module: MMFSparse.cpp Notices: Copyright(c) 2008 Jeffrey Ritcher & Christophe Nasarre ************************************************************************/ #include "../../CommonFiles/CmnHdr.h" #include "SparseStream.h" #include "resource.h" #include <tchar.h> #include <strsafe.h> ////////////////////////////////////////////////////////////////////////// //定义一个稀疏文件的内存映射文件操作类 class CMMFSparse :public CSparseStream{ private: HANDLE m_hFileMap; //文件映射对象 PVOID m_pvFile; //视图开始的地址 public: //构造函数(hStream为外部传进来的文件对象的句柄) CMMFSparse(HANDLE hStream = NULL, DWORD dwStreamSizeMaxLow = 0, DWORD dwStreamSizeMaxHigh = 0){ Initialize(hStream, dwStreamSizeMaxLow, dwStreamSizeMaxHigh); } //关闭Sparse MMF virtual ~CMMFSparse(){ ForceClose(); } //创建一个以稀疏文件为后备存储器的MMF对象,并映射到进程的地址空间 BOOL Initialize(HANDLE hStream = NULL, DWORD dwStreamSizeMaxLow = 0, DWORD dwStreamSizeMaxHigh = 0); //将CMMFSparse类的对象指针,转化为视图的第1个字节的地址 operator PBYTE() const { return (PBYTE)m_pvFile;} //允许显式的关闭MMF,而不需要等待析构函数的执行 VOID ForceClose(); }; ////////////////////////////////////////////////////////////////////////// BOOL CMMFSparse::Initialize(HANDLE hStream, DWORD dwStreamSizeMaxLow, DWORD dwStreamSizeMaxHigh){ if (m_hFileMap != NULL) ForceClose(); //初始化 m_hFileMap = m_pvFile = NULL; BOOL bOk = TRUE; //假设是成功的 if (hStream != NULL){ if ((dwStreamSizeMaxLow == 0) && (dwStreamSizeMaxHigh ==0)){ DebugBreak(); //非法的参数 } CSparseStream::Initialize(hStream); bOk = MakeSparse(); //产生一个稀疏文件 if (bOk){ //创建一个内存文件映射对象 m_hFileMap = ::CreateFileMapping(hStream, NULL, PAGE_READWRITE, dwStreamSizeMaxHigh,dwStreamSizeMaxLow,NULL); if (m_hFileMap != NULL){ //将m_hFileMap映射到进程的地址空间,形成一个视图 m_pvFile = ::MapViewOfFile(m_hFileMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); } else{ //映射失败时 CSparseStream::Initialize(NULL); ForceClose(); bOk = FALSE; } } } return (bOk); } ////////////////////////////////////////////////////////////////////////// VOID CMMFSparse::ForceClose(){ //清理 if (m_pvFile != NULL){ ::UnmapViewOfFile(m_pvFile); //撤消映射 m_pvFile = NULL; } if (m_hFileMap != NULL){ ::CloseHandle(m_hFileMap); m_hFileMap = NULL; } } ////////////////////////////////////////////////////////////////////////// TCHAR g_szPathname[MAX_PATH] = TEXT("\0"); HANDLE g_hStream = INVALID_HANDLE_VALUE; CMMFSparse g_mmf; #define STREAMSIZE (1*1024*1024) //1MB (1024 KB) ////////////////////////////////////////////////////////////////////////// void Dlg_ShowAllocatedRanges(HWND hwnd){ //内存映射文件有实际的后备储存器的情况显示在“己分配范围”编辑框中 DWORD dwNumEntries; FILE_ALLOCATED_RANGE_BUFFER* pfarb = g_mmf.QueryAllocateRanges(&dwNumEntries); if (dwNumEntries ==0){ SetDlgItemText(hwnd, IDC_FILESTATUS, TEXT("文件中没有己分配的范围")); } else{ TCHAR sz[4096] = { 0 }; for (DWORD dwEntry = 0; dwEntry < dwNumEntries;dwEntry++) StringCchPrintf(_tcschr(sz, _T(‘\0‘)), _countof(sz) - _tcslen(sz), TEXT("偏移:0x%05X,长度:%u KB\r\n"), //TEXT("偏移:%7.7u,长度:%7.7u\r\n"), pfarb[dwEntry].FileOffset.LowPart, pfarb[dwEntry].Length.LowPart/1024); SetDlgItemText(hwnd, IDC_FILESTATUS, sz); } g_mmf.FreeAllocateRanges(pfarb); } ////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){ chSETDLGICONS(hwnd, IDI_MMFSPARSE); //初始化对话框上的各控件 EnableWindow(GetDlgItem(hwnd, IDC_OFFSET), FALSE); Edit_LimitText(GetDlgItem(hwnd, IDC_OFFSET), 4); SetDlgItemInt(hwnd, IDC_OFFSET, 1000, FALSE); EnableWindow(GetDlgItem(hwnd, IDC_BYTE), FALSE); Edit_LimitText(GetDlgItem(hwnd, IDC_BYTE),3); SetDlgItemInt(hwnd, IDC_BYTE, 5, FALSE); EnableWindow(GetDlgItem(hwnd, IDC_WRITEBYTE), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_READBYTE), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_FREEALLOCATEDREGIONS), FALSE); //将文件保存在一个可写的文件夹中 GetCurrentDirectory(_countof(g_szPathname), g_szPathname); _tcscat_s(g_szPathname, _countof(g_szPathname), TEXT("\\MMFSparse")); //检查当前驱动器是否支持稀疏文件 TCHAR szVolume[16]; PTSTR pEndOfVolume = _tcschr(g_szPathname, _T(‘\\‘)); //查找驱动器号 if (pEndOfVolume == NULL){ chFAIL("无法在默认的文件夹中找到驱动器的卷标"); DestroyWindow(hwnd); return TRUE; } _tcsncpy_s(szVolume, _countof(szVolume), g_szPathname,pEndOfVolume-g_szPathname +1); //含‘\‘,形如:C:\ if (!CSparseStream::DoesFileSystemSupportSparseStreams(szVolume)){ chFAIL("默认的文件夹所在驱动器不支持稀疏文件!"); DestroyWindow(hwnd); return TRUE; } return TRUE; } ////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity){ switch (id) { case IDCANCEL: EndDialog(hwnd, id); break; case IDC_CREATEMMF: { g_hStream = CreateFile(g_szPathname, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (g_hStream == INVALID_HANDLE_VALUE){ chFAIL("创建文件失败!"); return; } //创建一个1MB(1024KB)的MMF if (!g_mmf.Initialize(g_hStream,STREAMSIZE,0)){ chFAIL("初始化稀疏内存映射文件失败!"); CloseHandle(g_hStream); g_hStream = NULL; return; } //显示分配情况 Dlg_ShowAllocatedRanges(hwnd); //启用或禁用相关控件 EnableWindow(GetDlgItem(hwnd, IDC_CREATEMMF), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_OFFSET), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_BYTE), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_WRITEBYTE), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_READBYTE), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_FREEALLOCATEDREGIONS), TRUE); //将焦点设置在“偏移”编辑框 SetFocus(GetDlgItem(hwnd, IDC_OFFSET)); } break; case IDC_READBYTE: { BOOL bTranslated; DWORD dwOffset = GetDlgItemInt(hwnd, IDC_OFFSET, &bTranslated,//转换是否成功 FALSE);//无符号数 if (bTranslated){ SetDlgItemInt(hwnd, IDC_BYTE, g_mmf[dwOffset * 1024], FALSE); Dlg_ShowAllocatedRanges(hwnd); } } break; case IDC_WRITEBYTE: { BOOL bTranslated; DWORD dwOffset = GetDlgItemInt(hwnd, IDC_OFFSET, &bTranslated,//转换是否成功 FALSE);//无符号数 if (bTranslated){ g_mmf[dwOffset * 1024] = (BYTE) GetDlgItemInt(hwnd,IDC_BYTE,NULL,FALSE); Dlg_ShowAllocatedRanges(hwnd); } } break; case IDC_FREEALLOCATEDREGIONS: { //通常,析构函数会关闭内存映射文件。 //但这里,我们是按“释放”按钮来强制关闭的,以便重置文件的一些区域为0 g_mmf.ForceClose(); //我们可以在上面调用ForceClose函数是因为当试图将一个己映射的 //文件的部分区域置0时,会引起DeviceIoControl调用失败。 g_mmf.DecommitPortionOfStream(0, STREAMSIZE);//文件所有字节都置0 //要关闭文件句柄并重新打弄,以便将更改刷新稀疏文件的状态 CloseHandle(g_hStream); //重新创建新文件 g_hStream = CreateFile(g_szPathname, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (g_hStream == INVALID_HANDLE_VALUE){ chFAIL("创建文件失败!"); return; } //重置为稀疏文件 g_mmf.Initialize(g_hStream, STREAMSIZE, 0); //更新界面 Dlg_ShowAllocatedRanges(hwnd); } break; } } ////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return FALSE; } ////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int){ DialogBox(hInstExe, MAKEINTRESOURCE(IDD_MMFSPARSE), NULL, Dlg_Proc); return 0; }
//SparseStream.h
/************************************************************************ Module: SparseStream.h Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre ************************************************************************/ #include "../../CommonFiles/CmnHdr.h" //#include <WinIoCtl.h> //For FILE_ALLOCATED_RANGE_BUFFER* #pragma once ////////////////////////////////////////////////////////////////////////// class CSparseStream{ public: //判断某个驱动器是否支持稀疏文件 static BOOL DoesFileSystemSupportSparseStreams(PCTSTR pszVolume); //判断一个文件是否是稀疏文件(静态方法) static BOOL DoesFileContainAnySparseStreams(PCTSTR pszPathname); public: CSparseStream(HANDLE hStream = INVALID_HANDLE_VALUE){ Initialize(hStream); } virtual ~CSparseStream(){} void Initialize(HANDLE hStream = INVALID_HANDLE_VALUE){ m_hStream = hStream; } public: BOOL IsStreamSparse() const; //是否是稀疏文件(非静态方法) BOOL MakeSparse(); //产生稀疏文件 //给稀疏文件指定范围的内容填0 BOOL DecommitPortionOfStream(__int64 qwFileOffsetStart, __int64 qwFileOffsetEnd); //查找稀疏文件中非零数据的范围 FILE_ALLOCATED_RANGE_BUFFER* QueryAllocateRanges(PDWORD pdwNumEntries); //释放QueryAllocateRanges分配到的内存空间 BOOL FreeAllocateRanges(FILE_ALLOCATED_RANGE_BUFFER* pfarb); public: operator HANDLE() const{ return (m_hStream);} private: HANDLE m_hStream; private: static BOOL AreFlagsSet(DWORD fdwFlagBits, DWORD fFlagsToCheck){ return ((fdwFlagBits & fFlagsToCheck) == fFlagsToCheck); } }; ////////////////////////////////////////////////////////////////////////// inline BOOL CSparseStream::DoesFileSystemSupportSparseStreams(PCTSTR pszVolume){ DWORD dwFileSystemFlags = 0; BOOL bOk = GetVolumeInformation(pszVolume, NULL, 0, NULL, NULL, &dwFileSystemFlags, NULL, 0); bOk = bOk&& AreFlagsSet(dwFileSystemFlags, FILE_SUPPORTS_SPARSE_FILES); return bOk; } ////////////////////////////////////////////////////////////////////////// inline BOOL CSparseStream::DoesFileContainAnySparseStreams(PCTSTR pszPathname){ DWORD dw = GetFileAttributes(pszPathname); return ((dw == 0xFFFFFFFF) ? FALSE: AreFlagsSet(dw,FILE_ATTRIBUTE_SPARSE_FILE)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CSparseStream::IsStreamSparse() const{ BY_HANDLE_FILE_INFORMATION bhfi; GetFileInformationByHandle(m_hStream, &bhfi); return (AreFlagsSet(bhfi.dwFileAttributes, FILE_ATTRIBUTE_SPARSE_FILE)); } ////////////////////////////////////////////////////////////////////////// inline BOOL CSparseStream::MakeSparse(){ DWORD dw; return (DeviceIoControl(m_hStream, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL)); } ////////////////////////////////////////////////////////////////////////// //给文件指定范围的内容填0.如果文件是稀疏文件,NTFS会释放文件,并不会扩大文件的实际大小 inline BOOL CSparseStream::DecommitPortionOfStream(__int64 qwFileOffsetStart, __int64 qwFileOffsetEnd){ //注意:如果文件尚未被映射时,该函数将不能正常工作 DWORD dw; FILE_ZERO_DATA_INFORMATION fzdi; fzdi.FileOffset.QuadPart = qwFileOffsetStart; fzdi.BeyondFinalZero.QuadPart = qwFileOffsetEnd + 1; return (DeviceIoControl( m_hStream, //文件句柄 FSCTL_SET_ZERO_DATA,//控制码 (PVOID)&fzdi, //input buffer,指明了文件要设置为0的范围 sizeof(fzdi), //缓冲区大小 NULL, //lpOutBuffer 0, //nOutBufferSize &dw, //number of bytes returned NULL)); } ////////////////////////////////////////////////////////////////////////// //查找稀疏文件中包含非零数据的范围 FILE_ALLOCATED_RANGE_BUFFER* CSparseStream::QueryAllocateRanges(PDWORD pdwNumEntries){ FILE_ALLOCATED_RANGE_BUFFER farb; farb.FileOffset.QuadPart = 0; farb.Length.LowPart = GetFileSize(m_hStream, (PDWORD)&farb.Length.HighPart); //试图收集数据之前,我们没有办法切确地知道内存块的大小,这里我们选择100*sizeof(farb) DWORD cb = 100 * sizeof(farb); FILE_ALLOCATED_RANGE_BUFFER* pfarb = (FILE_ALLOCATED_RANGE_BUFFER*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb); DeviceIoControl(m_hStream, FSCTL_QUERY_ALLOCATED_RANGES, &farb,sizeof(farb),//输入缓冲区 pfarb,cb,&cb, //输出缓冲区 NULL); *pdwNumEntries = cb / sizeof(*pfarb); return (pfarb); } ////////////////////////////////////////////////////////////////////////// inline BOOL CSparseStream::FreeAllocateRanges(FILE_ALLOCATED_RANGE_BUFFER* pfarb){ return (HeapFree(GetProcessHeap(), 0, pfarb)); } /////////////////////////////文件结束/////////////////////////////////////
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 17_MMFSparse.rc 使用 // #define IDD_MMFSPARSE 1 #define IDC_CREATEMMF 101 #define IDI_MMFSPARSE 102 #define IDC_OFFSET 103 #define IDC_WRITEBYTE 105 #define IDC_READBYTE 106 #define IDC_BYTE 109 #define IDC_FILESTATUS 1000 #define IDC_FREEALLOCATEDREGIONS 1002 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//MMFSparse.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_MMFSPARSE ICON "MMFSparse.ico" ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_MMFSPARSE DIALOGEX 15, 24, 188, 192 STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "MMF Sparse" FONT 10, "宋体", 400, 0, 0x0 BEGIN DEFPUSHBUTTON "创建一个1M(1024KB)的稀疏内存映射文件对象",IDC_CREATEMMF,10,3,168,14,WS_GROUP LTEXT "偏移(0 - 1023KB):",IDC_STATIC,10,23,68,8 EDITTEXT IDC_OFFSET,81,21,44,12 PUSHBUTTON "读取字节",IDC_READBYTE,132,19,46,14 LTEXT "字节(0-255):",IDC_STATIC,11,38,47,8 EDITTEXT IDC_BYTE,81,36,44,12,ES_UPPERCASE | ES_NUMBER PUSHBUTTON "写入字节",IDC_WRITEBYTE,132,35,46,14 PUSHBUTTON "释放所有己分配的区域",IDC_FREEALLOCATEDREGIONS,80,51,98,14 LTEXT "分配范围:",IDC_STATIC,9,63,59,8 EDITTEXT IDC_FILESTATUS,9,72,170,116,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_MMFSPARSE, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 184 TOPMARGIN, 1 BOTTOMMARGIN, 190 END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
17.8.3 内存映射文件的其他问题
(1)CreateFileMapping中fdwProtect参数的讨论
①SEC_COMMIT:只有当以页交换文件为后备存储器时才有起作用,用于给区域调拨物理存储器。不指定该标志时,默认也是以SEC_COMMIT创建内存映射文件的。(SEC,Section的缩写)。
②SEC_RESERVE:也是以页交换文件为后备存储器,但系统不会从页交换文件中调拨物理存储器,只返回文件映射对象的句柄。
(2)利用SEC_RESERVE进行部分区域的调拨
①默认下CreateFileMapping映射文件时,会传入SEC_COMMIT标志,所以MapViewOfFile会预订一块地址空间区域,并给区域(大小由该函数的dwNumberOfBytesToMap参数指定)全部调拨物理存储器,这有点浪费。我们可以在CreateFileMapping创建时传入SEC_RESERVE,用于告诉MapViewOfFile只预订这个区域,而不调拨物理存储器!
②区域的部分调拨的具体步骤:
A、CreateFileMapping时传入SEC_RESERVE。
B、创建视图:pvAddress = MapViewOfFile,这时会得到一个区域,但只是预订而不会给区域调拨物理存储器。
C、调用VirtualAlloc给区域调拨物理存储器,传上面的pvAddress传入该函数
D、这里可以像一般的内存映射文件一样的使用了。
③注意事项:
A、通过SEC_RESERVE标志得到的内存映射文件,不能用VirtualFree来撤消调拨的物理存储器,仍然要用UnmapViewOfFile。
B、SEC_RESERVE和SEC_COMMIT标志只能用在以“页交换”文件为后备存储器的内存映射文件。