Принудительная запись файла на диск
В настоящее время я реализую схему буферизации 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 );
}
(Это было только поверхностно проверено, но основная идея есть.)