Was passiert, wenn eine Datei, die zu 100% in den Seiten-Cache ausgelagert ist, von einem anderen Prozess geändert wird?

14

Ich weiß, dass eine Seiten-Cache-Seite, wenn sie geändert wird, als verschmutzt markiert ist und ein Zurückschreiben erfordert, aber was passiert, wenn:

Szenario: Die Datei / apps / EXE, bei der es sich um eine ausführbare Datei handelt, wird vollständig im Seiten-Cache abgelegt (alle Seiten befinden sich im Cache / Speicher) und von Prozess P ausgeführt

Die fortlaufende Veröffentlichung ersetzt dann / apps / EXE durch eine brandneue ausführbare Datei.

Annahme 1: Ich gehe davon aus, dass der Prozess P (und jeder andere mit einem Dateideskriptor, der auf die alte ausführbare Datei verweist) weiterhin den alten, im Speicher / in Apps / in der EXE-Datei ohne Probleme verwendet und dass jeder neue Prozess, der versucht, diesen Pfad auszuführen, diesen Pfad erhält die neue ausführbare Datei.

Annahme 2: Ich gehe davon aus, dass, wenn nicht alle Seiten der Datei in den Speicher gemappt sind, die Dinge in Ordnung sind, bis ein Seitenfehler vorliegt, der Seiten aus der Datei erfordert, die ersetzt wurden, und wahrscheinlich ein Fehler auftritt.

Frage 1: Wenn Sie alle Seiten der Datei mit so etwas wie vmtouch blockieren, ändert sich dadurch das Szenario überhaupt?

Frage 2: Wenn sich / apps / EXE auf einem entfernten NFS befindet, würde das einen Unterschied machen? (Nehme ich nicht an)

Bitte korrigieren oder validieren Sie meine 2 Annahmen und beantworten Sie meine 2 Fragen.

Nehmen wir an, dies ist eine CentOS 7.6-Box mit einer Art 3.10.0-957.el7-Kernel

Update: Wenn ich weiter darüber nachdenke, frage ich mich, ob dieses Szenario nicht anders ist als jedes andere Dirty-Page-Szenario.

Ich nehme an, dass der Prozess, der die neue Binärdatei schreibt, einen Lesevorgang durchführt und alle Cache-Seiten abruft, da alle Seiten ausgelagert sind, und dann alle diese Seiten als verschmutzt markiert werden. Wenn sie gesperrt sind, handelt es sich nur um nutzlose Seiten, die den Hauptspeicher belegen, nachdem die Referenzanzahl auf Null gesetzt wurde.

Ich vermute, wenn die aktuell ausgeführten Programme enden, wird alles andere die neue Binärdatei verwenden. Vorausgesetzt, dass alles in Ordnung ist, denke ich, dass es nur interessant ist, wenn nur ein Teil der Datei ausgelagert wird.

Gregg Leventhal
quelle
Nur um es zu erläutern, ist das Ersetzen einer Datei keine große Sache (abhängig davon, ob sie von der Anwendung erneut geöffnet wird und wie die Anwendung auf geänderten Inhalt reagiert), aber das Ändern von MMAP-Dateien kann zum Absturz von Anwendungen führen (dies ist ein häufiges Problem) in der java-welt, wenn eine zip-datei mit einem mmap-verzeichniseintrag geändert wird). Es hängt jedoch von der Plattform ab, es kann nicht garantiert werden, dass die mmap-Bereiche die Änderung sehen oder nicht.
Eckes

Antworten:

12

Die fortlaufende Veröffentlichung ersetzt dann / apps / EXE durch eine brandneue ausführbare Datei.

Dies ist der wichtige Teil.

Die Art und Weise, wie eine neue Datei veröffentlicht wird, besteht darin, eine neue Datei zu erstellen (z. B. /apps/EXE.tmp.20190907080000), den Inhalt zu schreiben, Berechtigungen und Eigentumsrechte festzulegen und sie schließlich in den endgültigen Namen umzubenennen (2) /apps/EXEund die alte Datei zu ersetzen.

Das Ergebnis ist, dass die neue Datei eine neue Inode-Nummer hat (was praktisch bedeutet, dass es sich um eine andere Datei handelt).

Und die alte Datei hatte eine eigene Inode-Nummer, die eigentlich immer noch existiert, obwohl der Dateiname nicht mehr darauf verweist (oder es gibt keine Dateinamen mehr, die auf diese Inode verweisen).

Der Schlüssel hier ist also, dass wenn wir unter Linux von "Dateien" sprechen, wir meistens wirklich von "Inodes" sprechen, da nach dem Öffnen einer Datei der Inode der Verweis ist, den wir auf die Datei behalten.

