Как сделать fsync в потоке?

Я хочу убедиться, что поток был записан на устройство. Какой переносной способ (переносимый на POSIX-системах) делать это?

Решает ли проблема, если я open файл отдельно в режиме только для чтения, чтобы получить файловый дескриптор и вызвать fsync с ним? Вот так:

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

int fd = open(filename, O_APPEND);
fsync(fd);
close(fd);

Ответы

Ответ 1

К сожалению, при просмотре стандарта нет basic_filebuf или любого из шаблонов классов basic_[io]?fstream, чтобы вы могли извлечь базовый дескриптор файла ОС (таким образом, что fileno() делает для C stdio I/О).

Здесь нет метода или конструктора open(), который принимает такой дескриптор файла как параметр (который позволит вам открыть файл с использованием другого механизма и записать дескриптор файла).

Существует basic_ostream::flush(), однако я подозреваю, что это фактически не вызов fsync() - я ожидаю, что, как и fflush() в stdio, он только очищает буферы библиотеки времени выполнения пользовательского пространства, это означает, что ОС все еще может буферизировать данные.

Короче говоря, похоже, что это невозможно.: (

Что делать? Мое предложение заключается в подклассе 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, чтобы упростить процесс открытия файла и получить его файловый дескриптор.

Ответ 2

Если вы можете использовать Boost, попробуйте поток на основе file_descriptor_sink, например.:

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

//  Write some stuff to file.

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

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

Ответ 3

Вы не можете переносить fsync() в дескриптор файла, открытый для чтения, вообще. В Linux fsync() documented как генерирующий EBADF, если дескриптор не находится в режиме записи.

Ответ 4

std::filebuf, вероятно, есть файловый дескриптор где-то внутри него, но получение на нем требует ужасных хаков для реализации.

Вот один такой ужасный взлом для libstdС++.

#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()));
}