MapViewOfFile是否重用了内存映射?

时间:2014-03-26 11:16:26

标签: winapi memory-mapped-files

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

换句话说,

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机器上运行,因此如果每个线程在虚拟内存中分配了自己的副本,那么我的内存就会耗尽。

4 个答案:

答案 0 :(得分:1)

MapViewOfFile在您的进程的地址空间中找到一个漏洞,该漏洞对于整个文件来说足够大。即使您两次传递相同的文件映射对象,我也不希望它返回相同的指针。对于不同的映射对象和不同的文件句柄,我肯定希望指针不同。

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

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

答案 1 :(得分:1)

  

指针1是否等于指针2?

如果MapViewOfFile为映射选择相同的地址,则指针可能相等。您无法使用MapViewOfFile来控制此问题,并且您可以使用MapViewOfFileEx(最后一个参数lpBaseAddress)对此进行控制。

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

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);

也就是说,指针等价不是你应该期望或依赖的东西。特别是如果您的映射很大,并且不会立即重新打开。

答案 2 :(得分:1)

将使用相同的物理内存,但两个指针可能不一样。在任何情况下,您都无法保证它们是相同的,即使它们在您进行测试时非常明确。读作:您不能依赖于这种情况的假设。

您正在两个不同的文件句柄上创建两个映射。它们总是指同一个文件(这就是为什么会使用相同的物理内存),但它们仍然是两个不同的映射,它们在任何方面都没有逻辑上的相互关联。

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

答案 3 :(得分:1)

这两行之间的

msdn has documented it

  

如上所述,您可以拥有相同的多个视图   内存映射文件,它们可以重叠。但是如何映射两个   相同内存映射文件的相同视图?学习完成后   取消映射文件的视图,你可以得出结论   在一个过程中不可能有两个相同的视图   因为他们的基地址是相同的,你将无法做到   区分它们。这不是真的。记住那个基地   MapViewOfFileMapViewOfFileEx返回的地址   function不是文件视图的基址。相反,它是   视图开始的进程中的基址。 所以映射两个   相同内存映射文件的相同视图将产生两个视图   具有不同的基址,但仍然具有相同的视图   内存映射文件的相同部分。

此外:

  

这个小练习的重点是要强调每一个观点   单个内存映射文件对象始终映射到唯一的范围   这个过程中的地址。 每个基地址都不同   视图。因此,映射视图的基址就是这样   需要取消映射视图。