Forcer l’écriture d’un fichier sur le disque

Je suis en train d’implémenter un système de mise en mémoire tampon ping / pong pour écrire un fichier sur le disque en toute sécurité. J’utilise C ++ / Boost sur une machine Linux / CentOS. Maintenant, je suis confronté au problème pour forcer l’écriture réelle du fichier sur le disque. Est-il possible de le faire indépendamment de toutes les règles de mise en cache du système de fichiers (ext3 / ext4) / SO règles personnalisées / contrôleur RAID / contrôleur de disque dur?

Est-il préférable d’utiliser le système de fichiers plain fread () / fwrite (), ostream ou boost?

J’ai entendu que le simple fait de vider le fichier (fflush ()) ne garantit pas l’écriture proprement dite

fflush (pour FILE *), std :: flush (pour IOStream) pour forcer votre programme à envoyer au système d’exploitation.

POSIX a

  • sync (2) pour demander de programmer l’écriture de ses tampons, mais peut revenir avant la fin de l’écriture (Linux attend que les données soient envoyées au matériel avant de retourner).

  • fsync (2) qui attend avec certitude que les données soient envoyées au matériel, mais nécessite un descripteur de fichier (vous pouvez en obtenir un à partir d’un fichier * avec fileno (3), je ne connais aucune méthode standard pour en obtenir un IOStream).

  • O_SYNC comme un drapeau à ouvrir (2).

Dans tous les cas, le matériel peut avoir ses propres tampons (mais s’il a le contrôle, une bonne implémentation essaiera de les vider également et ISTR que certains disques utilisent des condensateurs afin qu’ils puissent vider tout ce qui arrive à l’alimentation) et les systèmes de fichiers réseau ont leur propre avertissement.

Vous pouvez utiliser fsync () / fdatasync () pour forcer (Note 1) les données sur le stockage. Ceux-ci requièrent un descripteur de fichier, tel qu’indiqué par exemple par open (). La page de manuel linux contient plus d’informations spécifiques à Linux, en particulier sur la différence entre fsync et fdatasync.

Si vous n’utilisez pas directement les descendants de fichiers, de nombreuses abstractions contiendront des tampons internes résidant dans votre processus.

Par exemple, si vous utilisez un FICHIER *, vous devez d’abord vider les données de votre application.

 //... open and write data to a FILE *myfile fflush(myfile); fsync(fileno(myfile)); 
  • Remarque 1: ces appels forcent le système d’exploitation à vérifier que toutes les données contenues dans le cache du système d’exploitation sont écrites sur le lecteur et que le lecteur en est conscient. De nombreux disques durs sont liés au système d’exploitation à ce sujet et risquent de surcharger les données de la mémoire cache du lecteur.

Pas en standard C ++. Vous devrez utiliser une sorte d’E / S spécifique au système, comme open avec l’indicateur O_SYNC sous Unix, puis write .

Notez que ceci est partiellement implicite par le fait que ostream (et dans C, FILE* ) est mis en mémoire tampon. Si vous ne savez pas exactement quand quelque chose est écrit sur le disque, cela n’a pas beaucoup de sens d’insister sur l’intégrité transactionnelle de l’écriture. (Il ne serait pas trop difficile de concevoir un streambuf qui n’écrit que lorsque vous faites un flush explicite).

MODIFIER:

Comme exemple simple:

 class SynchronizedStreambuf : public std::streambuf { int myFd; std::vector myBuffer; protected: virtual int overflow( int ch ); virtual int sync(); public: SynchronizedStreambuf( std::ssortingng 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::ssortingng const& filename ) : myFd( open( filename.c_str(), O_WRONLY | O_CREAT | O_SYNC, 0664 ) ) { } SynchronizedStreambuf::~SynchronizedStreambuf() { sync(); close( myFd ); } 

(Cela n’a été testé que superficiellement, mais l’idée de base est là.)