IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Ecriture "crash safe" dans un fichier


Sujet :

Langage C++

  1. #1
    Membre confirmé
    Inscrit en
    Décembre 2003
    Messages
    87
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 87
    Par défaut Ecriture "crash safe" dans un fichier
    Bonjour,

    Dans mon application j'utilise un fichier xml pour sauvegarder l'état de différentes variables. Le but est qu'en cas de redémarrage de l'application (du pc) celle ci reprenne son exécution la ou elle c'était arrêtée. Mon problème est que si je tire la prise du PC / kill mon appli au moment de l'écriture les données contenues dans le fichier sont corrompues.

    Y a t'il un moyen d'éviter ce problème? Je pensais à une solution du genre écrire dans un fichier temporaire puis le déplacer pour écraser le fichier persistant.

    Petite précision au cas ou, je travaille sous linux et compile avec g++.

    Merci d'avance pour vos suggestions.

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Par défaut
    Tu pourrais peut être penser à un thread qui s'occuperait de scruter à intervalle régulier (de manière à ne pas surcharger le scheduleur) les variables à sauvegarder, et en cas de modification de ces variables (qui auraient été initialisées au préalable de manière à déterminer si oui ou non elles auraient été modifiées), le thread se chargerait d'écrire dans le fichier XML. De la sorte, ton fichier XML de sauvegarde serait constamment à jour, les risques de données corrompues sont fortement réduis mais pas totalement éliminés.

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Il y a, malheureusement, des limites à ce que l'on peut faire... : Si on débranche la prise, rien ni personne n'aura le temps d'appliquer aucune procédure garantissant à 100% qu'il sera possible de reprendre l'exécution du programme exactement là où il s'est arrêté.

    Par contre, on peut effectivement décider de recourrir (quelle horreur) à un singleton ou, ( beaucoup ) mieux, à un "monostate" dont le destructeur enregistrerait un état particulier de l'application et les quelques manipulations à effectuer, un peu à la manière d'un script à effectuer.

    Nous partirions alors vers quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    /* l'objet qui sera sauvegardé à la destruction */
    class Restorable
    {
        public:
            ~Restorable()
            {
                /* je fais simple, mais on peut partir sur boost::archive et
                 * / ou boost::serialization, par exemple
                 */
                std::ofstream ofs("resotre.txt");
                saveState(ofs);
                saveActions(ofs);
            }
            void addAction(Action * toadd)
            {
                actions.push_back(toadd);
            }
            void execute()
            {
                while(!actions.empty())
                {
                    /* exécuter la première action à effectuer
                     * chaque action modifie state ;-)
                     */
                    *(actions.begin())->execute();
                     delete *(actions.begin());
                     actions.pop_front();
                }
            }
            void saveState(std::ofstream& ofs)
            {
                /* ici, on sauvegarde l'état courent */
            }
            void saveActions(ofs)
            {
                while(!actions.empty())
                {
                    /* sauvegarde des actions restantes
                     */
                    *(actions.begin())->save(ofs);
                     delete *(actions.begin());
                     actions.pop_front();
                }
            }
        private:
            /* un "état" représentant les données à un moment donné */
            State state;
            std::list<Action* > actions;
    }
    /* le "monostate" qui gère effectivement les actions à 
     * entreprendre
     */
    class ActionManager
    {
        public:
            void addAction(Action * toadd)
            {
                toRestore.addAction(toadd);
                toRestore.execute();
            }
        private:
            static Resotrable toRestore;
    };
    /* dans un *.cpp */
    Restorable ActionManager::toRestore = Restorable();
    Chaque fois que tu as une action à effectuer, il ne te "reste" plus qu'à passer par ton "ActionManager" pour la gérer, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main()
    {
        Action *theAction = new ConcreteAction(/* parametres */);
        ActionManager man;
        man.addAction( theAction );
    }
    l'objet toRestore sera, d'office, détruit au moment de quitter l'application, ce qui provoquera normalement la sauvegarde tant de l'état actuel que pour les actions restant à entreprendre
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #4
    Membre confirmé
    Inscrit en
    Décembre 2003
    Messages
    87
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 87
    Par défaut
    Merci pour ces réponses. En fait le but c'est pas forcement de reprendre l’exécution exactement ou elle s'est arrêtée. Si on manque les changements des 5 dernières secondes c'est pas grave. Par contre le but est que cela fonctionne même si on tire la prise donc la solution de sauvegarder dans le destructeur ne s'applique pas ici.

    Je me demandais par contre si l'utilisation de rename éviterai que le fichier soit corrompu. Mon idée serait d'écrire dans un fichier temporaire temp.xml et après chaque écriture faire: rename("temp.xml", "persistent.xml");. Mais peut-être que je me trompe et que cette solution ne change absolument rien...

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par piemur2000 Voir le message
    Merci pour ces réponses. En fait le but c'est pas forcement de reprendre l’exécution exactement ou elle s'est arrêtée. Si on manque les changements des 5 dernières secondes c'est pas grave. Par contre le but est que cela fonctionne même si on tire la prise donc la solution de sauvegarder dans le destructeur ne s'applique pas ici.
    <mode petite histoire : on> Je regardais ce week end un reportage sur un crash d'avion qui, par chance, n'avait pas fait de victimes...

    Les enquêteurs s'étonnaient qu'un appareil de sauvegarde ne donnait pas les données relatives aux 45 dernières secondes avant le crash, et se sont donc, naturellement, dirigés vers "un problème électrique"...

    Jusqu'au moment où ils se sont rendus compte que le dit appareillage collectait énormément de données toutes les secondes puis les "compilait" pendant... 45 secondes avant de les enregistrer.

    L'appareil n'était absolument pas tombé en panne avant le crash, simplement, il avait cessé de fonctionné suite au crash, et donc, il y avait 45 secondes qui n'avaient simplement pas pu être sauvegardées, parce que en cours de traitement lorsque "tout s'est éteint"...
    <mode petite histoire : off >

    Comme je te l'ai dit, il est possible de se prémunir contre les impondérables, mais seulement jusqu'à un certain point...

    Tu pourrais, par exemple, envisager la mise en place d'un timer qui provoquerait de manière systématique la sauvegarde à intervale régulier (durant l'exécution "correcte" de l'application), voir une sauvegarde "avant plantage", mais RIEN, je le répète, ne t'offrira la moindre garantie au cas où tu te prendrais les pieds dans les cables
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre éclairé

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Par défaut
    Il serait peut-être possible qu'à chaque fois qu'une donnée change, un Event soit émit. Il serait alors possible de le catch et soit sauvegarder les changements à ce moment la, soit activer un bool, notifiant un thread externe qu'il existe des changements. Le thread externe pourrait simplement regarder à intervals réguliers s'il existe des changements et si oui les sauvegarder.

  7. #7
    Membre chevronné

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 426
    Par défaut
    Salut, je me permet d'intervenir car je crois qu'il s'agit d'un dialogue de sourds ( si j'ai bien compris )

    Ce que piemur2000 souhaite ( dis moi si je me trompe ), c'est qu'à certains moments de son programme ( et pas forcément en permanence ) pouvoir sauvegarder des infos de configuration ( états de variables ) dans un fichier.
    Sa hantise, c'est comment être certain qu'au prochain lancement du dit programme, celui-ci ait bien un fichier de config utilisable et non un fichier tout cassé ( si quelqu'un avait tiré la prise au mauvais moment )!

    Il propose une solution lui-même :
    1)créer un fichier avec un nom différent ( en prenant son temps ) : là si on débranche, le fichier de config utilisé au prochain démarrage ne sera pas corrompu ( puisque c'est l'ancien auquel on a pas touché )
    2)et une fois ce fichier bien écrit et cloturé : on change son nom pour lui donné celui du fichier que le programme chargera au prochain démarrage!

    piemur2000 considère alors que le changement de nom du fichier est bien plus rapide que la création de celui-ci, et que donc, ça se passe en moins de temps qu'il ne faut pour débrancher la prise de PC.

    piemur2000? Es-ce que j'ai compris la question? Ou suis-je à coté de la plaque?

    PS : @Koala01 : moi aussi j'ai vu le reportage!! Et moi aussi j'ai trouvé hallucinant que de prétendus experts n'aient même pas un vague idée du fonctionnement du matériel qu'ils expertisent et qu'il perdent tant de temps à chercher l'origine d'une panne qui n'existe pas!!! Ils ont du être très perturbés quand il ont vu qu'il n'y avait pas de lampes dans le poste de radio, mais d'étranges "milles-pattes" que certains initiés appellent "circuits intégrés"

  8. #8
    Membre confirmé
    Inscrit en
    Décembre 2003
    Messages
    87
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 87
    Par défaut
    @berty: Oui c'est exactement ça. L'idéal aurait été que l'opération de changement de nom ne puisse pas corrompre le fichier c'est à dire qu'elle réussisse ou échoue mais ne puisse pas laisser le système dans une situation corrompue. Malheureusement je ne pense pas garantir ça.

    Merci, il ne me reste plus qu'a implémenter cette solution.

  9. #9
    Membre chevronné

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 426
    Par défaut
    Je j'ai peut-être une solution ( proche de la tienne ) mais je ne sais pas ce qu'elle vaut. La voici :

    1)Quand tu t'apprête à faire une sauvegarde, tu crée d'abord un fichier vide par exemple "temoin.txt" ( ce fichier restera vide )

    2)Tu renomme le fichier "config.txt" valide en "config.bak" : ça permet de sauvegarder les information périmées au cas ou tu en aurais de besoin.

    3)Tu crée ton nouveau fichier "config.txt"

    4)Tu efface ton fichier "temoin.txt"

    Quel est l’intérêt du fichier "temoin.txt" vas-tu me demander?
    Et bien si ton programme trouve ce fichier lors de son lancement, ça veut dire que la sauvegarde à été interrompue ( la prise à été débranchée ), puisque le fichier "temoin.txt" n'as pas pu être effacée.
    Ça peut signifier que le fichier "config.txt" peut être corrompu et qu'il vaut mieux utiliser le fichier "config.bak".

    Une variante de cette solution ( sans le fichier "temoin.txt" cette fois ), "hyper sécure", est de nommer les fichiers avec un numéro, par exemple "config_00001.txt" puis "config_00002.txt", "config_00003.txt" etc... comme ça ça évite le risque pris en renommant le fichier ( au cas ou la prise soit débranchée juste à ce moment là ( ce serait pas de chance quand même, mais on ne sait jamais! ) ).
    Quand tu as créé le fichier numéro N, tu effaces le fichier numéro N-2.
    Et ainsi comme précédemment si tu trouves le fichier N-2 tu n'utilises alors pas le dernier fichier ( N ), qui risque d'être corrompu, mais l'avant dernier ( N-1 )...

    J'attends les avis sur ces solutions...

  10. #10
    Membre confirmé
    Inscrit en
    Décembre 2003
    Messages
    87
    Détails du profil
    Informations forums :
    Inscription : Décembre 2003
    Messages : 87
    Par défaut
    En effet l'utilisation du fichier témoin me parait une bonne solution pour s'assurer que tout c'est bien passé. Je pense que je vais implémenter ça de la manière suivante.

    1. Copier le fichier courant (data.txt) dans un fichier de sauvegarde (data.bak)
    2. Créer le fichier témoin (.updatingData)
    3. Écraser le fichier data avec les nouvelles données
    4. Supprimer .updatingData
    (éventuellement 5. Supprimer data.bak)

    Au démarrage si je trouve .updatingData je charge data.bak sinon data.txt.

    Merci pour les propositions.

  11. #11
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 487
    Par défaut
    Pour apporter ma maigre contribution, je dirais que vis-à-vis du disque, c'est mieux si tu ne perds pas de temps à « copier » les données mais qu'effectivement, tu crées directement un nouveau fichier en le nommant avec un numéro de séquence progressif. Tu effaces ensuite le plus ancien.

    Avantage supplémentaire : il te suffit d'un seul paramètre pour dire si tu souhaites garder les deux, les cinq ou les cent derniers fichiers. Tu peux ainsi te constituer si nécessaire une trace du déroulement de ton programme, utile pour le déboguage et en fonction de l'importance des données sur lesquelles tu travailles.

    N'oublie pas non plus de terminer le contenu de ton fichier par un checksum ou un C.R.C. pour garantir leur validité (et te rabattre le cas échéant sur le précédent fichier).

  12. #12
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par piemur2000 Voir le message
    @berty: Oui c'est exactement ça. L'idéal aurait été que l'opération de changement de nom ne puisse pas corrompre le fichier c'est à dire qu'elle réussisse ou échoue mais ne puisse pas laisser le système dans une situation corrompue. Malheureusement je ne pense pas garantir ça.

    Merci, il ne me reste plus qu'a implémenter cette solution.
    Certains systèmes de fichiers le garantissent pour toi : ext3/4 avec la journalisation, par exemple. L'idée est que les données écrites doivent être synchronisées avec les données du journal. Si le journal n'est pas mis à jour, alors les données ne sont pas écrites.

    Deux opérations sont définies : write et commit. Sur le write, tu sauvegarde les données courantes (copie du fichier), tu écris les données dans nouveau fichier, et tu mets à jour le fichier journal.

    Dans le commit, tu copies le nouveau fichier, tu mets à jour le journal, et tu efface la sauvegarde.

    Ces opérations sont sérialisées, c'est à fire que tu ne peux pas faire deux write suivi d'un commit ; l'ordre est toujours write/commit, write/commit. Au niveau des performances, ça se ressent un peu, mais la sécurité a un prix

    Quel que soit le moment, tu peux soit retrouver l'ancienne configuration, soit être sur que ta config est correcte.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  13. #13
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,

    Citation Envoyé par koala01 Voir le message
    Par contre, on peut effectivement décider de recourrir (quelle horreur) à un singleton ou, ( beaucoup ) mieux, à un "monostate"
    C'est un peu HS par rapport au sujet mais comme j'ai vu cette phrase reprise par ailleur, je me permets d'intervenir : un monostate n'est pas beaucoup mieux ni même un peu mieux qu'un singleton. Pour moi, monostate/singleton/variable globale/variable de classe/variable statique à l'intérieur d'une fonction sont tous des états globaux, apportent les mêmes problèmes et sont donc équivalents.

    Pour continuer à marteler mon avis, mieux vaut s'auto-citer : ici et pour continuer le débat : la nouvelle discussion de notre amis dragonjoker suite aux quelques mots de Koala ci-dessus.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 12
    Dernier message: 22/04/2009, 13h02
  2. Ecriture d'un code hexadecimale dans un fichier...
    Par kore62 dans le forum Langage
    Réponses: 2
    Dernier message: 13/09/2008, 18h03
  3. Ecriture et lecture de class dans un fichier
    Par Dam06 dans le forum C++
    Réponses: 1
    Dernier message: 17/04/2008, 10h25
  4. Ecriture d'un seul bit dans un fichier
    Par fantomasmusic dans le forum Entrée/Sortie
    Réponses: 4
    Dernier message: 31/01/2005, 19h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo