//1. (A):与虚拟内存相似,内存映射文件允许开发人员预订一块地址空间区域并给区域调拨物理存储器。不同之处在于,内存映射文件的物理存储器来自磁盘上已有的文件,而不是来自系统的页交换文件。 一旦把文件映射到地址空间,我们就可对它进行访问,就像整个文件都已被载入内存一样 (B):内存映射文件主要用于以下三种情况: (1):系统使用内存映射文件来载入并运行.exe和DLL文件。节省了页交换文件的空间及应用程序启动的时间。 (2):开发人员可用内存映射文件来访问磁盘上的数据文件。可避免直接对文件进行I/O操作和对文件内容进行缓存。 (3):通过使用内存映射文件,我们可以在一台机器的不同进程之间共享数据,此方法是在同一台机器不同进程间共享数据的最高效方法 //2. (A):当一个线程调用了 CreateProcess 的时候,系统会执行以下步骤: (1):系统会确定 CreateProcess 所指定exe所在的位置,若无法找到该exe文件,系统不会创建进程, CreateProcess 返回 FALSE (2):系统创建一个新的进程内核对象。 (3):系统为新进程创建一个私有地址空间 (4):系统预定一块足够大的地址空间来容纳exe文件。待预定的地址空间区域的具体位置已经在exe文件中指定。默认情况下exe文件的基地址是0x00400000(在64位windows下的64位程序来说可能会不同), 但是若在链接时使用 /BASE 链接器开关,就可以自己给应用程序指定不同的地址。 (5):系统会对地址空间区域进行标注,表明该区域的后备物理存储器来自磁盘上的exe文件,而并非来自系统的页交换文件。 (B):系统将exe映射到进程地址空间以后会访问exe文件中的一个段,这个段列出了一些DLL文件,包含了exe文件调用到的函数。然后系统会调用 LoadLibrary 来加载入每一个DLL,如果DLL还需要调用到其他DLL,系统同样会使用 LoadLibrary 来载入, 此过程执行的操作如下: (1):系统会预定一块足够大的地址空间区域来容纳DLL文件。待预定地址空间区域的具体位置在DLL文件中已经指定。默认情况下, Microsoft 会将 x86 平台的DLL的基地址设为 0x10000000 ,将 x64 平台的DLL基地址是0x00400000 也可以使用 /BASE 链接器开关来设定。与系统一起发布的DLL都具有不同的基地址,这样即使把他们都加在到同一个进程的地址空间,也不会发生重叠。 (2):如果系统无法在DLL文件中指定的基地址处预定区域,可能是因为该区域已经被别的DLL或exe占用,也可能是区域不够大,系统会尝试在另一个地址来为DLL预定地址区域。 (3):系统会对地址空间区域进行标注,表明该区域的后备物理存储器来自磁盘上的DLL文件,而非来自系统的页交换文件。如果Windows不能将DLL载入到指定的基地址而必须重定位的话,系统还会另外进行标注,表明DLL中有一部分存储器被映射到了页交换文件。 (C):如果因为某些原因无法将exe文件和所需DLL映射到地址空间区域,系统会先给用户显示一个对话框,然后释放进程地址空间和进程内存对象。 CreateProcess 返回 FALSE (D):所有exe文件和DLL文件都映射到进程地址空间以后,系统会开始执行exe文件的启动代码。当完成对exe文件的映射以后,系统会负责所有换页,缓存以及高速缓存操作。 //3. (A):如果一个应用程序已经在运行,那么当我们为这个应用程序创建一个新的进程时,系统只不过是打开另一个内存映射视图,创建一个新的进程内核对象,并(主线程)创建一个新的线程对象。 通过内存映射文件,同一个应用程序的多个实例可以共享内存中的代码和数据。 (B):文件的内容被分为段。代码在一个段中,全局变量在另一个段中。段会对齐到页面大小的整数倍。在exe或DLL文件中,代码段通常在数据段前面 (C):当应用程序的一个实例修改了数据页面的一些全局变量后,系统会使用写时复制,致使应用程序的其他实例不受影响 在调试一个应用程序的时候,我们用调试器设置一个断点,这时调试器会修改我们的代码,此时也会触发写时复制的特性,致使应用程序的其他实例不受影响 //4. (A):默认情况下,同一个exe或DLL的多个实例不会共享全局或静态数据,当其中一个实例更改了全局变量或静态数据时,会触发写时复制特性。 (B):段属性: (1):READ:可以从该段读取数据 (2):WRITE:可以向该段写入数据 (3):EXECUTE:可以执行该段的内容 (4):SHARED:该段的内容为多个实例所共享(这个属性实际上关闭了写时复制的机制) (C):dumpBin工具 指定 /headers 来查看exe或DLL文件的各个段 实例: #include <string> using std::string; #pragma data_seg("SZN") //创建自己的段 int nValue = 1024; string str = "123"; int nTem; //编译器只会将已初始化的变量保存在这个段中,未初始化的变量不会放入指定段中 #pragma data_seg() //停止把已初始化的变量放入指定的段,转而将其放入默认数据段 #pragma comment(linker, "/SECTION:SZN,RWS") //设定段的属性,R:read W:write S:share,并将链接器开关嵌入源代码中 __declspec(allocate("SZN")) int nAddValue; //此句代码允许我们将未初始化代码放入指定段,指定的段必须存在 int main() { printf("nValue = %d, str = %s, nTem = %d, nAddValue = %d ", nValue, str.c_str(), nTem, nAddValue); ++nValue; str += "0"; ++nTem; ++nAddValue; system("pause"); return 0; } //第一次运行输出:nValue = 1024, str = 123, nTem = 0, nAddValue = 0 //第二次运行输出:nValue = 1025, str = 123, nTem = 0, nAddValue = 1 上述代码生成的可执行 用dumpBin查看: C:Usersszn>"C:Program Files (x86)Microsoft Visual Studio 10.0VCinamd64d umpbin.exe" /headers C:UserssznDesktopDelDelDebugDel.exe Microsoft (R) COFF/PE Dumper Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file C:UserssznDesktopDelDelDebugDel.exe PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (x86) 8 number of sections 5A9FF1CF time date stamp Wed Mar 07 22:06:07 2018 0 file pointer to symbol table 0 number of symbols E0 size of optional header 102 characteristics Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # (PE32) 10.00 linker version 8A00 size of code 4600 size of initialized data 0 size of uninitialized data 111EA entry point (004111EA) @ILT+485(_mainCRTStartup) 1000 base of code 1000 base of data 400000 image base (00400000 to 00421FFF) 1000 section alignment 200 file alignment 5.01 operating system version 0.00 image version 5.01 subsystem version 0 Win32 version 22000 size of image 400 size of headers 0 checksum 3 subsystem (Windows CUI) 8140 DLL characteristics Dynamic base NX compatible Terminal Server Aware 100000 size of stack reserve 1000 size of stack commit 100000 size of heap reserve 1000 size of heap commit 0 loader flags 10 number of directories 0 [ 0] RVA [size] of Export Directory 1E000 [ 50] RVA [size] of Import Directory 20000 [ 459] RVA [size] of Resource Directory 0 [ 0] RVA [size] of Exception Directory 0 [ 0] RVA [size] of Certificates Directory 21000 [ 430] RVA [size] of Base Relocation Directory 1A820 [ 1C] RVA [size] of Debug Directory 0 [ 0] RVA [size] of Architecture Directory 0 [ 0] RVA [size] of Global Pointer Directory 0 [ 0] RVA [size] of Thread Storage Directory 0 [ 0] RVA [size] of Load Configuration Directory 0 [ 0] RVA [size] of Bound Import Directory 1E234 [ 1E4] RVA [size] of Import Address Table Directory 0 [ 0] RVA [size] of Delay Import Directory 0 [ 0] RVA [size] of COM Descriptor Directory 0 [ 0] RVA [size] of Reserved Directory SECTION HEADER #1 .textbss name 10000 virtual size 1000 virtual address (00401000 to 00410FFF) 0 size of raw data 0 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers E00000A0 flags Code Uninitialized Data Execute Read Write SECTION HEADER #2 .text name 89FB virtual size 11000 virtual address (00411000 to 004199FA) 8A00 size of raw data 400 file pointer to raw data (00000400 to 00008DFF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 60000020 flags Code Execute Read SECTION HEADER #3 .rdata name 24BC virtual size 1A000 virtual address (0041A000 to 0041C4BB) 2600 size of raw data 8E00 file pointer to raw data (00008E00 to 0000B3FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data Read Only Debug Directories Time Type Size RVA Pointer -------- ------ -------- -------- -------- 5A9FF1CF cv 43 0001B73C A53C Format: RSDS, {1D76314A-ED9E-4 885-83FC-A19116E49448}, 21, C:UserssznDesktopDelDelDebugDel.pdb SECTION HEADER #4 .data name 5EC virtual size 1D000 virtual address (0041D000 to 0041D5EB) 200 size of raw data B400 file pointer to raw data (0000B400 to 0000B5FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0000040 flags Initialized Data Read Write SECTION HEADER #5 .idata name B8C virtual size 1E000 virtual address (0041E000 to 0041EB8B) C00 size of raw data B600 file pointer to raw data (0000B600 to 0000C1FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0000040 flags Initialized Data Read Write SECTION HEADER #6 SZN name //自定义的段 130 virtual size 1F000 virtual address (0041F000 to 0041F12F) 200 size of raw data C200 file pointer to raw data (0000C200 to 0000C3FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers D0000040 flags Initialized Data Shared Read Write SECTION HEADER #7 .rsrc name 459 virtual size 20000 virtual address (00420000 to 00420458) 600 size of raw data C400 file pointer to raw data (0000C400 to 0000C9FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data Read Only SECTION HEADER #8 .reloc name 5E8 virtual size 21000 virtual address (00421000 to 004215E7) 600 size of raw data CA00 file pointer to raw data (0000CA00 to 0000CFFF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 42000040 flags Initialized Data Discardable Read Only Summary 1000 .data 1000 .idata 3000 .rdata 1000 .reloc 1000 .rsrc 9000 .text 10000 .textbss 1000 SZN //5.使用内存映射文件 (A): HANDLE WINAPI CreateFileMapping ( __in HANDLE hFile, __in_opt LPSECURITY_ATTRIBUTES lpFileMappingAttributes, __in DWORD flProtect, //保护属性 PAGE_* ,必须与 CreateFile 中指定的权限相兼容 __in DWORD dwMaximumSizeHigh, __in DWORD dwMaximumSizeLow, __in_opt LPCWSTR lpName ); Creates or opens a named or unnamed file mapping object for a specified file. If the function succeeds, the return value is a handle to the newly created file mapping object. If the object exists before the function call, the function returns a handle to the existing object (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS. If the function fails, the return value is NULL. hFile [in] A handle to the file from which to create a file mapping object. The file must be opened with access rights that are compatible with the protection flags that the flProtect parameter specifies. It is not required, but it is recommended that files you intend to map be opened for exclusive access If hFile is INVALID_HANDLE_VALUE, the calling process must also specify a size for the file mapping object in the dwMaximumSizeHigh and dwMaximumSizeLow parameters. In this scenario, CreateFileMapping creates a file mapping object of a specified size that is backed by the system paging file instead of by a file in the file system. dwMaximumSizeHigh [in] The high-order DWORD of themaximum size of the file mapping object. dwMaximumSizeLow [in] The low-order DWORD of the maximum size of the file mapping object. If this parameter and dwMaximumSizeHigh are 0 (zero), the maximum size of the file mapping object is equal to the current size of the file that hFile identifies. An attempt to map a file with a length of 0 (zero) fails with an error code of ERROR_FILE_INVALID. Applications should test for files with a length of 0 (zero) and reject those files. (B): LPVOID WINAPI MapViewOfFile ( __in HANDLE hFileMappingObject, //A handle to a file mapping object. The CreateFileMapping and OpenFileMapping functions return this handle. __in DWORD dwDesiredAccess, //The type of access to a file mapping object, which determines the protection of the pages __in DWORD dwFileOffsetHigh, //A high-order DWORD of the file offset where the view begins. __in DWORD dwFileOffsetLow, __in SIZE_T dwNumberOfBytesToMap ); Maps a view of a file mapping into the address space of a calling process. dwDesiredAccess [in]: FILE_MAP_ALL_ACCESS: A read/write view of the file is mapped. The file mapping object must have been created with PAGE_READWRITE or PAGE_EXECUTE_READWRITE protection. When used with the MapViewOfFile function, FILE_MAP_ALL_ACCESS is equivalent to FILE_MAP_WRITE. FILE_MAP_COPY: A copy-on-write view of the file is mapped. The file mapping object must have been created with PAGE_READONLY, PAGE_READ_EXECUTE, PAGE_WRITECOPY, PAGE_EXECUTE_WRITECOPY, PAGE_READWRITE, or PAGE_EXECUTE_READWRITE protection. When a process writes to a copy-on-write page, the system copies the original page to a new page that is private to the process. The new page is backed by the paging file. The protection of the new page changes from copy-on-write to read/write. When copy-on-write access is specified, the system and process commit charge taken is for the entire view because the calling process can potentially write to every page in the view, making all pages private. The contents of the new page are never written back to the original file and are lost when the view is unmapped. FILE_MAP_READ: A read-only view of the file is mapped. An attempt to write to the file view results in an access violation. The file mapping object must have been created with PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ, or PAGE_EXECUTE_READWRITE protection. FILE_MAP_WRITE: A read/write view of the file is mapped. The file mapping object must have been created with PAGE_READWRITE or PAGE_EXECUTE_READWRITE protection. When used with MapViewOfFile, (FILE_MAP_WRITE | FILE_MAP_READ) and FILE_MAP_ALL_ACCESS are equivalent to FILE_MAP_WRITE. Each of the preceding values can be combined with the following value. FILE_MAP_EXECUTE: An executable view of the file is mapped (mapped memory can be run as code). The file mapping object must have been created with PAGE_EXECUTE_READ, PAGE_EXECUTE_WRITECOPY, or PAGE_EXECUTE_READWRITE protection. dwFileOffsetLow [in]: A low-order DWORD of the file offset where the view is to begin. The combination of the high and low offsets must specify an offset within the file mapping. They must also match the memory allocation granularity of the system. That is, the offset must be a multiple of the allocation granularity. To obtain the memory allocation granularity of the system, use the GetSystemInfo function, which fills in the members of a SYSTEM_INFO structure. dwNumberOfBytesToMap [in]: The number of bytes of a file mapping to map to the view. All bytes must be within the maximum size specified by CreateFileMapping. If this parameter is 0 (zero), the mapping extends from the specified offset to the end of the file mapping. Mapping a file makes the specified portion of a file visible in the address space of the calling process. For files that are larger than the address space, you can only map a small portion of the file data at one time. When the first view is complete, you can unmap it and map a new view. To obtain the size of a view, use the VirtualQuery function. Multiple views of a file (or a file mapping object and its mapped file) are coherent if they contain identical data at a specified time. This occurs if the file views are derived from any file mapping object that is backed by the same file. A process can duplicate a file mapping object handle into another process by using the DuplicateHandle function, or another process can open a file mapping object by name by using the OpenFileMapping function. With one important exception, file views derived from any file mapping object that is backed by the same file are coherent or identical at a specific time. Coherency is guaranteed for views within a process and for views that are mapped by different processes. The exception is related to remote files. Although MapViewOfFile works with remote files, it does not keep them coherent. For example, if two computers both map a file as writable, and both change the same page, each computer only sees its own writes to the page. When the data gets updated on the disk, it is not merged. A mapped view of a file is not guaranteed to be coherent with a file that is being accessed by the ReadFile or WriteFile function. MapViewOfFileEx: Maps a view of a file mapping into the address space of a calling process. A caller can optionally specify a suggested base memory address for the view (C): BOOL WINAPI FlushViewOfFile ( __in LPCVOID lpBaseAddress, __in SIZE_T dwNumberOfBytesToFlush //会向上取整为页面大小的整数倍 ); Writes to the disk a byte range within a mapped view of a file. lpBaseAddress [in] A pointer to the base address of the byte range to be flushed to the disk representation of the mapped file. dwNumberOfBytesToFlush [in] The number of bytes to be flushed. If dwNumberOfBytesToFlush is zero, the file is flushed from the base address to the end of the mapping. If the function succeeds, the return value is nonzero.If the function fails, the return value is zero. (D): BOOL WINAPI UnmapViewOfFile(__in LPCVOID lpBaseAddress); Unmaps a mapped view of a file from the calling process's address space. lpBaseAddress [in] A pointer to the base address of the mapped view of a file that is to be unmapped. This value must be identical to the value returned by a previous call to the MapViewOfFile or MapViewOfFileEx function. If the function succeeds, the return value is nonzero.If the function fails, the return value is zero. (E): #include <Windows.h> #include <assert.h> int main() { HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hFileMap = NULL; do { hFile = CreateFile(TEXT("Test.dat"), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (INVALID_HANDLE_VALUE == hFile) { break; } char buff[260] = {}; for (int i = 0; i < 65536; ++i) { memcpy(buff, &i, sizeof i); for (int j = 4; j < 260; ++j) { buff[j] = j - 4; } DWORD nTem = 0; if (!WriteFile(hFile, buff, sizeof buff, &nTem, nullptr) || nTem != sizeof buff) { break; } } hFileMap = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, 0, nullptr); if (!hFileMap) { break; } void* pContent = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); //即可用 pContent 直接访问文件,就像文件已经全被载入到内存中一样 if (!pContent) { break; } *static_cast<char*>(pContent) = 6; //文件被修改 if (!FlushViewOfFile(pContent, 4096)) { assert(false); } if (!UnmapViewOfFile(pContent)) { break; } } while (false); if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); } if (hFileMap) { CloseHandle(hFileMap); } } //6. (A):系统允许我们将同一个文件中的数据映射到多个视图中。只要我们映射的是同一个文件映射对象,那么系统会确保各视图的数据是一致的,如果多个进程把同一个数据文件映射到多个视图中, 数据也仍会是一致的,这是因为数据文件中的每个页面在内存中只存在一份,但是这些内存页面被映射到了多个地址空间中,仅此而已 (B):在使用内存映射文件的时候,最好对打开的文件进行独占,否则当文件在别处被修改时,使用了内存映射文件的程序是不会得到通知的 (C): //进程间共享数据 #include <Windows.h> int main() { HANDLE hFileMap = NULL; do { hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 4096, TEXT("HelloWorld")); //以页交换文件为后备存储器的内存映射文件 if (!hFileMap) { printf("Error "); break; } void* pContent = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (!pContent) { printf("Error "); break; } ++*static_cast<char*>(pContent); if (!FlushViewOfFile(pContent, 4096)) { printf("Error "); break; } printf("%d ", *static_cast<char*>(pContent)); if (!UnmapViewOfFile(pContent)) { printf("Error "); break; } } while (false); system("pause"); if (hFileMap) { CloseHandle(hFileMap); } } 打开上述代码生成的可执行文件,且不要进行关闭,则 第一个exe输出:1 第二个exe输出:2 第三个exe输出:3 第四个exe输出:4 然后关闭所有exe,再重新打开,第一个exe又会输出1 (D): //稀疏调拨的内存映射文件 #include <Windows.h> int main() { HANDLE hFileMap = NULL; do { hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE | SEC_RESERVE, 0, 4096 * 10, TEXT("HelloWorld")); //稀疏调拨的内存映射文件 //SEC_RESERVE参考资料:https://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx if (!hFileMap) { printf("Error "); break; } char* pContent = reinterpret_cast<char*>(MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0)); if (!pContent) { printf("Error "); break; } //pContent[0] = 1; //没有指定物理存储器,会运行出错 auto pTem = VirtualAlloc(pContent, 4096, MEM_COMMIT, PAGE_READWRITE); if (pContent != reinterpret_cast<char*>(pTem)) { printf("Error "); break; } pContent[0] = 1; printf("%d ", pContent[0]); //输出1 if (!UnmapViewOfFile(pContent)) { printf("Error "); break; } } while (false); if (hFileMap) { CloseHandle(hFileMap); } }