Obwohl die Frage zum Stapelüberlauf zunächst ausreichend zu sein schien, verstehe ich aus Ihren Kommentaren, warum Sie möglicherweise noch Zweifel daran haben. Für mich ist dies genau die Art von kritischer Situation , in der die beiden UNIX-Subsysteme (Prozesse und Dateien) kommunizieren.
Wie Sie vielleicht wissen, werden UNIX-Systeme normalerweise in zwei Subsysteme unterteilt: das Dateisubsystem und das Prozesssubsystem. Sofern dies nicht durch einen Systemaufruf anders angegeben wird, sollte der Kernel diese beiden Subsysteme nicht miteinander interagieren lassen. Es gibt jedoch eine Ausnahme: das Laden einer ausführbaren Datei in die Textbereiche eines Prozesses . Natürlich kann man argumentieren, dass diese Operation auch durch einen Systemaufruf ( execve
) ausgelöst wird , aber dies ist normalerweise der einzige Fall, in dem das Prozesssubsystem eine implizite Anforderung an das Dateisubsystem sendet.
Da das Prozesssubsystem natürlich keine Möglichkeit hat, Dateien zu verarbeiten (andernfalls wäre es sinnlos, das Ganze in zwei Teile zu teilen), muss es alles verwenden, was das Dateisubsystem für den Zugriff auf Dateien bereitstellt. Dies bedeutet auch, dass das Prozesssubsystem jeder Maßnahme unterzogen wird, die das Dateisubsystem hinsichtlich der Ausgabe / Löschung von Dateien ergreift. In diesem Punkt würde ich empfehlen, Gilles 'Antwort auf diese U & L-Frage zu lesen . Der Rest meiner Antwort basiert auf dieser allgemeineren von Gilles.
Das erste, was beachtet werden sollte, ist, dass auf Dateien intern nur über Inodes zugegriffen werden kann . Wenn dem Kernel ein Pfad zugewiesen wird, besteht sein erster Schritt darin, ihn in einen Inode zu übersetzen, der für alle anderen Operationen verwendet wird. Wenn ein Prozess eine ausführbare Datei in den Speicher lädt, erfolgt dies über den Inode, der vom Dateisubsystem nach der Übersetzung eines Pfads bereitgestellt wurde. Inodes können mehreren Pfaden (Links) zugeordnet sein, und Programme können nur Links löschen. Um eine Datei und ihren Inode zu löschen, muss userland alle vorhandenen Links zu diesem Inode entfernen und sicherstellen, dass sie vollständig nicht verwendet wird. Wenn diese Bedingungen erfüllt sind, löscht der Kernel die Datei automatisch von der Festplatte.
Wenn Sie sich den Teil zum Ersetzen ausführbarer Dateien in Gilles 'Antwort ansehen, werden Sie feststellen , dass der Kernel je nachdem, wie Sie die Datei bearbeiten / löschen , unterschiedlich reagiert / sich anpasst, immer über einen im Dateisubsystem implementierten Mechanismus.
- Wenn Sie Strategie 1 ausprobieren ( Öffnen / Abschneiden auf Null / Schreiben oder Öffnen / Schreiben / Abschneiden auf neue Größe ), werden Sie feststellen, dass der Kernel Ihre Anfrage nicht bearbeitet. Sie erhalten eine Fehlermeldung 26: Textdatei beschäftigt (
ETXTBSY
). Keine Konsequenzen.
- Wenn Sie Strategie zwei versuchen, besteht der erste Schritt darin, Ihre ausführbare Datei zu löschen. Da es jedoch von einem Prozess verwendet wird, wird das Dateisubsystem aktiviert und verhindert, dass die Datei (und ihr Inode) wirklich von der Festplatte gelöscht wird. Ab diesem Zeitpunkt besteht die einzige Möglichkeit, auf den Inhalt der alten Datei zuzugreifen, darin, dies über den Inode zu tun. Dies geschieht, wenn das Prozesssubsystem neue Daten in Textabschnitte laden muss (intern ist es sinnlos, Pfade zu verwenden, außer bei der Übersetzung in Inodes). Auch wenn Sie nicht verbunden sindWenn die Datei alle Pfade entfernt hat, kann der Prozess sie weiterhin verwenden, als hätten Sie nichts getan. Das Erstellen einer neuen Datei mit dem alten Pfad ändert nichts: Die neue Datei erhält einen völlig neuen Inode, von dem der laufende Prozess nichts weiß.
Die Strategien 2 und 3 sind auch für ausführbare Dateien sicher: Obwohl das Ausführen von ausführbaren Dateien (und dynamisch geladenen 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.
- Strategie drei ist ziemlich ähnlich, da
mv
es sich um eine atomare Operation handelt. Dies erfordert wahrscheinlich die Verwendung des rename
Systemaufrufs. Da Prozesse im Kernelmodus nicht unterbrochen werden können, kann nichts diesen Vorgang stören, bis er abgeschlossen ist (erfolgreich oder nicht). Auch hier gibt es keine Änderung des Inodes der alten Datei: Es wird ein neuer erstellt, und bereits ausgeführte Prozesse wissen nichts davon, selbst wenn er mit einem der Links des alten Inodes verknüpft ist.
Bei Strategie 3 wird beim Verschieben der neuen Datei in 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 oder der neue Inhalt angezeigt - es besteht kein Risiko, dass gemischte Inhalte oder die Datei nicht angezeigt werden bestehender.
Neukompilieren einer Datei : Wenn Sie verwenden gcc
(und das Verhalten ist wahrscheinlich für viele andere Compiler ähnlich), verwenden Sie Strategie 2. Sie können dies sehen, indem Sie einen strace
der Prozesse Ihres Compilers ausführen:
stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
- Der Compiler erkennt durch die Systemaufrufe
stat
und lstat
, dass die Datei bereits vorhanden ist .
- Die Datei ist nicht verbunden . Während der Zugriff über den Namen nicht mehr möglich ist
a.out
, verbleiben Inode und Inhalt auf der Festplatte, solange sie von bereits ausgeführten Prozessen verwendet werden.
- Eine neue Datei wird erstellt und unter dem Namen ausführbar gemacht
a.out
. Dies ist eine brandneue Inode und brandneue Inhalte, die bereits laufende Prozesse nicht interessieren.
Wenn es nun um gemeinsam genutzte Bibliotheken geht, gilt das gleiche Verhalten. Solange ein Bibliotheksobjekt von einem Prozess verwendet wird, wird es nicht von der Festplatte gelöscht, unabhängig davon, wie Sie seine Links ändern. Immer wenn etwas in den Speicher geladen werden muss, führt der Kernel dies über den Inode der Datei durch und ignoriert daher die Änderungen, die Sie an den Links vorgenommen haben (z. B. das Verknüpfen mit neuen Dateien).