https://learn.microsoft.com/en-us/windows/win32/memory/using-file-mapping
Creating a File View
To map the data from a file to the virtual memory of a process, you must create a view of the file. The MapViewOfFile and MapViewOfFileEx functions use the file mapping object handle returned by CreateFileMapping to create a view of the file or a portion of the file in the process's virtual address space. These functions fail if the access flags conflict with those specified when CreateFileMapping created the file mapping object.
若要将数据从文件映射到进程的虚拟内存,必须创建文件的视图。MapViewOfFile 和 MapViewOfFileEx 函数使用 CreateFileMapping 返回的文件映射对象句柄在进程的虚拟地址空间中创建文件或文件的一部分的视图。如果访问标志与 CreateFileMapping 创建文件映射对象时指定的标记冲突,则这些函数将失败。
The MapViewOfFile function returns a pointer to the file view. By dereferencing a pointer in the range of addresses specified in MapViewOfFile, an application can read data from the file and write data to the file. Writing to the file view results in changes to the file mapping object. The actual writing to the file on disk is handled by the system. Data is not actually transferred at the time the file mapping object is written to. Instead, much of the file input and output (I/O) is cached to improve general system performance. Applications can override this behavior by calling the FlushViewOfFile function to force the system to perform disk transactions immediately.
MapViewOfFile 函数返回指向文件视图的指针。通过取消引用 MapViewOfFile 中指定的地址范围内的指针,应用程序可以从文件中读取数据并将数据写入文件。写入文件视图会导致对文件映射对象的更改。对磁盘上文件的实际写入由系统处理。在写入文件映射对象时,数据实际上不会传输。相反,大部分文件输入和输出 (I/O) 都会被缓存以提高一般系统性能。应用程序可以通过调用 FlushViewOfFile 函数来重写此行为,以强制系统立即执行磁盘事务。
The MapViewOfFileEx function works exactly like the MapViewOfFile function except that it allows a process to specify the base address of the view of the file in the process's virtual address space in the lpvBase parameter. If there is not enough space at the specified address, the call fails. Therefore, if you must map a file to the same address in multiple processes, the processes should negotiate an appropriate address: The lpvBase parameter must be an integral multiple of the system memory allocation granularity or the call fails. To obtain the system's memory allocation granularity, use the GetSystemInfo function, which fills in the members of a SYSTEM_INFO structure.
MapViewOfFileEx 函数的工作方式与 MapViewOfFile 函数完全相同,只不过它允许进程在 lpvBase 参数中指定进程虚拟地址空间中文件视图的基址。如果指定地址没有足够的空间,则调用将失败。因此,如果必须在多个进程中将文件映射到同一地址,则进程应协商适当的地址: lpvBase 参数必须是系统内存分配粒度的整数倍,否则调用将失败。若要获取系统的内存分配粒度,请使用 GetSystemInfo 函数,该函数填充SYSTEM_INFO结构的成员。
An application can create multiple file views from the same file mapping object. A file view can be a different size than the file mapping object from which it is derived, but it must be smaller than the file mapping object. The offset specified by the dwOffsetHigh and dwOffsetLow parameters of MapViewOfFile must be a multiple of the allocation granularity of the system.
应用程序可以从同一个文件映射对象创建多个文件视图。文件视图的大小可以与派生它的文件映射对象不同,但它必须小于文件映射对象。MapViewOfFile 的 dwOffsetHigh 和 dwOffsetLow 参数指定的偏移量必须是系统分配粒度的倍数。
Creating a File Mapping Object
1、
The first step in mapping a file is to open the file by calling the CreateFile function. To ensure that other processes cannot write to the portion of the file that is mapped, you should open the file with exclusive access. In addition, the file handle should remain open until the process no longer needs the file mapping object. An easy way to obtain exclusive access is to specify zero in the fdwShareMode parameter of CreateFile. The handle returned by CreateFile is used by the CreateFileMapping function to create a file mapping object.
映射文件的第一步是通过调用 CreateFile 函数打开文件。若要确保其他进程无法写入映射的文件部分,应以独占访问权限打开该文件。此外,文件句柄应保持打开状态,直到进程不再需要文件映射对象。获取独占访问权限的一种简单方法是在 CreateFile 的 fdwShareMode 参数中指定零。CreateFileMapping 函数使用 CreateFile 返回的句柄创建文件映射对象。
The CreateFileMapping function returns a handle to the file mapping object. This handle will be used when creating a file view so that you can access the shared memory. When you call CreateFileMapping, you specify an object name, the number of bytes to be mapped from the file, and the read/write permission for the mapped memory. The first process that calls CreateFileMapping creates the file mapping object. Processes calling CreateFileMapping for an existing object receive a handle to the existing object. You can tell whether or not a successful call to CreateFileMapping created or opened the file mapping object by calling the GetLastError function. GetLastError returns NO_ERROR to the creating process and ERROR_ALREADY_EXISTS to subsequent processes.
CreateFileMapping 函数返回文件映射对象的句柄。创建文件视图时将使用此句柄,以便您可以访问共享内存。调用 CreateFileMapping 时,可以指定对象名称、要从文件映射的字节数以及映射内存的读/写权限。调用 CreateFileMapping 的第一个进程创建文件映射对象。对现有对象调用 CreateFileMapping 的进程接收现有对象的句柄。可以通过调用 GetLastError 函数来判断对 CreateFileMapping 的成功调用是否创建或打开了文件映射对象。GetLastError 将NO_ERROR返回给创建进程,ERROR_ALREADY_EXISTS返回给后续进程。
The CreateFileMapping function fails if the access flags conflict with those specified when the CreateFile function opened the file. For example, to read and write to the file:
如果访问标志与 CreateFile 函数打开文件时指定的标记冲突,则 CreateFileMapping 函数将失败。例如,要读取和写入文件,请执行以下操作:
Specify the GENERIC_READ and GENERIC_WRITE values in the fdwAccess parameter of CreateFile.
在 CreateFile 的 fdwAccess 参数中指定 GENERIC_READ 和 GENERIC_WRITE 值。
Specify the PAGE_READWRITE value in the fdwProtect parameter of CreateFileMapping.
在 CreateFileMapping 的 fdwProtect 参数中指定PAGE_READWRITE值。
Creating a file mapping object does not commit physical memory, it only reserves it.
创建文件映射对象不会提交物理内存,它只会保留它。
2、File Mapping Size 文件映射大小
The size of the file mapping object is independent of the size of the file being mapped. However, if the file mapping object is larger than the file, the system expands the file before CreateFileMapping returns. If the file mapping object is smaller than the file, the system maps only the specified number of bytes from the file.
文件映射对象的大小与要映射的文件的大小无关。但是,如果文件映射对象大于文件,则系统会在 CreateFileMapping 返回之前展开该文件。如果文件映射对象小于文件,则系统仅映射文件中指定的字节数。
The dwMaximumSizeHigh and dwMaximumSizeLow parameters of CreateFileMapping allow you to specify the number of bytes to be mapped from the file:
CreateFileMapping 的 dwMaximumSizeHigh 和 dwMaximumSizeLow 参数允许您指定要从文件映射的字节数:
When you do not want the size of the file to change (for example, when mapping read-only files), call CreateFileMapping and specify zero for both dwMaximumSizeHigh and dwMaximumSizeLow. Doing this creates a file mapping object that is exactly the same size as the file. Otherwise, you must calculate or estimate the size of the finished file because file mapping objects are static in size; once created, their size cannot be increased or decreased. An attempt to map a file with a length of zero in this manner fails with an error code of ERROR_FILE_INVALID. Programs should test for files with a length of zero and reject such files.
如果不希望更改文件的大小(例如,在映射只读文件时),请调用 CreateFileMapping 并为 dwMaximumSizeHigh 和 dwMaximumSizeLow 指定零。执行此操作将创建一个与文件大小完全相同的文件映射对象。否则,必须计算或估计完成文件的大小,因为文件映射对象的大小是静态的;一旦创建,其大小就无法增加或减少。尝试以这种方式映射长度为零的文件将失败,错误代码为 ERROR_FILE_INVALID。程序应测试长度为零的文件并拒绝此类文件。
The size of a file mapping object that is backed by a named file is limited by disk space. The size of a file view is limited to the largest available contiguous block of unreserved virtual memory. This is at most 2 GB minus the virtual memory already reserved by the process.
命名文件支持的文件映射对象的大小受磁盘空间限制。文件视图的大小限制为最大可用连续的未预留虚拟内存块。这最多是 2 GB 减去进程已保留的虚拟内存。
The size of the file mapping object that you select controls how far into the file you can "see" with memory mapping. If you create a file mapping object that is 500 Kb in size, you have access only to the first 500 Kb of the file, regardless of the size of the file. Since it does not cost you any system resources to create a larger file mapping object, create a file mapping object that is the size of the file (set the dwMaximumSizeHigh and dwMaximumSizeLow parameters of CreateFileMapping both to zero) even if you do not expect to view the entire file. The cost in system resources comes in creating the views and accessing them.
所选文件映射对象的大小控制了内存映射可以“看到”到文件的深度。如果创建大小为 500 Kb 的文件映射对象,则无论文件大小如何,您都只能访问文件的前 500 KB。由于创建较大的文件映射对象不会花费任何系统资源,因此即使不希望查看整个文件,也要创建一个文件大小的文件映射对象(将 CreateFileMapping 的 dwMaximumSizeHigh 和 dwMaximumSizeLow 参数都设置为零)。系统资源的成本来自创建视图和访问它们。
You can view a portion of the file that does not start at the beginning of the file. For more information, see Creating a View Within a File.
您可以查看文件的不从文件开头开始的部分。有关详细信息,请参阅在文件中创建视图。
Creating a View Within a File
If you want to view a portion of the file that does not start at the beginning of the file, you must create a file mapping object. This object is the size of the portion of the file you want to view plus the offset into the file. For example, if you want to view the 1 kilobyte (1K) that begins 131,072 bytes (128K) into the file, you must create a file mapping object of at least 132,096 bytes (129K) in size. The view starts 131,072 bytes (128K) into the file and extend for at least 1,024 bytes. This example assumes a file allocation granularity of 64K.
如果要查看文件的某个部分,但该部分不是从文件的开头开始的,则必须创建一个文件映射对象。此对象是要查看的文件部分的大小加上文件的偏移量。例如,如果要查看文件开头 131,072 字节 (128K) 的 1 KB (1K),则必须创建大小至少为 132,096 字节 (129K) 的文件映射对象。视图从 131,072 字节 (128K) 开始进入文件,并至少扩展 1,024 字节。此示例假定文件分配粒度为 64K。
File allocation granularity affects where a map view can start. A map view must start at an offset into the file that is a multiple of the file allocation granularity. So the data you want to view may be the file offset modulo the allocation granularity into the view. The size of the view is the offset of the data modulo the allocation granularity, plus the size of the data that you want to examine.
文件分配粒度会影响地图视图的启动位置。地图视图必须从文件分配粒度倍数的文件偏移量开始。因此,您要查看的数据可能是文件偏移模,分配粒度进入视图。视图的大小是数据模的偏移量、分配粒度,加上要检查的数据的大小。
For example, suppose that the GetSystemInfo function indicates an allocation granularity of 64K. To examine 1K of data that is 138,240 bytes (135K) into the file, do the following:
例如,假设 GetSystemInfo 函数指示分配粒度为 64K。若要将 1K 的数据(即 138,240 字节 (135K))签入文件,请执行以下操作:
1、Create a file mapping object of at least 139,264 bytes (136K) in size.
创建大小至少为 139,264 字节 (136K) 的文件映射对象。
2、Create a file view that starts at a file offset that is the largest multiple of the file allocation granularity less than the offset you require. In this case, the file view starts at offset 131,072 (128K) into the file. The view is 139264 bytes (136K) minus 131,072 bytes (128K), or 8,192 bytes (8K), in size.
创建一个文件视图,该视图从文件偏移量开始,该偏移量是文件分配粒度的最大倍数,小于所需的偏移量。在这种情况下,文件视图从文件的偏移量 131,072 (128K) 处开始。视图的大小为 139264 字节 (136K) 减去 131,072 字节 (128K) 或 8,192 字节 (8K)。
3、Create a pointer offset 7K into the view to access the 1K in which you are interested.
在视图中创建一个 7K 的指针偏移量,以访问您感兴趣的 1K。
If the data you want straddles a file allocation granularity boundary, you could make the view larger than the file allocation granularity. This avoids breaking the data into pieces.
如果所需的数据跨越文件分配粒度边界,则可以使视图大于文件分配粒度。这样可以避免将数据分解成碎片。
The following program illustrates the second example above.
下面的程序演示了上面的第二个示例。
/* This program demonstrates file mapping, especially how to align a view with the system file allocation granularity. */ #include <windows.h> #include <stdio.h> #include <tchar.h> #define BUFFSIZE 1024 // size of the memory to examine at any one time #define FILE_MAP_START 138240 // starting point within the file of // the data to examine (135K) /* The test file. The code below creates the file and populates it, so there is no need to supply it in advance. */ TCHAR * lpcTheFile = TEXT("fmtest.txt"); // the file to be manipulated int main(void) { HANDLE hMapFile; // handle for the file's memory-mapped region HANDLE hFile; // the file handle BOOL bFlag; // a result holder DWORD dBytesWritten; // number of bytes written DWORD dwFileSize; // temporary storage for file sizes DWORD dwFileMapSize; // size of the file mapping DWORD dwMapViewSize; // the size of the view DWORD dwFileMapStart; // where to start the file map view DWORD dwSysGran; // system allocation granularity SYSTEM_INFO SysInfo; // system information; used to get granularity LPVOID lpMapAddress; // pointer to the base address of the // memory-mapped region char * pData; // pointer to the data int i; // loop counter int iData; // on success contains the first int of data int iViewDelta; // the offset into the view where the data //shows up // Create the test file. Open it "Create Always" to overwrite any // existing file. The data is re-created below hFile = CreateFile(lpcTheFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { _tprintf(TEXT("hFile is NULL\n")); _tprintf(TEXT("Target file is %s\n"), lpcTheFile); return 4; } // Get the system allocation granularity. GetSystemInfo(&SysInfo); dwSysGran = SysInfo.dwAllocationGranularity; // Now calculate a few variables. Calculate the file offsets as // 64-bit values, and then get the low-order 32 bits for the // function calls. // To calculate where to start the file mapping, round down the // offset of the data into the file to the nearest multiple of the // system allocation granularity. dwFileMapStart = (FILE_MAP_START / dwSysGran) * dwSysGran; _tprintf (TEXT("The file map view starts at %ld bytes into the file.\n"), dwFileMapStart); // Calculate the size of the file mapping view. dwMapViewSize = (FILE_MAP_START % dwSysGran) + BUFFSIZE; _tprintf (TEXT("The file map view is %ld bytes large.\n"), dwMapViewSize); // How large will the file mapping object be? dwFileMapSize = FILE_MAP_START + BUFFSIZE; _tprintf (TEXT("The file mapping object is %ld bytes large.\n"), dwFileMapSize); // The data of interest isn't at the beginning of the // view, so determine how far into the view to set the pointer. iViewDelta = FILE_MAP_START - dwFileMapStart; _tprintf (TEXT("The data is %d bytes into the view.\n"), iViewDelta); // Now write a file with data suitable for experimentation. This // provides unique int (4-byte) offsets in the file for easy visual // inspection. Note that this code does not check for storage // medium overflow or other errors, which production code should // do. Because an int is 4 bytes, the value at the pointer to the // data should be one quarter of the desired offset into the file for (i=0; i<(int)dwSysGran; i++) { WriteFile (hFile, &i, sizeof (i), &dBytesWritten, NULL); } // Verify that the correct file size was written. dwFileSize = GetFileSize(hFile, NULL); _tprintf(TEXT("hFile size: %10d\n"), dwFileSize); // Create a file mapping object for the file // Note that it is a good idea to ensure the file size is not zero hMapFile = CreateFileMapping( hFile, // current file handle NULL, // default security PAGE_READWRITE, // read/write permission 0, // size of mapping object, high dwFileMapSize, // size of mapping object, low NULL); // name of mapping object if (hMapFile == NULL) { _tprintf(TEXT("hMapFile is NULL: last error: %d\n"), GetLastError() ); return (2); } // Map the view and test the results. lpMapAddress = MapViewOfFile(hMapFile, // handle to // mapping object FILE_MAP_ALL_ACCESS, // read/write 0, // high-order 32 // bits of file // offset dwFileMapStart, // low-order 32 // bits of file // offset dwMapViewSize); // number of bytes // to map if (lpMapAddress == NULL) { _tprintf(TEXT("lpMapAddress is NULL: last error: %d\n"), GetLastError()); return 3; } // Calculate the pointer to the data. pData = (char *) lpMapAddress + iViewDelta; // Extract the data, an int. Cast the pointer pData from a "pointer // to char" to a "pointer to int" to get the whole thing iData = *(int *)pData; _tprintf (TEXT("The value at the pointer is %d,\nwhich %s one quarter of the desired file offset.\n"), iData, iData*4 == FILE_MAP_START ? TEXT("is") : TEXT("is not")); // Close the file mapping object and the open file bFlag = UnmapViewOfFile(lpMapAddress); bFlag = CloseHandle(hMapFile); // close the file mapping object if(!bFlag) { _tprintf(TEXT("\nError %ld occurred closing the mapping object!"), GetLastError()); } bFlag = CloseHandle(hFile); // close the file itself if(!bFlag) { _tprintf(TEXT("\nError %ld occurred closing the file!"), GetLastError()); } return 0; }