Wenn ich die Ausgabe eines Befehls in eine Datei umleite (z. B. echo Hello > file
), wird sichergestellt, dass diese Datei diese Daten unmittelbar nach dem Beenden des Befehls enthält? Oder gibt es immer noch ein sehr kleines Fenster zwischen den Befehlsexits und den in die Datei geschriebenen Daten? Ich möchte die Datei direkt nach dem Beenden des Befehls lesen, möchte aber keine leere Datei lesen.
linux
hard-drive
process
file-io
Eric
quelle
quelle
echo
und>
nicht getrennte (kurzlebige) Prozesse? Und wo wird die Ausgabeecho
vonrest>
ausgeführt?>
ist die Shell-Umleitung. Es ist dasselbe, als hätte das Programm die genannte Datei zum Schreiben geöffnet und durch stdout ersetzt, was genau das ist, was die Shell tut.file
Containing zu geben ,Hello
unabhängig davon, ob es gespült wird oder nicht.Antworten:
Es sind mehrere Schichten von Puffern / Caches beteiligt.
Der CPU-Cache.
Die Daten werden byteweise zusammengesetzt und im CPU-Cache abgelegt. Wenn der CPU-Cache voll ist und auf die Daten eine Zeit lang nicht zugegriffen wurde, wird der Block, der unsere Daten enthält, möglicherweise in den Hauptspeicher geschrieben. Diese sind größtenteils den Anwendungsprogrammierern verborgen.
Die In-Process-Puffer.
In dem Prozess, in dem die Daten gesammelt werden, ist ein Teil des Speichers reserviert, sodass wir so wenig Anforderungen wie möglich an das Betriebssystem stellen müssen, da dies vergleichsweise teuer ist. Der Prozess kopiert die Daten in diese Puffer, die wiederum von CPU-Caches gesichert werden können. Daher kann nicht garantiert werden, dass die Daten in den Hauptspeicher kopiert werden. Die Anwendung muss diese Puffer explizit leeren, z. B. mit fclose (3) oder fsync (3). Die exit (3) -Funktion tut dies auch, bevor der Prozess beendet wird, während die _exit (2) -Funktion dies nicht tut , weshalb es in der Manualpage eine große Warnung gibt, dass diese Funktion nur dann aufgerufen wird, wenn Sie wissen, was Sie sind tun.
Der Kernel puffert
Das Betriebssystem behält dann seinen eigenen Cache bei, um die Anzahl der Anforderungen zu minimieren, die es an die Festplatten senden muss. Dieser Cache gehört keinem bestimmten Prozess an, daher gehören die Daten dort möglicherweise zu Prozessen, die bereits beendet wurden. Da alle Zugriffe hier durchgeführt werden, werden die Daten dem nächsten Programm angezeigt, wenn sie hier angekommen sind. Der Kernel schreibt diese Daten auf die Datenträger, wenn er Zeit dazu hat oder wenn er ausdrücklich dazu aufgefordert wird.
Der Laufwerk-Cache
Die Laufwerke selbst haben auch einen Cache, um die Zugriffe zu beschleunigen. Diese werden relativ schnell geschrieben, und es gibt einen Befehl zum Schreiben der verbleibenden Daten in die Caches und zum Melden, wenn diese abgeschlossen sind. Das Betriebssystem verwendet diesen Befehl beim Herunterfahren, um sicherzustellen, dass keine Daten vor dem Ausschalten ungeschrieben bleiben.
Für Ihre Anwendung ist es ausreichend, dass die Daten in den Kernel-Puffern registriert sind (die tatsächlichen Daten befinden sich möglicherweise zu diesem Zeitpunkt noch in CPU-Caches und wurden möglicherweise nicht in den Hauptspeicher geschrieben): Der "Echo" -Prozess wird beendet Dies bedeutet, dass alle In-Process-Puffer geleert und die Daten an das Betriebssystem übergeben wurden. Wenn Sie dann einen neuen Prozess starten, wird garantiert, dass das Betriebssystem die gleichen Daten zurückgibt, wenn Sie dazu aufgefordert werden.
quelle
Wenn die Anwendung keine internen Caches hat, werden die Änderungen sofort in die Datei geschrieben. Das gleiche gilt für dein Beispiel. Die Datei ist eine logische Einheit im Speicher, die sofort aktualisiert wird. Alle nachfolgenden Operationen an der Datei werden die vom Programm vorgenommenen Änderungen angezeigt.
Dies bedeutet jedoch nicht, dass die Änderung auf die physische Festplatte geschrieben wurde. Die Änderungen können in den Dateisystem- oder Hardware-Caches des Betriebssystems verbleiben. Verwenden Sie den
sync
Befehl, um die Dateisystempuffer zu leeren .Sie sollten hier auf keine praktischen Probleme stoßen.
quelle
exit
nicht zumindest implizit aufgerufen wird). Andere Bibliotheken / Sprachen (zB Java!) Geben weniger Garantien.Im Allgemeinen lautet die Antwort nein .
Das hängt vom Befehl ab. Wie in den anderen Antworten erwähnt, sind alle Daten verfügbar, wenn der Befehl beendet wird , wenn der Befehl die Daten nicht intern puffert.
Aber die meisten, wenn nicht alle Standard - I / O - Bibliotheken tun Puffer stdout standardmäßig (in gewissem Maße) und andere Garantien über die automatische Spülung der Puffer , wenn die Anwendung geschlossen wird .
C garantiert, dass ein normaler Ausgang die Puffer leeren wird . "Normaler Ausstieg" bedeutet, dass er
exit
aufgerufen wird - entweder explizit oder durch Rückkehr vonmain
. Ein abnormaler Ausgang kann diesen Aufruf jedoch umgehen (und daher nicht gelöschte Puffer zurücklassen).Hier ist ein einfaches Beispiel:
Wenn Sie dies kompilieren und ausführen,
test
wird es nicht unbedingt in stdout geschrieben.Andere Programmiersprachen bieten noch weniger Garantien: Java wird beispielsweise beim Beenden des Programms nicht automatisch gelöscht . Wenn der Ausgabepuffer eine nicht abgeschlossene Zeile enthält, kann er daher verloren gehen, sofern
System.out.flush()
er nicht explizit aufgerufen wurde.Wie gesagt, Ihre Frage Körper fragt etwas etwas anders: Wenn die Daten in der Datei kommt überhaupt , sollte es tun so unmittelbar nach dem Befehl endet (vorbehaltlich der Einschränkungen in den anderen Antworten beschrieben).
quelle
write()
oderpwrite()
Systemaufrufe ausgeführt , und dann werden die Dateiänderungen sichtbar. Die letzte Dateiänderung erfolgt also definitiv vor dem Prozessabschluss, spätestens jedoch unmittelbar davor. Ich denke, selbst bei einer Datei gibt es keine Möglichkeit, die Beendigung des Prozesses zu beobachten, bevor nicht alle Dateiänderungen vorgenommen wurden.mmap(MAP_SHARED)
Ich denke, dass noch keine Frage dieses Problem ausreichend anspricht:
Wie die anderen Antworten erklären, leert ein gut funktionierendes Programm seine internen Dateipuffer, bevor der Prozess normal beendet wird . Danach verbleiben die Daten möglicherweise noch in Kernel- oder Hardware-Puffern, bevor sie in den dauerhaften Speicher geschrieben werden. Die Dateisystemsemantik von Linux garantiert jedoch, dass alle Prozesse den Inhalt von Dateien auf dieselbe Weise sehen wie der Kernel, einschließlich der internen Puffer 1 .
Dies wird normalerweise implementiert, indem höchstens ein Kernel-Puffer pro Dateiobjekt vorhanden ist und der gesamte Dateizugriff auf diesen Puffer erforderlich ist.
Wenn ein Prozess eine Datei liest, zeigt der Kernel dem Prozess den Pufferinhalt an, wenn sich der angeforderte Dateiteil derzeit im Puffer befindet. Ist dies nicht der Fall, ruft der Kernel die Daten vom zugrunde liegenden Speichermedium ab und legt sie in den Puffer. Kehren Sie dann zum vorherigen Schritt zurück.
Wenn ein Prozess in eine Datei schreibt, werden die Daten zuerst in den kernelinternen Puffer für diese Datei gestellt. Schließlich wird der Pufferinhalt in den Speicher gespült. In der Zwischenzeit wird der Lesezugriff aus demselben Puffer ausgeführt (siehe oben).
1 Zumindest für normale Dateien, Verzeichnisse und symbolische Links. FIFOs und Sockets sind eine andere Sache, da ihr Inhalt sowieso nie dauerhaft gespeichert wird. Es gibt einige Sonderfälle für reguläre Dateien, deren Inhalt davon abhängt, wer fragt. Beispiele sind Dateien in procfs und sysfs (denken Sie,
/proc/self
dies ist eine symbolische Verknüpfung zur Prozess-ID des Prozesses, der die symbolische Verknüpfung liest).quelle
mmap()
Dinge mit und O_DIRECT macht, was dazu führen kann, dass Dinge zwischen der Festplatte und dem Seiten-Cache nicht mehr synchron sind (aber das wird den Moment beheben, in dem der Prozess, der dies tut, beendet wird).Angenommen, Ihr Befehl wird von einem Programm ausgeführt, das die C-Laufzeitbibliothek verwendet, und sollte zu einem bestimmten Zeitpunkt aufgerufen werden
fclose
, um die geöffnete Datei zu schließen.Die Manpage für die
fclose
C-Funktion lautet:und die Manpage für
fflush
hat den gleichen Vermerk. Die Manpage fürclose
sagt:Beachten Sie, dass die Daten auch dann für andere Prozesse verfügbar sind, wenn sie nicht mit dem Laufwerk synchronisiert sind. Vielleicht ist das schon gut genug für dich.
Wenn Sie Zweifel haben, schreiben Sie einen Test.
quelle
close()
Syscall verwenden, um den Deskriptor einer Datei zu schließen.close
Dateien vor (in Hacky - Programme , die auf Fehler nicht überprüfen) austritt; Der Kernel räumt sie auf und ruftclose
Sie nach dem Abbruch Ihres Prozesses auf. Sie müssenfclose
jedoch gepufferte stdio-Streams verwenden oder libc dies für Sie erledigen lassenexit(3)
, im Gegensatz zum direkten Aufruf des Exitsystems.Ja. Die Shell öffnet die Ausgabedatei und
echo
gibt diese direkt aus. Nachdem der Befehl beendet wurde, ist er fertig.Ob sich die Daten bereits auf dem Datenträger befinden, ist eine andere Frage, die nur bei einem späteren Hardwarefehler von Bedeutung ist, oder ob Sie die Live-Partition mit einer forensischen Software unter Umgehung des bereitgestellten Dateisystems überprüfen.
Keine Sorge, der Kernel behält nur eine Ansicht der Datei, unabhängig davon, wie oft sie geöffnet wird.
quelle
mmap(MAP_SHARED)
: speicher in der mmap-region stimmen nicht mit lesevorgängen der datei überein (von diesem thread oder anderen prozessen). Deshalbmsync(2)
gibt es. Zumindest warnen die Manpages davor; Abhängig von der Implementierung ordnet Linux möglicherweise tatsächlich physische Seiten aus dem Pagecache zu. In diesem Fall würde ich vermuten, dass dies im Grunde genommen kohärent ist (Modulo-Speicherreihenfolge). Wie auch immer, es passiert immer noch alles vorher_exit(2)
.In der Regel werden alle Daten, die dem Kernel gehören, vom Kernel-Zeitraum verwaltet und bereinigt. Solche Daten umfassen Daten, die durch einen Systemaufruf, wie z
write(2)
.Allerdings, wenn Ihre Anwendung (zB C - Bibliothek) führt auf Pufferung oben von diesem, dann der Kern offensichtlich keine Ahnung hat , und daher bietet keine Garantie für ihre Reinigung.
Außerdem glaube ich nicht, dass es für die Bereinigung eine Zeitgarantie gibt - sie wird im Allgemeinen nach "bestem Vermögen" durchgeführt (sprich: "wenn ich eine Sekunde Zeit habe").
quelle
waitpid()
Rückkehr eines übergeordneten Prozesses eine Bereinigung / Pufferbereinigung erfolgt , sofern die Bereinigung überhaupt erfolgt. Das heißt, andere Prozesse können den Prozessabschluss nicht direkt beobachten, bevor von diesem Prozess vorgenommene Dateiänderungen vorgenommen wurden. (Ich sagte "direkt", um indirekte Beobachtung durch NFS-Datei-Zeitstempel auszuschließen, da das NFS-Caching zwischen Hosts nicht perfekt kohärent ist.)fsync
/fdatasync
, obwohl das Zurückschreiben des Puffers unter Linux nach/proc/sys/vm/dirty_writeback_centisecs
Hundertstelsekunden beginnt (sofern nicht durch anderen E / A-Verkehr verzögert), und verschiedene andere Tunables in diesem procfs-Verzeichnis ebenfalls Auswirkungen haben (z. B. wie) groß, um Puffer vor dem Zurückschreiben wachsen zu lassen).Nein, das gibt es nicht.
Sie können den endgültigen Inhalt der Datei direkt nach dem Beenden des Befehls lesen. Stattdessen wird die leere Datei nie gelesen. (Verwenden Sie in C und C ++ die Systemaufrufe wait , waitpid , wait3 oder wait4 , um auf das Beenden des Programms zu warten und erst dann die Datei zu lesen. Wenn Sie eine Shell, eine andere Programmiersprache oder eine Bibliothek (z. B. die C-Bibliothek) verwenden call system oder die Java Process- Klasse) verwendet wahrscheinlich bereits einen dieser Systemaufrufe.)
Wie andere Antworten und Kommentare gezeigt haben, können Sie nach dem Beenden des Programms eine leere Datei lesen, wenn das Programm beendet wurde, ohne die internen Ausgabepuffer zu leeren (z. B. wegen _exit , Abbruch oder Empfang eines schwerwiegenden Signals oder weil dies der Fall ist) ein Java-Programm, das normal beendet wird). An dieser Stelle können Sie jedoch nichts dagegen tun: Die nicht gelöschten Daten gehen für immer verloren, zusätzliche Wartezeiten stellen sie nicht wieder her.
quelle
Ja
Es tut mir leid, dass Sie vielleicht eine weitere überflüssige Antwort hinzugefügt haben, aber die meisten scheinen sich auf den roten Hering des Titels der Frage zu konzentrieren. Aber soweit ich das beurteilen kann, geht es bei der Frage überhaupt nicht um Pufferung, sondern um Folgendes:
Ja, bedingungslos. Die Verwendung von ">", die Sie beschreiben, zusammen mit "|" und "<" ist das Pipe-basierte Verarbeitungsmodell, auf dem die Unix- und Linux-Welt stark basiert. In Abhängigkeit von diesem Verhalten werden Sie in jeder Linux-Installation Hunderte, wenn nicht Tausende von Skripten finden.
Es funktioniert so, wie Sie es möchten, und wenn es auch nur die geringste Chance für einen Rennzustand gegeben hätte, wäre dies wahrscheinlich vor Jahrzehnten behoben worden.
quelle