1、文件内存映射基础
remember the following key points
1、The file is present on the disk of the machine on which the processes are running.
2、The file mapping object is present in the physical memory.
3、More than one process can create views for the same file mapping object. 多个进程可以为同一文件映射对象创建视图。
4、The file mapping object can contain the entire file or a part of it. Similarly, the file view for processes can contain the entire file mapping object or a part of it.
文件映射对象可以包含整个文件或其中的一部分。同样,进程的文件视图可以包含整个文件映射对象或其中的一部分。
5、All the copies are coherent and the same as that present on the disk
所有副本都是连贯的,并且与磁盘上的副本相同。
Advantages
1、It is a great help when working with huge files like database files as not the whole file needs to be present in the physical memory.
在处理数据库文件等大型文件时,这是一个很大的帮助,因为整个文件不需要存在于物理内存中。
2、More than one process can use a file on the disk for both read and write operations. Each process can create a new view, un-mapping the current file view.
多个进程可以使用磁盘上的文件进行读取和写入操作。每个进程都可以创建一个新视图,取消映射当前文件视图。
创建文件映射对象和文件视图的步骤
步骤 1:创建或打开表示磁盘上文件的文件对象。在这里,我们创建了一个新的文件对象,其句柄为 hFile,并命名为“datafile.txt”。
// Can be used as
HANDLE hFile = CreateFile(TEXT("datafile.txt"),
GENERIC_READ | GENERIC_WRITE,
0, // open with exclusive access
NULL, // no security attributes
CREATE_NEW, // creating a new temp file
FILE_FLAG_DELETE_ON_CLOSE, //delete the file after unmapping the view
NULL);
步骤 2:为文件创建一个映射对象,其中包含有关如何访问文件及其大小的信息。因此,在创建上述文件后,我们使用它的句柄并在物理内存中创建其映射。
HANDLE hFileMapping = ::CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bufferSize, filename);
步骤 3:将物理内存中的全部或部分文件映射对象映射到进程的虚拟地址空间。因此,在这里,我们将创建映射文件的视图,该视图将由该过程使用。
void* p = ::MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, param1, param2);
第 4 步:清理
A: 从进程地址空间取消映射文件映射对象。回溯上述步骤,首先,从进程的地址空间中删除文件视图。
UnmapViewOfFile(p);
B: 关闭文件映射对象。此步骤从物理内存中删除文件映射。
CloseHandle(hFileMapping);
C: 关闭文件对象。在这里,关闭在磁盘上打开的文件并释放句柄。由于在第一步中我们设置了标志FILE_FLAG_DELETE_ON_CLOSE,因此在此步骤之后将删除该文件。
CloseHandle(hFile);
注意:
Close the handles in the same order or else it will give rise to discrepancies.
以相同的顺序关闭手柄,否则会引起差异。
Close all the open file handles before trying to delete the file.
在尝试删除文件之前,请关闭所有打开的文件句柄。
流程适当改造适用于读取更大的文件:
1)映射文件开头的映像。
2)对该映像进行访问。
3)取消此映像
4)映射一个从文件中的一个更深的位移开始的新映像。
5)重复步骤2,直到访问完全部的文件数据。
HANDLE CreateFileA(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
HANDLE CreateFileMappingA(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCSTR lpName
);
LPVOID MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);
MapViewOfFile()函数负责把文件数据映射到进程的地址空间
hFileMappingObject:
CreateFileMapping()返回的文件映像对象句柄
dwDesiredAccess
再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配,主要可以使应用程序能更多的对数据的保护属性实行有效控制
dwFileOffsetHigh
dwFileOffsetLow
MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,
文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB
动态获取当前操作系统的分配粒度:
SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;
dwNumberOfBytesToMap
指定了数据文件的映射长度
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放
唯一的参数lpBaseAddress指定了返回区域的基地址,必须将其设定为MapViewOfFile()的返回值。在使用了函数MapViewOfFile()之后,必须要有对应的UnmapViewOfFile()调用,
否则在进程终止之前,保留的区域将无法释放。除此之外,前面还曾由CreateFile()和CreateFileMapping()函数创建过文件内核对象和文件映射内核对象,
在进程终止之前有必要通过CloseHandle()将其释放,否则将会出现资源泄漏的问题。
// FileMappingTest.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { HANDLE hFile = CreateFile(_T("D:\\TDDOWNLOAD\\The.Hobbit.2014.Battle.Of.The.Five.Armies.2014.1080p.WEB-DL.x264.AC3-EVO"), GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { cout<<"创建文件对象失败,错误代码 "<<GetLastError()<<endl; return 0; } HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (hFileMap == NULL) { cout<<"创建文件映射对象失败,错误代码 "<<GetLastError()<<endl; return 0; } SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); DWORD dwGran = SysInfo.dwAllocationGranularity; DWORD dwFileSizeHigh; __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh); qwFileSize |= (((__int64)dwFileSizeHigh) << 32); CloseHandle(hFile); __int64 qwFileOffset = 0; DWORD dwBlockBytes = 1000 * dwGran; if (qwFileSize < 1000 * dwGran) { dwBlockBytes = (DWORD)qwFileSize; } __int64 llCount = 0; while (qwFileSize > 0) { LPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),dwBlockBytes); if (lpbMapAddress == NULL) { cout<<"创建文件映射对象失败,错误代码 "<<GetLastError()<<endl; return 0; } DWORD dwCount = 0; for(DWORD i = 0; i < dwBlockBytes; i++) { BYTE temp = *(lpbMapAddress + i); dwCount++; } llCount+=dwCount; cout<<"Current read:"<<dwCount<<"bytes, total read: "<<llCount<<"btyes"<<endl; UnmapViewOfFile(lpbMapAddress); qwFileOffset += dwBlockBytes; qwFileSize -= dwBlockBytes; if (qwFileSize < dwBlockBytes) { dwBlockBytes = qwFileSize; } } CloseHandle(hFileMap); cout<<"Done"<<endl; return 0; }