Annahme 1 : Ich gehe davon aus, dass der Prozess P (und jeder andere mit einem Dateideskriptor, der auf die alte ausführbare Datei verweist) weiterhin den alten, im Speicher / in Apps / in der EXE-Datei ohne Probleme verwendet und jeder neue Prozess, der versucht, diesen Pfad auszuführen, diesen Pfad erhält die neue ausführbare Datei.

Richtig.

Annahme 2 : Ich gehe davon aus, dass, wenn nicht alle Seiten der Datei in den Speicher gemappt sind, die Dinge in Ordnung sind, bis ein Seitenfehler vorliegt, der Seiten aus der Datei erfordert, die ersetzt wurden, und wahrscheinlich ein Fehler auftritt.

Falsch. Der alte Inode ist noch vorhanden, sodass Seitenfehler aus dem Prozess, der die alte Binärdatei verwendet, diese Seiten weiterhin auf der Festplatte finden können.

Sie können einige Auswirkungen davon feststellen, indem Sie sich den /proc/${pid}/exeSymlink (oder die entsprechende lsofAusgabe) für den Prozess ansehen, in dem die alte Binärdatei ausgeführt wird. Dieser zeigt /app/EXE (deleted)an, dass der Name nicht mehr vorhanden ist, der Inode jedoch noch vorhanden ist.

Sie können auch sehen, dass der von der Binärdatei verwendete Speicherplatz erst freigegeben wird, nachdem der Prozess beendet wurde (vorausgesetzt, es ist der einzige Prozess, bei dem dieser Inode geöffnet ist.) Überprüfen Sie die Ausgabe von dfvor und nach dem Beenden des Prozesses, um die Größe zu verringern von dieser alten Binärdatei dachten Sie, dass es sie nicht mehr gibt.

Übrigens, dies gilt nicht nur für Binärdateien, sondern auch für alle offenen Dateien. Wenn Sie eine Datei in einem Prozess öffnen und die Datei entfernen, bleibt die Datei auf der Festplatte, bis dieser Prozess die Datei schließt (oder stirbt) Der Dateisystemtreiber (im Linux-Kernel) protokolliert, wie viele Verweise auf diesen Inode im Speicher vorhanden sind , und gibt den Inode erst von der Festplatte frei, wenn auch alle Verweise vom laufenden System freigegeben wurden.

Frage 1 : Wenn Sie alle Seiten der Datei mit so etwas wie vmtouch blockieren, ändert sich das Szenario

Diese Frage basiert auf der falschen Annahme 2, dass das Nicht-Sperren der Seiten zu Fehlern führt. Das wird es nicht.

Frage 2 : Wenn sich / apps / EXE auf einem entfernten NFS befindet, würde das einen Unterschied machen? (Nehme ich nicht an)

Es soll auf die gleiche Art und Weise funktionieren, aber es gibt einige "Fallstricke" mit NFS.

Manchmal können Sie die Artefakte beim Löschen einer Datei sehen, die noch in NFS geöffnet ist (wird in diesem Verzeichnis als versteckte Datei angezeigt).

Sie können NFS-Exporten auch Gerätenummern zuweisen, um sicherzustellen, dass diese beim Neustart des NFS-Servers nicht neu gemischt werden.

Die Grundidee ist jedoch dieselbe. Der NFS-Client-Treiber verwendet weiterhin Inodes und versucht, Dateien (auf dem Server) zu behalten, während auf den Inode verwiesen wird.

filbranden
quelle
1
Blockiert das Umbenennen (2), bis der Referenzzähler der Datei mit dem alten Namen auf Null geht?
Gregg Leventhal
2
Nein, Rename (2) wird nicht blockieren. Die alte Inode wird möglicherweise sehr lange aufbewahrt.
Filbranden
1
Lesen Sie die Antwort von @ mosvy, warum Sie nicht in eine Datei schreiben können, die gerade ausgeführt wird (Sie erhalten ETXTBSY). Das Aufheben der Verknüpfung und das Erstellen neuer Verknüpfungen hat den gleichen Effekt wie das Umbenennen: Sie erhalten eine neue Inode. (Umbenennen ist besser, weil es dann keinen Moment gibt, in dem der Dateiname nicht existiert. Es ist eine atomare Operation, die den Namen ersetzt, um auf den neuen Inode zu verweisen.)
filbranden
4
@GreggLeventhal: "Welche Annahme treffen Sie über den von mir verwendeten kontinuierlichen Veröffentlichungsprozess, der sicherstellt, dass temporäre Dateien verwendet werden?" - Denn solange es Unix gibt, war und ist dies der einzig vernünftige Weg. renameDies ist so ziemlich die einzige Datei- und Dateisystemoperation, die garantiert atomar ist (vorausgesetzt, wir überschreiten weder Dateisystem- noch Gerätegrenzen). Daher ist "temporäre Datei erstellen und dann rename" das Standardmuster zum Aktualisieren von Dateien. Dies ist zum Beispiel auch das, was jeder Texteditor unter Unix verwendet.
Jörg W Mittag
1
@ grahamj42: renameist Teil von POSIX. Zugegeben, es ist in Bezug auf ISO C enthalten (Abschnitt 7.21.4.2 im aktuellen Entwurf), aber es ist dort enthalten.
Jörg W Mittag
6

