Continuous Release ersetzt dann /apps/EXE durch eine brandneue ausführbare Datei.
Das ist der wichtige Teil.
Eine neue Datei wird freigegeben, indem eine neue Datei erstellt wird (z. B. /apps/EXE.tmp.20190907080000
), den Inhalt zu schreiben, Berechtigungen und Eigentumsrechte festzulegen und endlich Umbenennung(2) in den endgültigen Namen /apps/EXE
, ersetzt die alte Datei.
Das Ergebnis ist, dass die neue Datei eine neue Inode-Nummer hat (was tatsächlich bedeutet, dass es sich um eine andere Datei handelt.)
Und die alte Datei hatte ihre eigene Inode-Nummer, die eigentlich immer noch da ist obwohl der Dateiname nicht mehr darauf zeigt (oder es gibt keine Dateinamen mehr, die auf diesen Inode zeigen).
Der Schlüssel hier ist also, dass wir, wenn wir unter Linux von „Dateien“ sprechen, meistens wirklich von „Inodes“ sprechen, da, sobald eine Datei geöffnet wurde, der Inode die Referenz ist, die wir auf die Datei behalten.
Annahme 1 :Ich gehe davon aus, dass Prozess P (und jeder andere mit einem Dateideskriptor, der auf die alte ausführbare Datei verweist) weiterhin die alte, im Speicher befindliche /apps/EXE ohne Probleme verwendet, und jeder neue Prozess, der versucht, diesen Pfad auszuführen, erhält die neue ausführbar.
Richtig.
Annahme 2 :Ich gehe davon aus, dass, wenn nicht alle Seiten der Datei in den Speicher abgebildet werden, die Dinge in Ordnung sind, bis ein Seitenfehler auftritt, der Seiten aus der Datei erfordert, die ersetzt wurden, und wahrscheinlich ein Segfault auftritt?
Falsch. Der alte Inode ist immer noch vorhanden, sodass Seitenfehler aus dem Prozess, der die alte Binärdatei verwendet, immer noch in der Lage sein werden, diese Seiten auf der Festplatte zu finden.
Sie können einige Auswirkungen davon sehen, indem Sie sich /proc/${pid}/exe
ansehen Symlink (oder äquivalent lsof
Ausgabe) für den Prozess, der die alte Binärdatei ausführt, was /app/EXE (deleted)
anzeigt um anzuzeigen, dass der Name nicht mehr da ist, aber der Inode noch da ist.
Sie können auch sehen, dass der von der Binärdatei verwendete Speicherplatz erst freigegeben wird, nachdem der Prozess stirbt (vorausgesetzt, es ist der einzige Prozess mit diesem geöffneten Inode). Überprüfen Sie die Ausgabe von df
vor und nach dem Beenden des Prozesses werden Sie sehen, wie er um die Größe der alten Binärdatei schrumpft, von der Sie dachten, dass sie nicht mehr da ist.
Übrigens, das gilt nicht nur für Binärdateien, sondern 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). Dateisystemtreiber (im Linux-Kernel) hält einen Zähler darüber, wie viele Verweise auf diesen Inode im Speicher existieren , und wird den Inode erst dann von der Festplatte freigeben, wenn alle Referenzen vom laufenden System ebenfalls freigegeben wurden.
Frage 1 :Wenn Sie alle Seiten der Datei mit etwas wie vmtouch mlock, ändert das das Szenario
Diese Frage basiert auf der falschen Annahme 2, dass das Nicht-Sperren der Seiten Segfaults verursacht. Das wird es nicht.
Frage 2 :Wenn /apps/EXE auf einem Remote-NFS ist, würde das einen Unterschied machen? (Ich vermute nicht)
Es ist gemeint funktioniert auf die gleiche Weise und meistens funktioniert es auch, aber es gibt einige "Fallstricke" mit NFS.
Manchmal können Sie die Artefakte des Löschens einer Datei sehen, die noch in NFS geöffnet ist (erscheint als versteckte Datei in diesem Verzeichnis.)
Sie haben auch eine Möglichkeit, NFS-Exporten Gerätenummern zuzuweisen, um sicherzustellen, dass diese nicht "neu gemischt" werden, wenn der NFS-Server neu gestartet wird.
Aber die Grundidee ist dieselbe. Der NFS-Client-Treiber verwendet immer noch Inodes und versucht, Dateien (auf dem Server) zu behalten, während der Inode noch referenziert wird.
Annahme 2:Ich gehe davon aus, dass, wenn nicht alle Seiten der Datei in den Speicher abgebildet werden, die Dinge in Ordnung sind, bis ein Seitenfehler auftritt, der Seiten aus der Datei erfordert, die ersetzt wurden, und wahrscheinlich ein Segfault auftritt?
Nein, das wird nicht passieren, weil der Kernel es Ihnen nicht erlaubt, irgendetwas in einer gerade ausgeführten Datei zum Schreiben oder Ersetzen zu öffnen. Eine solche Aktion schlägt mit ETXTBSY
fehl [1][strong> :
cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy
Wenn dpkg usw. eine Binärdatei aktualisiert, überschreibt es sie nicht, sondern verwendet rename(2)
was den Verzeichniseintrag einfach auf eine völlig andere Datei verweist, und alle Prozesse, die noch Mappings oder offene Handles auf die alte Datei haben, werden sie problemlos weiterverwenden.
[1] die ETXBUSY
Der Schutz erstreckt sich nicht auf 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 abgebildet wird, wird den Prozess zum Absturz bringen. Unter Linux übergibt der dynamische Linker pflichtbewusst den MAP_DENYWRITE
Flag auf mmap(2)
, aber täuschen Sie sich nicht - es hat überhaupt keine Wirkung. Beispiel:
$ cc -xc - <<<'void lib(){}' -shared -o lib.so
$ cc -Wl,-rpath=. lib.so -include unistd.h -xc - <<<'
extern void lib();
int main(){ truncate("lib.so", 0); lib(); }
'
./a.out
Bus error
Die Antwort von Filbranden ist richtig, vorausgesetzt, der kontinuierliche Freigabeprozess führt eine ordnungsgemäße atomare Ersetzung von Dateien über rename
durch . Wenn dies nicht der Fall ist, sondern die Datei an Ort und Stelle 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 nicht mit dem Seiten-Cache übereinstimmen, da der Seiten-Cache die kanonische Version ist und die, die geändert wird. Alle Schreibvorgänge in eine Datei erfolgen über den Seitencache. Wenn es dort bereits vorhanden ist, werden die vorhandenen Seiten geändert. Wenn es noch nicht vorhanden ist, führen Versuche, eine Teilseite zu ändern, dazu, dass die gesamte Seite zwischengespeichert wird, gefolgt von einer Änderung, als ob sie bereits zwischengespeichert wäre. Schreibvorgänge, die sich über eine ganze Seite oder mehr erstrecken, können (und tun dies fast sicher) den Leseschritt, in dem sie eingefügt werden, optimieren. In jedem Fall existiert immer nur eine kanonisch modifizierbare Version einer Datei (*), die im Seitencache .
(*) Ich habe leicht gelogen. Für NFS und andere Remote-Dateisysteme kann es mehr als eines geben, und sie implementieren normalerweise (je nachdem, welches und welche Mount- und serverseitigen Optionen verwendet werden) die Atomarität und die Semantik für Schreibvorgänge nicht korrekt. Aus diesem Grund halten viele von uns sie für grundlegend kaputt und weigern sich, sie für Situationen zu verwenden, in denen gleichzeitig mit der Verwendung geschrieben wird.