【问题标题】:Memory mapped files optional write possible?内存映射文件可选写入可能吗?
【发布时间】:2010-12-10 09:38:45
【问题描述】:

当使用内存映射文件时,它看起来要么是只读的,要么是只写的。我的意思是你不能:

  • 有一个可以写,后来决定不保存
  • 已打开阅读,稍后决定保存

我们的应用程序使用一个可写的内存映射文件来保存数据文件,但由于用户可能想退出而不保存更改,我们必须使用用户实际编辑的临时文件。当用户选择保存更改时,原始文件将被临时文件覆盖,因此它具有最新的更改。这很麻烦,因为文件可能非常大 (>1GB),而且复制它们需要很长时间。

我尝试了许多用于创建文件映射的标志组合,但似乎没有一个允许灵活地按需保存。任何人都可以确认是这种情况吗?我们的应用程序是用 Delphi 编写的,但在我们的例子中它使用标准的 Windows API 来创建映射

FMapHandle := CreateFileMapping(FFileHandle, nil, PAGE_READWRITE, 0, 2 * 65536, nil);
FBasePointer := MapViewOfFile(FileMapHandle, FILE_MAP_WRITE, FileOffsetHigh,
FileOffsetLow, NumBytes);

【问题讨论】:

  • 这里的 memory-mapped 文件没有什么特别之处。如果你写一个文件——任何文件——那么你就真的写过了。保存文件正在写入。不想保存就别写了。
  • 这并不完全正确 - 您可以写入内存映射文件,而不会将更改反映在基础文件中,具体取决于创建它的标志。

标签: windows delphi winapi memory memory-mapped-files


【解决方案1】:

我不认为你可以。我的意思是你也许可以,但这对我来说没有任何意义:-)

内存映射文件的全部意义在于它是实际文件的窗口。如果您不希望在文件中反映任何更改,您可能必须执行一些操作,例如批量处理数据结构(例如,基地址、大小和数据的数组)中的更改并在保存时应用它们。

在这种情况下,您实际上不会需要内存映射文件,只需读入并维护您要更改的块(如果有可能多用户访问,请先锁定文件)。

更新:

您是否考虑过在保存时删除原始文件并将临时文件重命名为原始文件名的可能性?这可能比将 1G 数据从临时复制到原始数据要快得多。这样,如果您不想保存它,只需删除临时文件并保留原始文件即可。

您仍然需要在加载时将原始数据复制到临时文件中,但您不必将临时数据复制回来(无论是否保存) - 这样可以将时间减半。

【讨论】:

  • 我害怕那个!我想也许省略 FlushViewOfFile 会导致更改被拒绝。唉,不,反正它是写出来的。
  • 嗨,是的,PaxDiablo,这正是我们所做的。我应该在原帖中提到这一点。
【解决方案2】:

可能,但不重要。

您必须了解内存映射的基础知识,以及内存映射文件的三种模式之间的区别。两者都留出一部分虚拟地址空间并在内部表中创建映射条目。最初没有分配物理 RAM。因此,当您尝试访问内存时,CPU 故障并且操作系统必须修复。它通过将文件内容复制到 RAM 并将 RAM 映射到错误地址的进程来实现。

现在,三种模式之间的区别在于描述符在映射页面上的设置方式。在所有情况下,您都可以获得页面上的读取权限。 (第一种模式)。但是,如果您请求写入访问权限并随后对其进行写入,则在您第一次写入时,该页面被标记为可写和脏页。然后可以由操作系统自行决定将其写回原始文件(第二模式)。最后,可以获得写时复制语义。您仍然开始只对内存中的页面进行读取访问。当您写入它时,CPU 仍然出现故障,操作系统需要对其进行修复。使用写时复制,通过将更改页面的后备存储设置为页面文件而不是原始映射文件来完成修复。

因此,在您的情况下,您想使用写时复制模式。如果用户决定放弃修改,没问题。您只需丢弃内存映射。所有在内存中修改并由页面文件支持的页面也将被丢弃。

如果用户确实决定保存,那么您的任务会稍微困难一些。您现在需要确定文件的哪些部分已更改。这些更改在内存中,您需要将这些更改重新应用到源文件。您可以使用Page Guards 执行此操作。因此,当用户决定保存时,将所有修改的页面复制到单独的内存块,重新映射(未更改的)文件以进行写入,然后应用更改。

【讨论】:

  • 谢谢,我会调查它,尽管它明显的复杂性可能会抵消它的好处!
  • 实际上我不会称之为“明显复杂”。实际上,pageguard 的想法只是创建一个修改后的页面指针列表。 “复制所有修改的页面”和“应用更改”都是微不足道的操作:for each (srcPage in set) { CopyMemory(newPage.base, srcPage.base, Page::size); }
猜你喜欢
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 2016-01-08
  • 2012-11-26
  • 2018-05-18
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
相关资源
最近更新 更多