【问题标题】:How do I ensure data is written to disk before closing fstream?如何确保在关闭 fstream 之前将数据写入磁盘?
【发布时间】:2011-11-23 06:17:31
【问题描述】:

下面的内容看起来很合理,但我听说数据理论上仍然可以在缓冲区中而不是在磁盘上,即使在 close() 调用之后也是如此。

#include <fstream>

int main()
{
    ofstream fsi("test.txt");

    fsi << "Hello World";

    fsi.flush();

    fsi.close();

    return 0;
}

【问题讨论】:

  • 是的,但这是标准 C++ 库中未公开的操作系统细节。您可以不为此担心并相信操作系统会正确处理此问题,或者提供有关您使用的操作系统和编译器的非常具体的详细信息。

标签: c++ fstream


【解决方案1】:

您无法使用标准工具完成此操作,而必须依赖操作系统工具。 对于 POSIX fsync 应该是你需要的。由于无法从标准流中获取 C 文件描述符,因此您必须在整个应用程序中使用 C 流,或者只打开文件以刷新磁盘。或者有sync,但这会刷新所有缓冲区,您的用户和其他应用程序会讨厌这些缓冲区。

【讨论】:

    【解决方案2】:

    您可以通过刷新流来保证缓冲区中的数据写入磁盘。这可以通过调用其flush() 成员函数、flush 操纵器、endl 操纵器来完成。

    但是,在您的情况下没有必要这样做,因为close 保证将任何挂起的输出序列写入物理文件。

    § 27.9.1.4 / 6:

    basic_filebuf* close();

    效果:如果 is_open() == false,返回一个空指针。如果存在放置区域,则调用 overflow(traits::eof()) 来刷新字符。 (...)

    【讨论】:

    • 语言不能保证数据立即写入物理文件。大多数文件系统都有一个写缓存,它充当写操作的中间人。要绕过此缓存,需要使用非缓冲 I/O,即直接 I/O(例如,Windows 上的FILE_FLAG_NO_BUFFERING)。
    【解决方案3】:

    § 27.9.1.4
    basic_filebuf* close();
    效果:如果 is_open() == false,返回一个空指针。 如果放置区域 存在,调用 overflow(traits::eof()) 来刷新字符。 如果最后一个 在 *this 上调用的虚拟成员函数(在下溢、上溢、 seekoff 和 seekpos) 溢出然后调用 a_codecvt.unshift (可能多次)确定终止序列,插入 这些字符和 再次调用 overflow(traits::eof())。 最后, 无论前面的调用是否失败或抛出 异常,函数关闭文件(好像通过调用 std::fclose(文件))。如果该函数进行了任何调用, 包括 std::fclose,失败,close 失败,返回一个空指针。 如果这些调用之一引发异常,则捕获该异常并 关闭文件后重新抛出。

    保证刷新文件。但是,请注意,OS 可能会将其缓存起来,而 OS 可能不会立即刷新它。

    【讨论】:

      【解决方案4】:

      您使用的是哪个操作系统?

      您需要使用直接(非缓冲)I/O 来保证将数据直接写入物理设备,而不会影响文件系统写入缓存。请注意,在物理写入之前,它仍然必须通过磁盘缓存。

      在 Windows 上,您可以在打开文件时使用 FILE_FLAG_WRITE_THROUGH 标志。

      【讨论】:

      • FILE_FLAG_WRITE_THROUGH 工作不可靠。例如。对于 SATA 设备,“通过”仅意味着“通过”HDD/SSD 的缓存。你应该改用FlushFileBuffers
      • @PaulGroke FlushFileBuffers they say 在 Win 7 中损坏。
      • @Eugene Ryabtsev:我知道那些 cmets。如果我正确理解该错误并不“需要” FlushFileBuffers 被调用。我只能希望 MS 已经修复了这个错误。如果不是这样,我真的会感到惊讶,因为我想假设这样的事情具有最高优先级。
      【解决方案5】:

      close() 成员函数关闭底层 OS 文件描述符。此时,该文件应该在磁盘上。

      【讨论】:

      • 我不想冒险 - 我需要检查可能发生的任何错误,然后警告应用程序的用户稍后尝试保存文件。
      • 由操作系统决定何时将数据实际写入磁盘,不是吗?
      • 没有 100% 的保证。操作系统可以物理刷新数据,下次你去读取它时,它已经被伽马射线或其他东西破坏了。操作系统说“它在磁盘上”是您可能获得或需要的尽可能多的保证。如果你真的很偏执,那就自己做 ECC。
      【解决方案6】:

      我很确定调用close() 的全部目的是刷新缓冲区。 This site 同意。尽管取决于您的文件系统和挂载设置,仅仅因为您已经“写入磁盘”并不意味着您的文件系统驱动程序和磁盘硬件实际上已经获取了数据并在物理金属片上产生了磁位.它可能仍在磁盘缓冲区中。

      【讨论】:

      【解决方案7】:

      关闭前flushing 怎么样?

      【讨论】:

      • 这正是close() 所做的:它在关闭前刷新。除非您建议在关闭前冲洗之前冲洗。那太傻了。
      • 是的,确实很傻。没有看到close(),假设这是一个更简单的问题。 :)
      猜你喜欢
      • 1970-01-01
      • 2014-03-09
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 1970-01-01
      • 2017-03-25
      相关资源
      最近更新 更多