关于winapi:是否可以重用MapViewOfFile内存映射?

Are MapViewOfFile memory mappings reused?

如果我在同一进程中为同一文件创建2个单独的映射,是否将共享指针?

换句话说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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计算机上运行,??因此,如果每个线程在虚拟内存中分配了自己的副本,那么我可能会用完内存。


msdn已在两行之间记录了它:

As mentioned above, you can have multiple views of the same
memory-mapped file, and they can overlap. But what about mapping two
identical views of the same memory-mapped file? After learning how to
unmap a view of a file, you could come to the conclusion that it would
not be possible to have two identical views in a single process
because their base address would be the same, and you wouldn't be able
to distinguish between them. This is not true. Remember that the base
address returned by either the MapViewOfFile or the MapViewOfFileEx
function is not the base address of the file view. Rather, it is the
base address in your process where the view begins. So mapping two
identical views of the same memory-mapped file will produce two views
having different base addresses, but nonetheless identical views of
the same portion of the memory-mapped file.

也:

The point of this little exercise is to emphasize that every view of a
single memory-mapped file object is always mapped to a unique range of
addresses in the process. The base address will be different for each
view. For that reason the base address of a mapped view is all that is
required to unmap the view.


将使用相同的物理内存,但是两个指针可能会不同。无论如何,即使您在测试时偶然碰到它们,也不能保证它们会相同。读为:您永远不能依赖这种情况的假设。

您正在两个不同的文件句柄上创建两个映射。偶然地,它们引用相同的文件(这就是为什么要使用相同的物理内存的原因),但是它们仍然是两个不同的映射,它们在逻辑上不以任何方式相互关联。

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


Will pointer1 ever be equal to pointer2?

如果MapViewOfFile为映射选择相同的地址,则指针可能相等。您不能使用MapViewOfFile控制它,而您可以使用MapViewOfFileEx(在那里的最后一个参数lpBaseAddress)对此进行一些控制。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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在您进程的地址空间中发现一个漏洞,该漏洞对于整个文件来说足够大。即使您两次传递相同的文件映射对象,我也不希望它返回相同的指针。对于不同的映射对象和不同的文件句柄,我绝对希望指针有所不同。

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

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