Lorsque je redirige la sortie d'une commande vers un fichier (par exemple, echo Hello > file
) ce fichier sera-t-il garanti d'avoir de telles données juste après la sortie de la commande? Ou existe-t-il encore une très petite fenêtre entre les sorties de commande et les données écrites dans le fichier? Je voudrais lire le fichier juste après la sortie de la commande, mais je ne veux pas lire un fichier vide.
21
echo
- ils et>
non séparés (de courte durée)? Et où la sortie deecho
rester avant>
est-elle exécutée?>
est la redirection de shell. C'est la même chose que si le programme avait ouvert le fichier nommé pour l'écriture et remplacé stdout par ce qui est exactement ce que fait le shell.file
contenant,Hello
qu'il soit vidé ou non.Réponses:
Plusieurs couches de tampons / caches sont impliquées.
Le cache CPU.
Les données sont regroupées octet par octet et stockées dans le cache CPU. Si le cache du processeur est plein et que les données n'ont pas été accessibles depuis un certain temps, 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.
Il y a de la mémoire mise de côté dans le processus où les données sont collectées, nous devons donc faire le moins de requêtes possible au système d'exploitation, car c'est relativement cher. Le processus copie les données dans ces tampons, qui peuvent à nouveau être sauvegardés par des caches CPU, il n'y a donc aucune garantie que les données sont copiées dans la mémoire principale. L'application doit vider explicitement ces tampons, par exemple en utilisant fclose (3) ou fsync (3). La sortie (3) fonction fait aussi avant que le processus est terminé, alors que la fonction _exit (2) ne , ce qui explique pourquoi il y a un grand avertissement dans la page de manuel pour cette fonction de l' appeler seulement si vous savez ce que vous êtes Faire.
Les tampons du noyau
Le système d'exploitation conserve ensuite son propre cache, afin de minimiser le nombre de demandes qu'il doit envoyer aux disques. Ce cache n'appartient à aucun processus en particulier, donc les données qu'il contient peuvent appartenir à des processus qui sont déjà terminés, et puisque tous les accès passent par ici, le prochain programme verra les données s'il est arrivé ici. Le noyau écrira ces données sur les disques lorsqu'il aura le temps de le faire ou lorsque cela lui sera explicitement demandé.
Le cache du lecteur
Les disques durs 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 pour écrire les données restantes dans les caches et signaler quand cela est terminé, 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 des caches CPU à ce stade, et peuvent ne pas avoir été écrites dans la mémoire principale): le processus "écho" se termine, ce qui signifie que tous les tampons en cours doivent avoir été vidés et que les données ont été transmises au système d'exploitation, et lorsque vous démarrez un nouveau processus, il est garanti que le système d'exploitation restituera les mêmes données à la demande.
la source
Si l'application n'a pas de caches internes, les modifications seront immédiatement écrites dans le fichier. De même 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.
Toutefois , cela ne signifie pas que la modification a été écrite sur le disque physique. Les modifications peuvent persister dans les caches du système de fichiers du système d'exploitation ou les caches matériels. Pour vider les tampons du système de fichiers, utilisez la
sync
commande.Vous ne devriez pas rencontrer de problèmes pratiques ici.
la source
exit
n'est pas au moins implicitement appelé). D'autres bibliothèques / langages (par exemple Java!) Offrent moins de garanties.En général, la réponse est non .
Cela dépend de la commande. Comme les autres réponses le mentionnent, si la commande ne met pas en mémoire tampon les données en interne, toutes les données seront disponibles à la fin de la commande.
Mais la plupart, sinon tous, les bibliothèques d' E / S standard ne stdout tampon par défaut (dans une certaine mesure), et donnent différentes garanties sur le rinçage automatique des tampons lorsque les applications de fermait ses portes.
C garantit qu'une sortie normale videra les tampons . «Sortie normale» signifie que l'
exit
on appelle - soit explicitement, soit en revenant demain
. Cependant, une sortie anormale peut contourner cet appel (et donc laisser des tampons non vidés).Voici un exemple simple:
Si vous le compilez et l'exécutez,
test
il ne sera pas nécessairement écrit sur stdout.D'autres langages de programmation offrent encore moins de garanties: Java, par exemple, ne se vide pas automatiquement à la fin du programme . Si le tampon de sortie contient une ligne non terminée, elle peut donc être perdue, sauf si elle a
System.out.flush()
été appelée explicitement.Cela dit, votre corps de question demande quelque chose de légèrement différent: si les données arrivent dans le fichier du tout , il doit le faire immédiatement après la commande ( se termine sous réserve des mises en garde décrits dans les autres réponses).
la source
write()
oupwrite()
appel système se produira avant la fin du processus, et c'est alors que les modifications du fichier deviennent visibles. Ainsi, le dernier changement de fichier est définitivement avant la fin du processus, immédiatement avant au plus tard. Je pense que même avec unmmap(MAP_SHARED)
fichier, il n'y a aucun moyen d'observer la fin du processus avant que tous les changements de fichiers ne se produisent.Je pense qu'aucune question n'aborde encore suffisamment cette question:
Comme les autres réponses l'expliquent, un programme qui se comporte bien vide ses tampons de fichiers internes avant que le processus ne se termine normalement . Par la suite, les données peuvent encore persister dans les tampons du noyau ou du matériel avant d'être écrites sur un 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 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 du tampon au processus, si la partie de fichier demandée est actuellement dans le tampon; si ce n'est pas le cas, le noyau récupère les données du support de stockage sous-jacent et les place dans le tampon, puis revient à 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 de ce fichier. Finalement, le contenu du tampon sera vidé dans le stockage. Dans l'intervalle, l'accès en lecture est satisfait à partir du même tampon (voir ci-dessus).
1 Au moins pour les fichiers, répertoires et liens symboliques normaux. Les FIFO et les sockets sont une question différente car leur contenu n'est jamais stocké de manière persistante de toute façon. Il existe des cas particuliers de fichiers normaux dont le contenu dépend de qui demande; des exemples sont des fichiers dans procfs et sysfs (pensez
/proc/self
qui est un lien symbolique vers l'ID de processus du processus lisant le lien symbolique).la source
mmap()
et O_DIRECT, ce qui peut entraîner une désynchronisation entre le disque et le cache de pages (mais cela résoudra le moment où le processus qui se termine).En supposant que votre commande est exécutée par un programme utilisant la bibliothèque d'exécution C, à un moment donné, il devrait invoquer
fclose
pour fermer le fichier ouvert.La page de manuel de la
fclose
fonction C indique:et la page de manuel pour
fflush
a la même note. La page de manuel declose
dit:Notez que les données sont disponibles pour d'autres processus même si elles ne sont pas synchronisées avec le lecteur. C'est peut-être déjà assez bon pour vous.
En cas de doute, faites un test.
la source
close()
système pour fermer le descripteur d'un fichier.close
fichiers avant de quitter (dans les programmes hacky qui ne vérifient pas les erreurs); le noyau les nettoiera, vous appelant efficacementclose
après la fin de votre processus. Vous avez cependant besoin defclose
tous les flux stdio tamponnés, ou laissez libc le faire pour vousexit(3)
, contrairement à l'appel système de sortie directement.Oui. Le shell ouvre le fichier de
echo
sortie et y renvoie directement. Une fois la commande terminée, c'est fait.Que les données soient déjà sur le support est une autre question, qui n'a d'importance que s'il y a par la suite une défaillance matérielle, ou si vous inspectez la partition en direct avec un logiciel judiciaire, en contournant le système de fichiers monté.
Ne vous inquiétez pas, le noyau ne conserve qu'une seule vue du fichier, quelle que soit la fréquence d'ouverture.
la source
mmap(MAP_SHARED)
: les magasins dans la région mmapée ne sont pas cohérents avec les lectures du fichier (par ce thread ou d'autres processus). Voilà pourquoimsync(2)
existe. C'est du moins ce que préviennent les pages de manuel; selon l'implémentation, Linux peut en fait mapper des pages physiques du cache de pages, auquel cas je suppose qu'il est fondamentalement cohérent (ordonnancement de la mémoire modulo). Quoi qu'il en soit, tout se passe toujours avant_exit(2)
.En règle générale, toutes les données appartenant au noyau sont conservées et nettoyées par le noyau, point final. 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 une mise en mémoire tampon en plus 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 garantie de calendrier pour le nettoyage - il est, en général, effectué sur la base du "meilleur effort" (lire: "quand j'ai une seconde").
la source
waitpid()
retour d' un processus parent , si le nettoyage a lieu. c'est-à-dire que les autres processus ne peuvent pas observer directement l' arrêt du processus avant toute modification de fichier effectuée par ce processus. (J'ai dit "directement" pour exclure l'observation indirecte via les horodatages des fichiers NFS, car la mise en cache NFS n'est pas parfaitement cohérente entre les hôtes.)fsync
/fdatasync
, bien que la réécriture de la mémoire tampon sur Linux démarre après des/proc/sys/vm/dirty_writeback_centisecs
centièmes de seconde (si elle n'est pas retardée par un autre trafic d'E / S), et divers autres paramètres ajustables dans ce répertoire procfs affectent également les choses (par exemple, comment large pour laisser les tampons croître avant de faire une réécriture).Non, il n'y en a pas.
Vous pouvez lire le contenu final du fichier juste après la fin de la commande, vous ne lirez jamais le fichier vide à la place. (En C et C ++, utilisez les appels système wait , waitpid , wait3 ou wait4 pour attendre la fin du programme, puis lisez le fichier uniquement. Si vous utilisez un shell, un autre langage de programmation ou une bibliothèque (par exemple la bibliothèque C système d' appel ou la classe Java Process ), il utilise probablement déjà l'un de ces appels système.)
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 est sorti sans vider ses tampons de sortie internes (par exemple à cause de _exit , abandonner ou recevoir un signal fatal, ou parce qu'il est un programme Java se fermant normalement). Cependant, vous ne pouvez rien faire à ce stade: les données non vidées sont perdues à jamais, une attente supplémentaire ne les récupérera pas.
la source
Oui
Désolé d'avoir peut-être ajouté une autre réponse superflue, mais la plupart semblent se concentrer sur le hareng 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 ceci:
Oui, sans condition. L'utilisation de ">" que vous décrivez, avec "|" et "<", est le modèle de traitement basé sur les canaux sur lequel le monde Unix et Linux est fortement basé. Vous trouverez des centaines, voire des milliers de scripts dépendant totalement de ce comportement dans chaque installation Linux.
Il fonctionne comme vous le souhaitez par conception, et s'il y avait même la moindre chance d'une condition de course, elle aurait été corrigée il y a probablement des décennies.
la source