如何在 ofstream 上执行 fsync?

29

我希望确保 ofstream 已经写入了磁盘设备。有什么可移植的方法(在 POSIX 系统上可移植)可以实现这一点吗?

如果我单独以附加模式打开文件并获取文件描述符,然后使用该文件描述符调用 fsync,是否可以解决问题?像这样:

ofstream out(filename);
/* ...
   write content into out
   ...
*/
out.close();

int fd = open(filename, O_APPEND);
fsync(fd);
close(fd);
4个回答

13

如果你能使用Boost库,可以尝试使用基于file_descriptor_sink的流,例如:

#include <boost/filesystem.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <unistd.h>

boost::filesystem::path filePath("some-file");
boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file(filePath);

//  Write some stuff to file.

//  Ensure the buffer's written to the OS ...
file.flush();

//  Tell the OS to sync it with the real device.
//
::fdatasync(file->handle());

即使加了头文件,我也无法编译它。Boost自2014年以来有改变吗? - parsley72
1
@parsley72,我刚刚使用Boost 1.80.0重新测试了一遍,它仍然可以工作。 - mrtimdog

7
很不幸,查看标准后发现basic_filebuf或任何basic_[io]?fstream类模板都没有提供任何方法来提取底层操作系统文件描述符(就像fileno()对于C stdio I/O一样)。
也没有一个接受这样的文件描述符作为参数的open()方法或构造函数(这将允许您使用不同的机制打开文件并记录文件句柄)。
虽然有basic_ostream::flush(),但我怀疑它实际上并没有调用fsync()。我认为,就像stdio中的fflush()一样,它只确保用户空间运行时库缓冲区被刷新,这意味着操作系统仍然可能缓存数据。
因此,简而言之,似乎没有可移植的方法来做到这一点。:(
那怎么办呢?我的建议是子类化basic_filebuf<C, T>:
template <typename charT, typename traits = std::char_traits<charT> >
class my_basic_filebuf : public basic_filebuf<charT, traits> {
    ....

public:
    int fileno() { ... }
    ....
};

typedef my_basic_filebuf<char> my_filebuf;

要使用它,您可以使用默认构造函数构造一个ofstream,然后使用rdbuf()分配新缓冲区:

my_filebuf buf;
buf.open("somefile.txt");

ofstream ofs;
ofs.rdbuf(&buf);

ofs << "Writing to somefile.txt..." << endl;
int fd = static_cast<my_filebuf*>(ofs.rdbuf())->fileno();

当然,您也可以从basic_ostream派生一个新类,以使打开文件和检索其文件描述符的过程更加方便。

@BЈовић:(1)确实如此;我不确定你的问题在哪里。(2) 你一小时前评论了一篇2009年的文章,现在已经有1个赞了:这看起来一点也不像一个傀儡账户。 - j_random_hacker
我不确定我的评论为什么消失了,但是你的回答是无用的。在最重要的部分有省略号。我的问题是你的“例子”不能编译。那又怎样呢?即使它是从2009年开始的?谷歌把我带到这个问答中,我期望能够找到一些有用的东西。但是没有。此外,他并没有要求以可移植的方式进行操作,而是使用posix。 - BЈовић
你的评论可能被删除了,因为它很粗鲁。 - j_random_hacker

5

std::filebuf 可能在其内部拥有一个文件描述符,但需要可怕的特定于实现的 hack 才能访问它。

下面是一个针对 libstdc++ 的特定于实现的可怕 hack。

#include <fstream>
#include <unistd.h>

int GetFileDescriptor(std::filebuf& filebuf)
{
  class my_filebuf : public std::filebuf
  {
  public:
    int handle() { return _M_file.fd(); }
  };

  return static_cast<my_filebuf&>(filebuf).handle();
}

int main()
{
  std::ofstream out("test");

  out << "Hello, world!";
  out.flush();

  fsync(GetFileDescriptor(*out.rdbuf()));
}

什么是 _M_file? - Amruth A
@AmruthA - std::filebuf 的 libstdc++ 实现中的一个受保护成员变量。 - Josh Kelley

2

如果文件描述符处于读模式,您无法可移植地执行fsync()。在Linux中,fsync()文档记录为如果描述符不在写模式下,则会生成EBADF。


好的,那如果我以追加模式打开它呢? - Vik
2
你没有回答问题。ofstream打开一个文件用于写入,而不是读取。 - BЈовић
此外,您可以在以读模式打开的目录上执行fsync()。实际上,这就是强制磁盘写入的方法。 - BЈовић

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接