Lorsque je redirige la sortie d'une commande vers un fichier (par exemple, echo Hello > file
), est-il garanti que ce fichier contient de telles données juste après la fermeture de la commande? Ou existe-t-il encore une très petite fenêtre entre les commandes exits et les données écrites dans le fichier? J'aimerais lire le fichier juste après la fermeture de la commande, mais je ne veux pas lire un fichier vide.
Plusieurs couches de tampons/caches sont impliquées.
Le cache du processeur.
Les données sont rassemblées octet par octet et stockées dans le cache de la CPU. Si le cache de la CPU est plein et que les données ne sont pas utilisées depuis un moment, le bloc contenant nos données peut être écrit dans la mémoire principale. Ceux-ci sont, pour la plupart, cachés aux programmeurs d'applications.
Les tampons en cours de traitement.
Une partie de la mémoire est mise de côté dans le processus de collecte des données. Nous devons donc envoyer le moins de demandes possible au système d'exploitation, car cela coûte relativement cher. Le processus copie les données dans ces mémoires tampons, qui peuvent à nouveau être sauvegardées par des caches de CPU, il n’est donc pas garanti que les données soient copiées dans la mémoire principale. L'application doit vider explicitement ces tampons, par exemple à l'aide de fclose (3) ou de fsync (3). La fonction exit (3) le fait également avant la fin du processus, alors que la fonction _exit (2) ne le fait pas , c’est pourquoi un grand avertissement apparaît dans la page de manuel pour que cette fonction ne l’appelle que si vous Sais ce que tu fais.
Les tampons du noyau
Le système d'exploitation conserve alors son propre cache afin de réduire le nombre de demandes qu'il doit envoyer aux disques. Ce cache n'appartenant à aucun processus en particulier, les données qu'il contient peuvent appartenir à des processus déjà terminés et, comme tous les accès passent par ici, le programme suivant verra les données s'il est arrivé ici. Le noyau écrira ces données sur les disques lorsqu'il en aura le temps ou explicitement.
Le cache du lecteur
Les lecteurs de disque eux-mêmes conservent également un cache pour accélérer les accès. Celles-ci sont écrites assez rapidement et il existe une commande permettant d'écrire les données restantes dans les caches et de signaler, une fois celle-ci terminée, que le système d'exploitation utilise à l'arrêt pour s'assurer qu'aucune donnée n'est laissée non écrite avant la mise hors tension.
Pour votre application, il suffit que les données soient enregistrées dans les tampons du noyau (les données réelles peuvent encore vivre dans les caches de la CPU à ce stade et n'ont peut-être pas été écrites dans la mémoire principale): le processus "echo" se termine, Cela signifie que tous les tampons en cours de processus doivent avoir été vidés et que les données ont été transmises au système d'exploitation. Lorsque vous démarrez un nouveau processus, il est alors garanti que le système d'exploitation restituera les mêmes données à la demande.
Si l'application ne dispose pas de caches internes, les modifications seront immédiatement écrites dans le fichier. La même chose pour votre exemple. Le fichier est une entité logique en mémoire qui sera immédiatement mise à jour. Toutes les opérations ultérieures sur le fichier verront les modifications apportées par le programme.
Cependant , cela ne signifie pas que la modification a été écrite sur le disque physique. Les modifications peuvent persister dans les caches de système de fichiers du système d'exploitation ou de matériel. Pour vider les tampons du système de fichiers, utilisez la commande sync
.
J'aimerais lire le fichier juste après la fermeture de la commande, mais je ne veux pas lire un fichier vide.
Vous ne devriez pas rencontrer de problèmes pratiques ici.
La mémoire tampon sera-t-elle automatiquement vidée sur le disque lorsqu'un processus se terminera?
En général, la réponse est no .
Cela dépend de la commande. Comme le mentionnent les autres réponses, if, la commande ne met pas les données en mémoire tampon en interne. Toutes les données sont disponibles à la fin de la commande.
Mais la plupart, sinon la totalité, des bibliothèques d'E/S standard do buffer stdout par défaut (dans une certaine mesure), et donnent des garanties différentes sur le vidage automatique des tampons à la fermeture de l'application.
C garantit qu’une sortie normale videra les tampons . «Sortie normale» signifie que exit
est appelé - soit explicitement, soit en revenant de main
. Cependant, une sortie anormale peut contourner cet appel (et donc laisser des tampons non vidés derrière).
Voici un exemple simple:
#include <signal.h>
#include <stdio.h>
int main() {
printf("test");
raise(SIGABRT);
}
Si vous compilez ceci et l'exécutez, test
sera pas nécessairement écrit sur stdout.
D'autres langages de programmation offrent encore moins de garanties: Java, par exemple, fait pas vidage automatique à la fin du programme . Si le tampon de sortie contient une ligne non terminée, elle peut donc être perdue, à moins que System.out.flush()
n'ait été appelé explicitement.
Cela dit, le corps de votre question demande quelque chose de légèrement différent: si les données arrivent dans le fichier du tout _, il devrait le faire immédiatement après la fin de la commande (sous réserve des avertissements décrits dans les autres réponses).
Je pense qu’aucune question ne résout suffisamment ce problème:
J'aimerais lire le fichier juste après la fermeture de la commande, mais je ne veux pas lire un fichier vide.
Comme l'expliquent les autres réponses, un programme performant vide ses mémoires tampons de fichiers internes avant la fin du processus normalement. Ensuite, les données peuvent toujours rester dans les tampons du noyau ou du matériel avant d'être écrites dans le stockage persistant. Cependant , la sémantique du système de fichiers de Linux garantit que tous les processus voient le contenu des fichiers de la même manière que le noyau _ {y compris les tampons internes} _1.
Ceci est généralement implémenté en ayant au plus un tampon dans le noyau par objet de fichier et en exigeant que tous les accès aux fichiers passent par ce tampon.
Si un processus lit un fichier, le noyau présentera le contenu de la mémoire tampon au processus, si la partie de fichier demandée est actuellement dans la mémoire tampon; Si ce n'est pas le cas, le noyau récupérera les données du support de stockage sous-jacent et les placera dans la mémoire tampon, puis reviendra à l'étape précédente.
Si un processus écrit dans un fichier, les données sont d'abord placées dans la mémoire tampon du noyau pour ce fichier. Finalement, le contenu de la mémoire tampon sera vidé dans la mémoire. En même temps, l'accès en lecture est satisfait à partir du même tampon (voir ci-dessus).
1 Au moins pour les fichiers normaux, les répertoires et les liens symboliques. Les FIFO et les sockets sont une autre affaire puisque leur contenu n’est jamais stocké de manière persistante de toute façon. Il existe des cas spéciaux de fichiers normaux dont le contenu dépend de qui le demande; les exemples sont des fichiers dans procfs et sysfs (think /proc/self
qui est un lien symbolique vers l'ID de processus du processus qui lit le lien symbolique).
En supposant que votre commande soit exécutée par un programme utilisant la bibliothèque d'exécution C, elle devrait à un moment donné appeler fclose
pour fermer le fichier ouvert.
La page de manuel de la fonction fclose
C indique:
NOTES Notez que fclose () vide uniquement les tampons de l'espace utilisateur fournis par la bibliothèque C. Pour vous assurer que les données sont physiquement stockées sur le disque, vous devez également vider les mémoires tampons du noyau, par exemple avec sync (2) ou fsync (2).
et la page de manuel de fflush
a la même note. La page de manuel de close
indique:
Une fermeture réussie ne garantit pas que les données ont bien été sauvegardées sur le disque, car le noyau diffère l’écriture. Il n’est pas courant pour un système de fichiers de vider les mémoires tampons lorsque le flux est fermé. Si vous devez vous assurer que les données sont physiquement stockées, utilisez fsync (2). (Cela dépendra du matériel de disque à ce stade.)
Notez que les données sont disponibles pour les autres processus même si elles ne sont pas synchronisées avec le lecteur. Peut-être que cela suffit déjà pour vous.
Si vous avez un doute, écrivez un test.
Lorsque je redirige la sortie d'une commande vers un fichier (par exemple,
echo Hello > file
), est-il garanti que ce fichier contient de telles données juste après la fermeture de la commande?
Oui. Le shell ouvre le fichier de sortie et echo
renvoie directement dans celui-ci. Une fois la commande terminée, c'est fait.
Ou existe-t-il encore une très petite fenêtre entre les commandes exits et les données écrites dans le fichier?
Le fait que les données se trouvent déjà sur le support est un autre problème, qui importe uniquement en cas de défaillance matérielle ultérieure, ou si vous inspectez la partition en direct avec un logiciel d'investigation, en ignorant le système de fichiers monté.
J'aimerais lire le fichier juste après la fermeture de la commande, mais je ne veux pas lire un fichier vide.
Ne vous inquiétez pas, le noyau ne conserve qu'une vue du fichier, quelle que soit la fréquence à laquelle il est ouvert.
Ou existe-t-il encore une très petite fenêtre entre les commandes exits et les données écrites dans le fichier?
Non, il n'y en a pas.
J'aimerais lire le fichier juste après la fermeture de la commande, mais je ne veux pas lire un fichier vide.
Vous pouvez lire le contenu final du fichier juste après la fermeture de la commande. Vous ne lirez jamais le fichier vide à la place. (En C et C++, utilisez les appels wait , waitpid , wait3 ou wait4 pour attendre que le programme se ferme, et Si vous utilisez un shell, un autre langage de programmation ou une bibliothèque (par exemple, l’appel de bibliothèque C system ou Java Process class), il utilise probablement l’un des ces appels système déjà.)
Comme d'autres réponses et commentaires l'ont souligné, vous pouvez finir par lire un fichier vide après la sortie du programme si le programme s'est terminé sans vider ses mémoires tampons de sortie internes (par exemple, à cause de _exit , abort ou recevoir un signal fatal, ou parce qu'il s'agit d'un programme Java se terminant normalement). Cependant, vous ne pouvez rien faire à ce sujet à ce stade: les données non vidées sont perdues à jamais, une attente supplémentaire ne les récupérera pas.
En règle générale, toute donnée appartenant au kernel est maintenue et nettoyée par le noyau, point. Ces données incluent les données transférées dans la mémoire du noyau par un appel système tel que write(2)
.
Cependant, si votre application (par exemple, la bibliothèque C) effectue la mise en mémoire tampon sur haut de cela, le noyau n'a évidemment aucune idée et ne garantit donc pas son nettoyage.
De plus, je ne crois pas qu'il y ait une synchronisation garantie pour le nettoyage - elle est généralement effectuée sur la base du "meilleur effort" (lire: "quand j'ai une seconde") .
Désolé d’ajouter peut-être une réponse superflue, mais la plupart semblent se concentrer sur le fil rouge du titre de la question. Mais pour autant que je sache, la question ne concerne pas du tout la mise en mémoire tampon, mais la suivante:
Lorsque je redirige la sortie d'une commande vers un fichier (par exemple, echo Hello> fichier), ce fichier sera-t-il doté de telles données juste après la fermeture de la commande?
Oui, sans condition. L'utilisation de ">" que vous décrivez, avec "|" et "<", est le modèle de traitement basé sur le canal sur lequel le monde Unix et Linux est fortement basé. Vous trouverez des centaines, voire des milliers de scripts totalement dépendants de ce comportement dans chaque installation Linux.
Cela fonctionne comme vous le souhaitez, et s'il y avait la moindre chance de condition de concurrence, cela aurait été corrigé il y a probablement plusieurs décennies.