【问题标题】:Memory mapped IO concept details内存映射 IO 概念详解
【发布时间】:2017-11-26 02:40:59
【问题描述】:

我正在尝试找出在 Windows 中写入文件的最佳方法。为此,我一直在运行一些内存映射测试,试图弄清楚发生了什么以及我应该如何组织事情......

场景:该文件旨在用于单个进程、多个线程中。您应该将线程视为处理文件存储的工作人员;其中一些会读取,一些会写入 - 在某些情况下文件会增长。我希望我的状态能够在进程和操作系统崩溃中幸存下来。文件可能很大,例如:1 TB。

在 MSDN 上阅读了很多内容后,我编写了一个小测试用例。我基本上做的是以下几点:

  1. 使用FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH打开一个文件(CreateFile)。
  2. 使用某些文件增长机制在文件上构建 mmap 文件句柄 (CreateFileMapping)。
  3. 使用扇区大小的倍数(来自STORAGE_PROPERTY_QUERY)映射内存区域(MapViewOfFile)。我打算使用的模式是 READ+WRITE。

到目前为止,我一直无法弄清楚如何准确地使用这些构造(diskmon 之类的工具有充分的理由不起作用)所以我决定在这里问。我基本上想知道的是:如何在我的场景中最好地使用这些构造?

如果我理解正确,这或多或少是正确的方法;但是,我不确定 CreateFileMappingMapViewOfFile 的确切作用以及这是否可以在多个线程中工作(例如,写入刷新到磁盘时的排序方式)。

  1. 我打算按照 (1) 每个进程打开一次文件。
  2. 每个线程,我打算按照 (2) 为整个文件创建一个 mmap 文件句柄。如果我需要增加文件,我会估计我需要多少空间,关闭句柄并使用CreateFileMapping 重新打开它。
  3. 当工作人员在做它的事情时,它需要文件的片段。所以,我打算为每块使用MapViewOfFile(似乎限制为 2 GB),处理它并再次取消映射。

问题:

  1. 我是否正确理解了这些概念?
  2. 何时将数据物理读取和写入磁盘?那么,当我有一个在 (3) 中写入 1 MB 数据的循环时,它会在 unmap 调用之后 写入该数据吗?或者它会在我在另一页中击中内存的那一刻写入数据? (毕竟,磁盘是块设备,所以在某些时候我们必须写一个块......)
  3. 这可以在多个线程中工作吗?这是关于电话本身的——我不确定如果你有 - 说 - 100 名工人,他们是否会出错。
  4. 我知道(写入的)数据可以立即在其他线程中使用(除非它是远程文件),这意味着我应该注意读/写并发性。如果我打算写东西,然后更新单个物理块)标头(表明读者应该从现在开始使用另一个指针) - 那么它保证数据被写入之前到标题?
  5. 我使用 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


【解决方案1】:

内存映射文件通常最适合阅读;不写。您面临的问题是在进行映射之前您必须知道文件的大小。

你说:

在某些情况下文件会增长

这确实排除了内存映射文件。

当您在 Windoze 上创建内存映射文件时,您正在创建自己的页面文件并将一定范围的内存映射到该页面文件。这往往是读取二进制数据的最快方式,尤其是在文件连续的情况下。

对于写,内存映射文件是有问题的。

【讨论】:

    猜你喜欢
    • 2020-01-08
    • 2013-06-19
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 2012-09-12
    • 1970-01-01
    • 2012-04-02
    • 2011-08-31
    相关资源
    最近更新 更多