Как сделать fsync на офстриме? - PullRequest
16 голосов
/ 24 марта 2009

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

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

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

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

Ответы [ 4 ]

6 голосов
/ 23 мая 2014

Если вы можете использовать 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's written to the OS ...
file.flush();

//  Tell the OS to sync it with the real device.
//
::fdatasync(file->handle());
5 голосов
/ 24 марта 2009

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

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

3 голосов
/ 10 февраля 2015

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

Вот один из таких ужасных способов взлома libstdc ++.

#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()));
}
2 голосов
/ 24 марта 2009

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

...