Annahme 2: Ich gehe davon aus, dass, wenn nicht alle Seiten der Datei in den Speicher gemappt sind, die Dinge in Ordnung sind, bis ein Seitenfehler vorliegt, der Seiten aus der Datei erfordert, die ersetzt wurden, und wahrscheinlich ein Fehler auftritt.

Nein, das wird nicht passieren, weil der Kernel nicht zulässt, dass Sie in einer Datei, die gerade ausgeführt wird, irgendetwas zum Schreiben und Ersetzen öffnen. Eine solche Aktion schlägt fehl mit ETXTBSY[1]:

cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy

Wenn dpkg usw. eine Binärdatei aktualisiert, wird sie nicht überschrieben, sondern verwendet rename(2), um den Verzeichniseintrag einfach auf eine völlig andere Datei zu verweisen, und alle Prozesse, die noch Zuordnungen oder offene Handles zur alten Datei haben, verwenden sie weiterhin ohne Probleme .

[1] Ein solcher Schutz gilt nicht für andere Dateien, die ebenfalls als "Text" (Live-Code / ausführbare Datei) betrachtet werden können: gemeinsam genutzte Bibliotheken, Java-Klassen usw .; Das Ändern einer solchen Datei, während sie von einem anderen Prozess zugeordnet wurde, führt zum Absturz. Unter Linux übergibt der Dynamic Linker die MAP_DENYWRITEFlagge pflichtbewusst an mmap(2), macht aber keinen Fehler - er hat keinerlei Auswirkungen.

Mosvy
quelle
1
Wann ist die Umbenennung im dpkg-Szenario abgeschlossen, sodass der Eintrag für / apps / EXE auf den Inode der neuen Binärdatei verweist? Wann gibt es keine Verweise mehr auf die alte? Wie funktioniert das?
Gregg Leventhal
2
rename(2)ist atomar; Sobald es abgeschlossen ist, verweist der Verzeichniseintrag auf die neue Datei. Die Prozesse, die zu diesem Zeitpunkt noch die alte Datei verwendeten, konnten nur über vorhandene Zuordnungen oder über offene Handles darauf zugreifen (die möglicherweise auf einen Waisen-Dentry verweisen, auf den nur noch über zugegriffen werden kann /proc/PID/fd).
Mosvy
1
Ihre Antwort gefällt mir am besten, weil ich aufgrund Ihrer ETXTBSY-Erwähnung auf utcc.utoronto.ca/~cks/space/blog/unix/WhyTextFileBusyError gestoßen bin, das alle meine Fragen beantwortet.
Gregg Leventhal
4

Die Antwort von filbranden ist richtig, vorausgesetzt, der kontinuierliche Release-Prozess ersetzt die Dateien ordnungsgemäß über rename. Wenn dies nicht der Fall ist, die Datei jedoch direkt geändert wird, liegen die Dinge anders. Ihr mentales Modell ist jedoch immer noch falsch.

Es besteht keine Möglichkeit, dass Dinge auf der Festplatte geändert werden und mit dem Seiten-Cache inkonsistent sind, da der Seiten-Cache die kanonische Version ist und die Version , die geändert wurde. Alle Schreibvorgänge in eine Datei erfolgen über den Seiten-Cache. Wenn es dort bereits vorhanden ist, werden die vorhandenen Seiten geändert. Wenn es noch nicht vorhanden ist, wird beim Versuch, eine Teilseite zu ändern, die gesamte Seite zwischengespeichert, gefolgt von einer Änderung, als wäre sie bereits zwischengespeichert. Schreibvorgänge, die sich über eine ganze Seite oder mehr erstrecken, können den Leseschritt, in dem sie eingelesen werden, mit ziemlicher Sicherheit optimieren. In jedem Fall gibt es nur eine kanonisch änderbare Version einer Datei (*), diejenige im Seiten-Cache .

(*) Ich habe leicht gelogen. Bei NFS und anderen Remote-Dateisystemen kann es mehr als ein Dateisystem geben, und diese implementieren in der Regel (je nachdem, welches und welche Mount- und serverseitigen Optionen verwendet werden) die Atomarität und Ordnungssemantik für Schreibvorgänge nicht korrekt. Aus diesem Grund betrachten viele von uns sie als grundlegend kaputt und lehnen es ab, sie in Situationen zu verwenden, in denen Schreibvorgänge gleichzeitig mit der Verwendung erfolgen.

R .. GitHub STOP ICE HILFE
quelle