Принудительная запись файла на диск

В настоящее время я реализую схему буферизации ping / pong для безопасной записи файла на диск. Я использую C++/Boost на машине Linux/CentOS. Теперь я столкнулся с проблемой принудительной записи файла на диск. Возможно ли это сделать независимо от всех политик кэширования файловой системы (ext3/ext4) / пользовательских правил SO / контроллера RAID / контроллера жесткого диска?

Лучше всего использовать обычную файловую систему fread()/fwrite(), C++ ostream или boost?

Я слышал, что простое удаление файла (fflush()) не гарантирует фактическую запись

3 ответа

Решение

fflush (для FILE*), std::flush (для IOStream) для принудительной отправки вашей программы в ОС.

POSIX имеет

  • sync(2), чтобы попросить запланировать запись своих буферов, но может вернуться до того, как запись будет завершена (Linux ожидает, что данные будут отправлены на аппаратное обеспечение, прежде чем вернуться).

  • fsync(2), который гарантированно ожидает отправки данных на оборудование, но нуждается в дескрипторе файла (вы можете получить его из FILE * с помощью fileno(3), я не знаю ни одного стандартного способа получить его из IOStream).

  • O_SYNC как флаг для открытия (2).

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

Вы можете использовать fsync() / fdatasync() для принудительного (Примечание 1) данных в хранилище. Для этого требуется файловый дескриптор, как указано, например, open(). На man-странице linux есть больше специфической информации о linux, особенно о различиях fsync и fdatasync.

Если вы не используете дескрипторы файлов напрямую, многие абстракции будут содержать внутренние буферы, находящиеся в вашем процессе.

Например, если вы используете ФАЙЛ *, сначала вы должны удалить данные из вашего приложения.

//... open and write data to a FILE *myfile
fflush(myfile);
fsync(fileno(myfile));
  • Примечание 1. Эти вызовы заставляют ОС гарантировать, что любые данные из любого кэша ОС будут записаны на диск, и диск подтвердит этот факт. Многие жесткие диски лгут ОС об этом и могут помещать данные в кэш-память на диске.

Не в стандартном C++. Вам придется использовать какой-то системный ввод-вывод, например open с O_SYNC флаг под Unix, а затем write,

Обратите внимание, что это частично подразумевается тем фактом, что ostream (и в С, FILE*) буферизируются. Если вы точно не знаете, когда что-то записывается на диск, тогда нет смысла настаивать на транзакционной целостности записи. (Было бы не сложно разработать streambuf который пишет только когда вы делаете явный сброс, однако.)

РЕДАКТИРОВАТЬ:

В качестве простого примера:

class SynchronizedStreambuf : public std::streambuf
{
    int myFd;
    std::vector<char> myBuffer;

protected:
    virtual int overflow( int ch );
    virtual int sync();

public:
    SynchronizedStreambuf( std::string const& filename );
    ~SynchronizedStreambuf();
};

int SynchronizedStreambuf::overflow( int ch )
{
    if ( myFd == -1 ) {
        return traits_type::eof();
    } else if ( ch == traits_type::eof() ) {
        return sync() == -1 ? traits_type::eof() : 0;
    } else {
        myBuffer.push_back( ch );
        size_t nextPos = myBuffer.size();
        myBuffer.resize( 1000 );
        setp( &myBuffer[0] + nextPos, &myBuffer[0] + myBuffer.size() );
        return ch;
    }
}

int SynchronizedStreambuf::sync()
{
    size_t toWrite = pptr() - &myBuffer[0];
    int result = (toWrite == 0 || write( myFd, &myBuffer[0], toWrite ) == toWrite ? 0 : -1);
    if ( result == -1 ) {
        close( myFd );
        setp( NULL, NULL );
        myFd = -1;
    } else {
        setp( &myBuffer[0], &myBuffer[0] + myBuffer.size() );
    }
    return result;
}

SynchronizedStreambuf::SynchronizedStreambuf( std::string const& filename )
    : myFd( open( filename.c_str(), O_WRONLY | O_CREAT | O_SYNC, 0664 ) )
{
}

SynchronizedStreambuf::~SynchronizedStreambuf()
{
    sync();
    close( myFd );
}

(Это было только поверхностно проверено, но основная идея есть.)

Другие вопросы по тегам