【发布时间】:2017-11-26 02:40:59
【问题描述】:
我正在尝试找出在 Windows 中写入文件的最佳方法。为此,我一直在运行一些内存映射测试,试图弄清楚发生了什么以及我应该如何组织事情......
场景:该文件旨在用于单个进程、多个线程中。您应该将线程视为处理文件存储的工作人员;其中一些会读取,一些会写入 - 在某些情况下文件会增长。我希望我的状态能够在进程和操作系统崩溃中幸存下来。文件可能很大,例如:1 TB。
在 MSDN 上阅读了很多内容后,我编写了一个小测试用例。我基本上做的是以下几点:
- 使用
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH打开一个文件(CreateFile)。 - 使用某些文件增长机制在文件上构建 mmap 文件句柄 (
CreateFileMapping)。 - 使用扇区大小的倍数(来自
STORAGE_PROPERTY_QUERY)映射内存区域(MapViewOfFile)。我打算使用的模式是 READ+WRITE。
到目前为止,我一直无法弄清楚如何准确地使用这些构造(diskmon 之类的工具有充分的理由不起作用)所以我决定在这里问。我基本上想知道的是:如何在我的场景中最好地使用这些构造?
如果我理解正确,这或多或少是正确的方法;但是,我不确定 CreateFileMapping 与 MapViewOfFile 的确切作用以及这是否可以在多个线程中工作(例如,写入刷新到磁盘时的排序方式)。
- 我打算按照 (1) 每个进程打开一次文件。
- 每个线程,我打算按照 (2) 为整个文件创建一个 mmap 文件句柄。如果我需要增加文件,我会估计我需要多少空间,关闭句柄并使用
CreateFileMapping重新打开它。 - 当工作人员在做它的事情时,它需要文件的片段。所以,我打算为每块使用
MapViewOfFile(似乎限制为 2 GB),处理它并再次取消映射。
问题:
- 我是否正确理解了这些概念?
- 何时将数据物理读取和写入磁盘?那么,当我有一个在 (3) 中写入 1 MB 数据的循环时,它会在 unmap 调用之后 写入该数据吗?或者它会在我在另一页中击中内存的那一刻写入数据? (毕竟,磁盘是块设备,所以在某些时候我们必须写一个块......)
- 这可以在多个线程中工作吗?这是关于电话本身的——我不确定如果你有 - 说 - 100 名工人,他们是否会出错。
- 我知道(写入的)数据可以立即在其他线程中使用(除非它是远程文件),这意味着我应该注意读/写并发性。如果我打算写东西,然后更新单个物理块)标头(表明读者应该从现在开始使用另一个指针) - 那么它保证数据被写入之前到标题?
- 我使用 1 个文件还是多个文件(当然,假设它们在同一个物理设备上)是否重要?
【问题讨论】:
-
当您需要 FILE_FLAG_WRITE_THROUGH 和“避免操作系统崩溃”时,使用 MMF 是完全不合适的。您无法准确控制 MMF 更改何时刷新到磁盘。不要这样做。
-
@HansPassant 有趣。我开始探索内存映射,因为我在一些论文中读到一些数据库将它用于磁盘 IO(并且它们根据 ACID 保证“持久性”)。我还在 PostgreSQL 错误报告 (postgresql.org/message-id/… ) 中读到他们停止使用
FlushFileBuffers是很糟糕的。那么,如果两者都出局了 - 您在此处提出的解决方案 做什么? -
使用 WriteFile 和 ReadFile 有什么问题?由于您已关闭缓冲,在我看来,它们提供了您正在寻找的保证,至少就操作系统而言。 (底层硬件可能会也可能不会尊重这些保证,如果你也想对电源故障保持稳健,但如果不这样做,你就无能为力了。)
-
(顺便说一句,我相信 MapViewOfFile 将适用于大于 2GB 的视图,当然前提是您为 64 位构建。在这种情况下没有实际意义,因为正如 Hans 所说,您不会得到你需要的保证。)
-
@HarryJohnston Nothing
WriteFile/ReadFile有问题;我只是想找出最好的方法来做到这一点。所以,让我直截了当地说:你们基本上告诉我的是我应该坚持使用它们,结合 NO_BUFFERING 和 WRITE_THROUGH。 (或者,不要使用这些标志并明确使用FlushFileBuffers- 任何表现最好的)。对吗?
标签: c++ windows multithreading operating-system mmap