2017-02-09 2 views
1

Sur mon système embarqué, je veux m'assurer que les données sont écrites en toute sécurité lorsque je ferme un fichier - si le système signale que les données ont été enregistrées, l'utilisateur doit être capable d'enlever le courant immédiatement.Ai-je besoin de fermer un fichier avant d'appeler syncfs()

Je sais que la bonne façon de le faire est fsync(), fclose() et fsync() sur le répertoire (cfr. this blog entry). Cependant, il est un peu difficile d'obtenir un descripteur de fichier pour le répertoire dans mon cas (je devrais passer par /proc/self/fd pour trouver le nom de fichier et dériver le répertoire à partir de là). Il serait beaucoup plus simple pour moi de faire syncfs() sur l'ensemble du système de fichiers - je sais que c'est le seul fichier qui est ouvert sur le système de fichiers de toute façon.

Maintenant, ma question est la suivante:

  • Est-ce suffisant pour faire syncfs()?
  • Dois-je d'abord fclose() le FILE * d'abord (pour que l'entrée du répertoire soit à jour)? Ou est fflush() suffisante?
  • S'il doit être fermé, est-il utile de dup() le descripteur de fichier avant la fermeture afin que je puisse l'utiliser directement pour syncfs()?

Répondre

1

Activer le drapeau "de synchronisation" dans votre système de fichiers (/ etc/fstab), par défaut est "async" (désactivé). Lorsque cet indicateur est activé, toutes les modifications apportées au système de fichiers correspondant sont immédiatement vidées sur le disque. Cela rend votre système de fichiers entier lent, mais en fonction de vos exigences système embarquées, cela peut être une bonne option à considérer.

+0

Merci, mais ce n'est pas une option. Cela rendrait chaque bloc write() pendant un temps relativement long et nous ne pouvons pas nous le permettre. Lorsque le fichier est fermé est un bon moment pour synchroniser. – Arnout

1

Tout d'abord, ne pas mélanger la bibliothèque standard <stdio.h> appels (comme fprintf(3) ou fopen(3)) avec les appels système (comme open(2) ou close(2) ou sync(2)) que les formateurs sont des routines bibliothèque qui utilisent des tampons en cours de traitement pour stocker les données temporaires , pour lequel le système n'est pas conscient, et les autres sont des interfaces de système d'exploitation qui rendent le système responsable de la maintenance des données à partir de maintenant. Vous les distinguerez facilement car les premiers utilisent les descripteurs FILE * pour fonctionner, tandis que les derniers utilisent int descripteurs entiers pour fonctionner.

Donc, si vous utilisez un appel système pour assurer que vos données sont correctement synchronisées sur le disque, il est absolument neccessary au premier fflush(3) vos processus données tampons avant de faire le système de fichiers ou sync(2)fsync(2) appel.

Aucun sync(2) est justifié de se produire à fclose(3) ou même close(2) temps, ou dans le atexit() callbacks votre processus fait avant exit().
Les tampons du système d'exploitation sont retardés en écriture pour des raisons de performance, et close(2) n'est pas un événement qui déclenche une telle chose. Pensez simplement que de nombreux processus peuvent être en train de lire et d'écrire le même fichier en même temps, et que chaque close(2) déclenchement d'un flush du système de fichiers pourrait être pénible à réaliser. Le système d'exploitation déclenche ces appels à intervalles réguliers, sur les appels système umount(2), sur l'arrêt du système et sur des appels spécifiques aux appels système sync(2) et fsync(2).

Si vous devez maintenir le descripteur FILE *fd ouvert, juste faire une fflush(fd) pour ce descripteur pour faire en sorte que le système d'exploitation a tous ses tampons pour fwrite(3) d ou fprintf(3) données ées en premier.

Donc finalement, si vous utilisez <stdio.h> fonctions, tout d'abord faire un fflush() pour tous les descripteurs FILE * que vous avez écrit, ou appelez fflush(NULL); dire stdio de synchroniser tous les descripteurs en un seul appel. Ensuite, faites l'appel sync(2) ou fsync(2) pour vous assurer que toutes vos données sont physiquement sur le disque. Pas besoin de fermer quoi que ce soit.

FILE *fd; 
... 
fflush(fd); 
fsync(fileno(fd)); 
/* here you know that up to the last write(2) or fwrite(3)... 
* data is synced to disk */ 

Par ailleurs, votre approche d'aller /dev/fd/<number> pour obtenir le descripteur (que vous aviez précédemment) est défectueux pour deux raisons:

  • Une fois que vous fermez votre descripteur, /dev/fd/<number> n'est plus le descripteur que vous voulez. Normalement, ça n'existe pas, même. Essayez ceci:

    #include <string.h> 
    #include <stdlib.h> 
    #include <fcntl.h> 
    #include <unistd.h> 
    #include <stdio.h> 
    #include <errno.h> 
    
    int main() 
    { 
        int fd; 
        char fn[] = "/dev/fd/1"; 
    
        close(1); /* close standard output */ 
        fd = open(fn, O_RDONLY); /* try to reopen from /dev/fd */ 
        if (fd < 0) { 
         fprintf(stderr, 
           "%s: %s(errno=%d)\n", 
           fn, 
           strerror(errno), 
           errno); 
         exit(EXIT_FAILURE); 
        } 
        exit(EXIT_SUCCESS); 
    } /* main */ 
    
  • Vous ne pouvez pas obtenir le répertoire dans lequel un fichier ouvert appartient avec seulement le descripteur de fichier. Dans un fichier multiliaison, il peut y avoir des milliers de répertoires qui pointent juste dessus. Il n'y a rien sur l'inode (ou dans la structure de fichier ouverte) qui vous permet d'obtenir le chemin utilisé pour ouvrir ce fichier. Une façon courante d'utiliser des fichiers temporaires est simplement de les créer et de les immédiatement, ainsi personne ne peut l'ouvrir à nouveau. Même si vous conservez le fichier ouvert, vous y avez accès, mais aucun chemin ne le pointe plus.

+0

Soyez également prudent, car le fait de forcer les synchronisations dans les systèmes embarqués qui utilisent la mémoire flash est un problème pour les cartes SD défectueuses qui atteignent leur fin de vie prématurément. –

+0

Merci pour votre longue réponse, mais j'ai demandé à propos de syncfs(), pas sync() ou fsync(). Pour les données du fichier il n'y a pas de problème, je peux juste faire fflush() + fsync() (BTW il n'y a pas d'alternative au mélange FILE * et fd ici, car l'interface FILE * n'offre pas une opération de synchronisation). Le problème est vraiment l'entrée du répertoire, qui n'est pas synchronisée quand vous faites sync(), et qui peut encore être mise à jour quand vous fermez() le fd (je ne suis pas sûr de ça). Vous pouvez réellement obtenir le répertoire en passant par/proc/self/fd, à condition qu'il n'ait pas été renommé() d bien sûr. – Arnout

+0

@Arnout, il suffit de lire les pages de manuel, 'syncfs (2)' est la même fonctionnalité que BSD 'fsync (2)', pour synchroniser sur le stockage secondaire les données liées à un fichier ouvert. Hier, j'étais sur un Mac OS X qui dérive de BSD et pas d'accès à un système Linux pour vérifier les pages man. L'alternative 'FILE *' vous offre une fonction 'fileno (3)' qui vous donne accès au descripteur du système de fichiers, de sorte que tous les appels système y sont disponibles. Quoi qu'il en soit, l'entrée du répertoire n'est jamais synchronisée en premier (l'entrée est toujours synchronisée après le fichier, pour assurer l'intégrité du système de fichiers) et n'est pas liée au fichier que vous avez ... –