Winapi MapViewOfFile内存映射是否重用?

Winapi MapViewOfFile内存映射是否重用?,winapi,memory-mapped-files,Winapi,Memory Mapped Files,如果我在同一进程中创建同一文件的两个单独映射,指针会被共享吗 换言之: LPCTSTR filename = //... HANDLE file1 = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0); HANDLE fileMapping1 = CreateFileMapping(file1, NULL, PAGE_READONLY, 0, 0, 0); void* pointer1 = M

如果我在同一进程中创建同一文件的两个单独映射,指针会被共享吗

换言之:

LPCTSTR filename = //...

HANDLE file1 = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0);
HANDLE fileMapping1 = CreateFileMapping(file1, NULL, PAGE_READONLY, 0, 0, 0);
void* pointer1 = MapViewOfFile(fileMapping1, FILE_MAP_READ, 0, 0, 0);

CloseHandle(fileMapping1);
CloseHandle(file1);

HANDLE file2 = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0);
HANDLE fileMapping2 = CreateFileMapping(file2, NULL, PAGE_READONLY, 0, 0, 0);
void* pointer2 = MapViewOfFile(fileMapping2, FILE_MAP_READ, 0, 0, 0);

CloseHandle(fileMapping2);
CloseHandle(file2);
pointer1
是否会等于
pointer2


我问这个问题的原因是我有几个线程需要在一个大的(300+MB)文件中搜索,我想使用内存映射。但是,进程需要能够在旧的32位xp机器上运行,因此,如果每个线程在虚拟内存中分配了自己的副本,那么我可能会耗尽内存。

MapViewOfFile
在进程的地址空间中发现一个足以容纳整个文件的漏洞。即使两次传递相同的文件映射对象,我也不希望它返回相同的指针。对于不同的映射对象和不同的文件句柄,我肯定希望指针是不同的

在幕后,Windows应该使用相同的“section”对象,因此两个虚拟地址空间范围应该映射到相同的物理内存。这与映射同一文件的两个进程相同

要从两个线程使用相同的内存范围,一个线程必须映射文件并将指针存储在共享位置。另一个线程必须从共享位置检索该指针。您可能需要引用计数来决定何时取消映射文件(通过调用
UnmapViewOfFile
-关闭文件映射句柄不会释放该地址空间)

指针1会永远等于指针2吗

如果
MapViewOfFile
为映射选择相同的地址,指针可能相等。您不能使用
MapViewOfFile
来控制它,您可以使用(最后一个参数
lpBaseAddress
来控制它)

每个单独的
MapViewOfFile
都可以在相同的物理数据上创建新的映射,因此即使同时打开两个映射,操作系统也不需要将文件映射映射到相同的地址,从而保持数据的一致性。通过稍微修改代码,很容易看出这一点:

HANDLE file1 = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
HANDLE fileMapping1 = CreateFileMapping(file1, NULL, PAGE_READWRITE, 0, 0, 0);
void* pointer1 = MapViewOfFile(fileMapping1, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);

//CloseHandle(fileMapping1);
//CloseHandle(file1);

HANDLE file2 = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
HANDLE fileMapping2 = CreateFileMapping(file2, NULL, PAGE_READWRITE, 0, 0, 0);
void* pointer2 = MapViewOfFile(fileMapping2, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);

INT& n1 = *((INT*) pointer1);
INT& n2 = *((INT*) pointer2);

ATLASSERT(&n1 != &n2);  // The pointers are not equal even though they point 
            // the same data!
INT n3 = 0;
n1 = 2;
n3 += n2;
n1 = 3;
n3 += n2;
ATLASSERT(n3 == 5); // That's 2+3 we wrote through n1 and read through n2

//CloseHandle(fileMapping2);
//CloseHandle(file2);

也就是说,指针等价性不是您应该期望或依赖的东西。特别是如果映射很大,并且不会立即重新打开。将使用相同的物理内存,但两个指针可能不相同。在任何情况下,您都不能保证它们是相同的,即使在您测试时它们是相同的。读作:你永远不能依赖这样的假设

您正在两个不同的文件句柄上创建两个映射。顺便说一句,它们引用同一个文件(这就是为什么要使用相同的物理内存),但它们仍然是两个不同的映射,在逻辑上彼此之间没有任何关系

是的,在两个不同的地址拥有相同的物理内存听起来可能不合逻辑,不合理(甚至不可能)。然而,这是一件完全合法的事情。

言外之意:

如上所述,您可以拥有同一视图的多个视图 内存映射文件,它们可以重叠。但是映射两个呢 相同内存映射文件的相同视图?在学会如何 取消映射文件的视图,您可能会得出这样的结论: 在一个进程中不可能有两个相同的视图 因为他们的基本地址是一样的,而你不能 区分它们。事实并非如此。记住,基地
MapViewOfFile
MapViewOffilex
函数不是文件视图的基址。相反,这是最重要的 进程中视图开始的基址So映射2 相同内存映射文件的相同视图将生成两个视图 具有不同的基地址,但对不同的 内存映射文件的相同部分。

此外:

这个小练习的目的是强调 单个内存映射文件对象始终映射到 进程中的地址每个端口的基址将不同 看法因此,映射视图的基址就是全部 需要取消映射视图。


由于同一文件支持的文件映射保证在进程内是一致的(远程文件除外),我不明白为什么
pointer1
不能指向与
pointer2
相同的内存块。事实上,两个指针相同是实现一致性保证的最直接的方法。但是,这两种方式都没有文档保证(关于指针的标识)。没有理由认为映射会导致相同的指针:分别映射/未映射,后端上有不同的句柄。我们期望的是,始终将两个不同的指针映射到同一物理内存。此外,在代码段中,您可能希望
file2
成为获取
fileMapping2
的参数,而不是
file1
(复制/粘贴)。下一行也是一样。@RomanR。是的,复制粘贴error@Roman特别指出“从同一文件支持的任何文件映射对象派生的文件视图是一致的”。文件映射对象和文件句柄都不需要共享。我也不理解你评论中的那部分:“你总是会得到两个不同的指针映射到同一个物理内存。”这也解决了在文件中必须取消映射两个具有相同startpos但视图大小不同的视图的问题。看起来指针肯定会因为这个原因而不同。