Ersetzen von Dateien im Allgemeinen
Erstens gibt es verschiedene Strategien, um eine Datei zu ersetzen:
Öffnen Sie die vorhandene Datei zum Schreiben, kürzen Sie sie auf die Länge 0 und schreiben Sie den neuen Inhalt. (Eine seltenere Variante besteht darin, die vorhandene Datei zu öffnen, den alten Inhalt mit dem neuen Inhalt zu überschreiben und die Datei auf die neue Länge zu kürzen, wenn sie kürzer ist.) In Shell-Begriffen:
echo 'new content' >somefile
Entfernen Sie die alte Datei und erstellen Sie eine neue Datei mit demselben Namen. In Shell-Begriffen:
rm somefile
echo 'new content' >somefile
Schreiben Sie in eine neue Datei unter einem temporären Namen und verschieben Sie die neue Datei an den vorhandenen Namen. Durch das Verschieben wird die alte Datei gelöscht. In Shell-Begriffen:
echo 'new content' >somefile.new
mv somefile.new somefile
Ich werde nicht alle Unterschiede zwischen den Strategien auflisten, ich werde nur einige erwähnen, die hier wichtig sind. Wenn in Strategie 1 derzeit ein Prozess die Datei verwendet, wird der neue Inhalt beim Aktualisieren angezeigt. Dies kann zu Verwirrung führen, wenn der Prozess erwartet, dass der Dateiinhalt gleich bleibt. Beachten Sie, dass es sich nur um Prozesse handelt, bei denen die Datei geöffnet ist (wie in lsof
oder in sichtbar) . Interaktive Anwendungen, bei denen ein Dokument geöffnet ist (z. B. Öffnen einer Datei in einem Editor), lassen die Datei normalerweise nicht geöffnet. Sie laden den Dateiinhalt während der Der Vorgang "Dokument öffnen" und sie ersetzen die Datei (unter Verwendung einer der oben genannten Strategien) während des Vorgangs "Dokument speichern"./proc/PID/fd/
Bei den Strategien 2 und 3 somefile
bleibt die alte Datei während der Inhaltsaktualisierung geöffnet , wenn bei einem Prozess die Datei geöffnet ist. Bei Strategie 2 wird beim Entfernen der Datei tatsächlich nur der Eintrag der Datei im Verzeichnis entfernt. Die Datei selbst wird nur entfernt, wenn kein Verzeichniseintrag zu ihr führt (auf typischen Unix-Dateisystemen kann es mehr als einen Verzeichniseintrag für dieselbe Datei geben ) und kein Prozess hat sie geöffnet. Hier ist eine Möglichkeit, dies zu beobachten: Die Datei wird nur entfernt, wenn der sleep
Prozess beendet wird ( rm
nur der Verzeichniseintrag wird entfernt).
echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .
Bei Strategie 3 wird beim Verschieben der neuen Datei auf den vorhandenen Namen der Verzeichniseintrag entfernt, der zum alten Inhalt führt, und ein Verzeichniseintrag erstellt, der zum neuen Inhalt führt. Dies geschieht in einer atomaren Operation, daher hat diese Strategie einen großen Vorteil: Wenn ein Prozess die Datei zu einem beliebigen Zeitpunkt öffnet, wird entweder der alte Inhalt oder der neue Inhalt angezeigt - es besteht kein Risiko, dass gemischte Inhalte angezeigt werden, oder die Datei nicht bestehender.
Ausführbare Dateien ersetzen
Wenn Sie Strategie 1 mit einer laufenden ausführbaren Datei unter Linux versuchen, wird eine Fehlermeldung angezeigt.
cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy
Eine „Textdatei“ ist eine Datei, die aus unklaren historischen Gründen ausführbaren Code enthält . Linux weigert sich, wie viele andere Unix-Varianten, den Code eines laufenden Programms zu überschreiben. Einige Unix-Varianten erlauben dies und führen zu Abstürzen, es sei denn, der neue Code ist eine sehr durchdachte Modifikation des alten Codes.
Unter Linux können Sie den Code einer dynamisch geladenen Bibliothek überschreiben. Es ist wahrscheinlich, dass das Programm, das es verwendet, abstürzt. (Möglicherweise können Sie dies nicht beobachten, sleep
da der gesamte Bibliothekscode geladen wird, der beim Start benötigt wird. Probieren Sie ein komplexeres Programm aus, das nach dem Schlafengehen etwas Nützliches ausführt, z perl -e 'sleep 9; print lc $ARGV[0]'
. B.. )
Wenn ein Interpreter ein Skript ausführt, wird die Skriptdatei auf normale Weise vom Interpreter geöffnet, sodass kein Schutz gegen das Überschreiben des Skripts besteht. Einige Interpreter lesen und analysieren das gesamte Skript, bevor sie mit der Ausführung der ersten Zeile beginnen, andere lesen das Skript nach Bedarf. Siehe Was passiert, wenn Sie ein Skript während der Ausführung bearbeiten? und wie geht Linux mit Shell-Skripten um? für mehr Details.
Die Strategien 2 und 3 sind auch für ausführbare Dateien sicher: Obwohl ausführbare Dateien (und dynamisch geladene Bibliotheken) keine offenen Dateien im Sinne eines Dateideskriptors sind, verhalten sie sich sehr ähnlich. Solange ein Programm den Code ausführt, bleibt die Datei auch ohne Verzeichniseintrag auf der Festplatte.
Aktualisieren einer Anwendung
Die meisten Paketmanager verwenden Strategie 3, um Dateien zu ersetzen, da der oben erwähnte große Vorteil darin besteht, dass das Öffnen der Datei zu jedem Zeitpunkt zu einer gültigen Version führt.
Anwendungs-Upgrades können zu Problemen führen, wenn das Upgrade einer einzelnen Datei nur aus einzelnen Dateien besteht (Programm, Bibliotheken, Daten usw.). Betrachten Sie die folgende Abfolge von Ereignissen:
- Eine Instanz der Anwendung wird gestartet.
- Die Anwendung wird aktualisiert.
- Die ausgeführte Instanzanwendung öffnet eine ihrer Datendateien.
In Schritt 3 öffnet die ausgeführte Instanz der alten Version der Anwendung eine Datendatei aus der neuen Version. Ob dies funktioniert oder nicht, hängt von der Anwendung ab, um welche Datei es sich handelt und in welchem Umfang die Datei geändert wurde.
Nach einem Upgrade werden Sie feststellen, dass das alte Programm noch ausgeführt wird. Wenn Sie die neue Version ausführen möchten, müssen Sie das alte Programm beenden und die neue Version ausführen. Paketmanager beenden und starten Dämonen normalerweise bei einem Upgrade neu, lassen Endbenutzeranwendungen jedoch in Ruhe.
Einige Daemons haben spezielle Verfahren, um Upgrades durchzuführen, ohne den Daemon beenden und auf den Neustart der neuen Instanz warten zu müssen (was zu einer Dienstunterbrechung führt). Dies ist im Fall von init erforderlich , das nicht getötet werden kann. init-Systeme bieten eine Möglichkeit, den laufenden Instanzaufruf aufzufordern execve
, sich selbst durch die neue Version zu ersetzen.