内存映射文件可选写入可能吗?
当使用内存映射文件时,它似乎要么是只读的,要么是只写的。我的意思是你不能:
- 打开一个文件进行写入,然后决定不保存它
- 打开一个文件进行读取,然后决定保存它
我们的应用程序使用可写的内存映射文件来保存数据文件,但由于用户可能想退出而不保存更改,我们必须使用用户实际编辑的临时文件。当用户选择保存更改时,原始文件将被临时文件覆盖,因此它具有最新的更改。这很麻烦,因为文件可能非常大(>1GB)并且复制它们需要很长时间。
我尝试了许多用于创建文件映射的标志组合,但似乎都不允许灵活地按需保存。任何人都可以确认情况是这样吗?我们的应用程序是用 Delphi 编写的,但在我们的例子中,它使用标准 Windows API 来创建映射
FMapHandle := CreateFileMapping(FFileHandle, nil, PAGE_READWRITE, 0, 2 * 65536, nil);
FBasePointer := MapViewOfFile(FileMapHandle, FILE_MAP_WRITE, FileOffsetHigh,
FileOffsetLow, NumBytes);
When using memory-mapped files it seems it is either read-only, or write-only. By this I mean you can't:
- have one open for writing, and later decide not to save it
- have open open for reading, and later decide to save it
Our application uses a writable memory-mapped file to save data files, but since the user might want to exit without saving changes, we have to use a temporary file which the user actually edits. When the user opts to save the changes, the original file is overwritten with the temporary file so it has the latest changes. This is cumbersome because the files can be very large (>1GB) and it takes a long time to copy them.
I've tried many combinations of the flags used to create the file mapping but none seem to allow the flexibility of saving on demand. Can anyone confirm this is the case? Our application is written in Delphi, but it uses the standard Windows API to create the mapping, in our case
FMapHandle := CreateFileMapping(FFileHandle, nil, PAGE_READWRITE, 0, 2 * 65536, nil);
FBasePointer := MapViewOfFile(FileMapHandle, FILE_MAP_WRITE, FileOffsetHigh,
FileOffsetLow, NumBytes);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为你不能。我的意思是你也许能够,但这对我来说没有任何意义:-)
内存映射文件的全部意义在于它是实际文件的一个窗口。如果您不希望更改反映在文件中,则可能需要执行一些操作,例如批量处理数据结构(例如,基地址、大小和数据的数组)中的更改,并在保存时应用它们。
在这种情况下,您实际上不需要内存映射文件,只需读入并维护您想要更改的块(如果有可能进行多用户访问,请先锁定文件)。
更新:
您是否想过在保存时删除原始文件并将临时文件重命名为原始文件名的可能性?这可能比将 1G 数据从临时数据复制到原始数据要快得多。这样,如果您不想保存它,只需删除临时文件并保留原始文件即可。
加载时您仍然需要将原始数据复制到临时文件,但不必将临时数据复制回来(无论是否保存) - 这会将所花费的时间减半。
I don't think you can. By that I mean you may be able to, but it doesn't make any sense to me :-)
The whole point of a memory-mapped file is that it's a window onto the actual file. If you don't wany changes reflected in the file, you'll probably have to do something like batch up the changes in a data structure (e.g., an array of base address, size and data) and apply them when saving.
In which case, you wouldn't actually need the memory mapped file, just read in and maintain the chunks you want to change (lock the file first if there's a chance of multi-user access).
Update:
Have you thought of the possibility of, when doing a save, deleting the original file and just renaming the temporary file to the original file name? That's likely to be much faster than copying 1G of data from temporary to original. That way, if you don't want it saved, just delete the temporary file and keep the original.
You'll still have to copy the original data to the temporary file when loading but you won't have to copy the temporary data back (whether you save it or not) - that would halve the time taken.
有可能,但并非微不足道。
您必须了解内存映射基础知识,以及内存映射文件的三种模式之间的区别。两者都留出一部分虚拟地址空间并在内部表中创建映射条目。最初没有分配物理 RAM。因此,当您尝试访问内存时,CPU 会出现故障,操作系统必须进行修复。它通过将文件内容复制到 RAM 并将 RAM 映射到进程的错误地址来实现这一点。
现在,三种模式之间的区别在于如何在映射页面上设置描述符。在所有情况下,您都可以获得页面的读取权限。 (第一种模式)。但是,如果您请求写访问并随后对其进行写入,则在第一次写入时,该页面将被标记为可写且脏。然后可以根据操作系统的判断将其写回原始文件(第二种模式)。最后,可以获得写时复制语义。一开始您仍然只能对内存中的页面进行读访问。当你写入它时,CPU仍然出现故障,操作系统需要修复它。使用写时复制,可以通过将更改页面的后备存储设置为页面文件而不是原始映射文件来完成修复。
因此,在您的情况下,您想使用写时复制模式。如果用户决定放弃修改,没有问题。您只需丢弃内存映射即可。所有在内存中修改并由页面文件支持的页面也将被丢弃。
如果用户确实决定保存,那么您的任务会稍微困难一些。您现在需要找出文件的哪些部分已更改。这些更改位于内存中,您需要将它们重新应用到源文件。您可以使用页面防护来执行此操作。因此,当用户决定保存时,将所有修改的页面复制到单独的内存块,重新映射(未更改的)文件以进行写入,然后应用更改。
Possible, but non-trivial.
You have to understand memory mapped basics, and the difference between the three modes of memory-mapped files. Both set aside a part of your virtual address space and create a mapping entry in an internal table. No physical RAM is initially allocated. Hence, when you DO try to access the memory, the CPU faults and the OS has to fix up. It does so by copying the file contents to RAM and mapping the RAM to your process, at the faulting address.
Now, the difference between the three modes is how the descriptors are set on the mapped pages. In all cases you get read access on the pages. (The first mode). However, if you ask for write access and subsequently write to it, on your first write the page is marked as writeable and dirty. It can then be written back to the original file, at the discretion of the OS (Second mode). Finally, it's possible to get copy-on-write semantics. You still start out with only read access to the page in memory. When you write to it, the CPU still faults and the OS needs to fix it up. With copy-on-write, that fixup is done by setting the backing store of the changed page to the page file, instead of the original mapped file.
So, in your case you want to use copy-on-write mode. If the user decides to discard the modifications, no problem. You simply discard the memory mapping. All pages that were modified in memory, and were backed by the page file are also discarded.
If the user does decide to save, you've got a slightly harder task. You now need to figure out which parts of the file have changed. Those changes are in memory, and you need to reapply those to the source file. You can do this with Page Guards. So, when the user decides to save, copy all modified pages to a separate memory block, remap the (unchanged) file for write, and apply the changes.