Ich habe gerade festgestellt, dass ich ein laufendes aktives Programm in ein anderes Verzeichnis verschieben kann. Nach meiner Erfahrung war das in MacOs oder Windows nicht möglich. Wie funktioniert es in Ubuntu?
Bearbeiten: Ich dachte, es wäre nicht möglich auf dem Mac, aber anscheinend ist es möglich, wie Kommentare bestätigen. Es ist vielleicht nur unter Windows nicht möglich. Vielen Dank für alle Antworten.
rename(2)
unter OS X keine ausführbare Datei ausführen? Was passiert, verstehst duEBUSY
oder so? Warum funktioniert es nicht? Die Manpage rename (2) dokumentiertETXTBUSY
diesen Systemaufruf nicht und spricht nur davonEBUSY
, dass Verzeichnisumbenennungen möglich sind. Daher wusste ich nicht, dass ein POSIX-System das Umbenennen von ausführbaren Dateien überhaupt nicht zulassen könnte.Antworten:
Lass es mich zusammenfassen.
Wenn Sie eine ausführbare Datei ausführen, wird eine Folge von Systemaufrufen ausgeführt, vor allem
fork()
undexecve()
:fork()
Erstellt einen untergeordneten Prozess des aufrufenden Prozesses, bei dem es sich (meistens) um eine exakte Kopie des übergeordneten Prozesses handelt. Beide Prozesse führen weiterhin dieselbe ausführbare Datei aus (verwenden schreibgeschützte Speicherseiten, um effizient zu sein). Es wird zweimal zurückgegeben: Im übergeordneten Element wird die untergeordnete PID zurückgegeben. Im untergeordneten Element wird 0 zurückgegeben. Normalerweise werden die untergeordneten Prozessaufrufe sofort ausgeführt:execve()
Nimmt einen vollständigen Pfad zur ausführbaren Datei als Argument und ersetzt den aufrufenden Prozess durch die ausführbare Datei. Zu diesem Zeitpunkt erhält der neu erstellte Prozess seinen eigenen virtuellen Adressraum, dh virtuellen Speicher, und die Ausführung beginnt an seinem Einstiegspunkt (in einem Zustand, der in den Regeln von Plattform ABI für neue Prozesse festgelegt ist).Zu diesem Zeitpunkt hat der ELF-Loader des Kernels die Text- und Datensegmente der ausführbaren Datei in den Speicher abgebildet , als hätte er den
mmap()
Systemaufruf verwendet (mit gemeinsamen schreibgeschützten bzw. privaten Schreib- / Lesezuordnungen). Das BSS wird auch wie mit MAP_ANONYMOUS abgebildet. (Übrigens, der Einfachheit halber ignoriere ich hier die dynamische Verknüpfung: Der dynamische Linkeropen()
s undmmap()
s alle dynamischen Bibliotheken, bevor Sie zum Einstiegspunkt der ausführbaren Hauptdatei springen.)Nur wenige Seiten werden tatsächlich von der Festplatte in den Speicher geladen, bevor ein new-exec () ed seinen eigenen Code ausführt. Weitere Seiten werden nach Bedarf eingeblendet , wenn der Prozess diese Teile seines virtuellen Adressraums berührt. (Das Vorladen von Code- oder Datenseiten vor dem Ausführen von User-Space-Code ist nur eine Leistungsoptimierung.)
Die ausführbare Datei wird durch den Inode auf der unteren Ebene identifiziert. Nachdem die Ausführung der Datei gestartet wurde, behält der Kernel den Dateiinhalt anhand des Inode-Verweises bei, nicht anhand des Dateinamens, wie dies bei offenen Dateideskriptoren oder dateibasierten Speicherzuordnungen der Fall ist. So können Sie die ausführbare Datei problemlos an einen anderen Speicherort des Dateisystems oder sogar auf ein anderes Dateisystem verschieben. Als Randnotiz, um die verschiedenen Statistiken
/proc/PID
des Prozesses zu überprüfen, können Sie einen Blick in das Verzeichnis werfen (PID ist die Prozess-ID des angegebenen Prozesses). Sie können die ausführbare Datei sogar so öffnen, wie/proc/PID/exe
sie von der Festplatte getrennt wurde.Lassen Sie uns jetzt die Bewegung ausgraben:
Wenn Sie eine Datei innerhalb desselben Dateisystems verschieben, wird der Systemaufruf ausgeführt
rename()
, der die Datei lediglich in einen anderen Namen umbenennt. Der Inode der Datei bleibt unverändert.Während zwischen zwei verschiedenen Dateisystemen zwei Dinge passieren:
Der Inhalt der Datei wird zuerst von
read()
und an den neuen Speicherort kopiertwrite()
Danach wird die Verknüpfung der Datei mit dem Quellverzeichnis aufgehoben
unlink()
und die Datei erhält offensichtlich einen neuen Inode auf dem neuen Dateisystem.rm
eigentlich nur istunlink()
-Ing die angegebene Datei aus dem Verzeichnisbaum, die Schreibberechtigung für das Verzeichnis so haben werden Ihnen ausreichend Recht bekommt alle Dateien aus dem Verzeichnis zu entfernen.Stellen Sie sich zum Spaß vor, was passiert, wenn Sie Dateien zwischen zwei Dateisystemen verschieben und keine Berechtigung für
unlink()
die Datei aus der Quelle haben.Nun, die Datei wird zuerst an das Ziel kopiert (
read()
,write()
) undunlink()
schlägt dann aufgrund unzureichender Berechtigungen fehl. Die Datei bleibt also in beiden Dateisystemen !!quelle
mmap
undunmap
Systemaufrufe sind nicht zu be- und entladen werden die Seiten bei Bedarf verwendet, Seiten vom Kernel geladen werden , wenn sie erzeugen einen Seitenfehler Zugriff auf Seiten aus dem Speicher entfernt werden , wenn das OS fühlt sich der RAM wäre besser für etwas anderes verwendet werden. An diesen Lade- / Entladevorgängen ist kein Systemaufruf beteiligt.Nun, das ist ziemlich einfach. Nehmen wir eine ausführbare Datei mit dem Namen / usr / local / bin / whoopdeedoo. Das ist nur ein Hinweis auf den sogenannten Inode (Grundstruktur von Dateien auf Unix-Dateisystemen). Es ist die Inode, die als "in Benutzung" markiert wird.
Wenn Sie nun die Datei / usr / local / whoopdeedoo löschen oder verschieben, wird nur der Verweis auf den Inode verschoben (oder gelöscht). Die Inode selbst bleibt unverändert. Das war's im Grunde.
Ich sollte es überprüfen, aber ich glaube, Sie können dies auch auf Mac OS X-Dateisystemen tun.
Windows verfolgt einen anderen Ansatz. Warum? Wer weiß...? Ich kenne die Interna von NTFS nicht. Theoretisch sollten alle Dateisysteme, die Verweise auf interne Strukturen für Dateinamen verwenden, dazu in der Lage sein.
Ich gebe zu, ich habe zu stark vereinfacht, aber lies den Abschnitt "Implikationen" auf Wikipedia, der einen viel besseren Job macht als ich.
quelle
In allen anderen Antworten scheint Folgendes zu fehlen: Sobald eine Datei geöffnet ist und ein Programm einen geöffneten Dateideskriptor enthält, wird die Datei erst dann aus dem System entfernt, wenn dieser Dateideskriptor geschlossen wird.
Versuche, den Inode zu löschen, auf den verwiesen wird, werden verzögert, bis die Datei geschlossen wird: Das Umbenennen im selben oder in einem anderen Dateisystem kann die geöffnete Datei weder beeinflussen, noch die Datei explizit löschen oder durch eine neue überschreiben. Die einzige Möglichkeit, eine Datei durcheinander zu bringen, besteht darin, ihren Inode explizit zu öffnen und mit dem Inhalt zu experimentieren.
Wenn der Kernel eine Datei ausführt, behält er außerdem einen Verweis auf die ausführbare Datei bei, was wiederum verhindert, dass Änderungen während der Ausführung vorgenommen werden.
Selbst wenn es so aussieht, als ob Sie die Dateien, aus denen ein laufendes Programm besteht, löschen / verschieben können, bleibt der Inhalt dieser Dateien im Speicher, bis das Programm endet.
quelle
execve()
gibt kein FD zurück, sondern führt einfach das Programm aus. So zum Beispiel, wenn Sie laufentail -f /foo.log
dann sie eine FD (/proc/PID/fd/<fd_num>
) in Verbindung mittail
für diefoo.log
aber nicht für die ausführbare Datei selbsttail
, nicht auf seinen Eltern auch. Dies gilt auch für die einzelnen ausführbaren Dateien.execve
deshalb sehe ich nicht, wie dies relevant ist. Sobald der Kernel mit der Ausführung einer Datei beginnt, ändert der Versuch, die Datei zu ersetzen, nichts an dem Programm, das der Kernel laden wird, um den Punkt moot zu rendern. Wenn Sie die ausführbare Datei "aktualisieren" möchten, während sie ausgeführt wird, können Sieexecve
irgendwann einen Aufruf ausführen , damit der Kernel die Datei erneut liest, aber ich verstehe nicht, wie dies wichtig ist. Der Punkt ist: Das Löschen einer "laufenden ausführbaren Datei" löst keine Datenlöschung aus, bis die ausführbare Datei gestoppt wird.execve()
und ein FD, wenn in diesem Fall kein FD beteiligt ist.open()
gibt eine Datei - Descriptor , die heemayl spricht hier mit istexecve()
. Ja, ein laufender Prozess hat einen Verweis auf seine ausführbare Datei, aber das ist kein Dateideskriptor. Selbst wenn esmunmap()
alle Zuordnungen seiner ausführbaren Datei bearbeitet hätte, hätte es wahrscheinlich immer noch eine Referenz (die sich in / proc / self / exe widerspiegelt), die die Freigabe der Inode verhindert. (Dies wäre ohne Absturz möglich, wenn dies von einer nie zurückgegebenen Bibliotheksfunktion aus geschehen würde.) Übrigens kann das Abschneiden oder Ändern einer in Verwendung befindlichen ausführbaren Datei zu Problemen führenETXTBUSY
, aber möglicherweise funktionieren.Wenn Sie eine Datei in einem Linux-Dateisystem verschieben, müssen Sie nur den Inode von
..
(übergeordnetes Verzeichnis) in den des neuen Speicherorts ändern, sofern er nicht die Grenzen des Dateisystems überschreitet (lesen: bleibt auf derselben Festplatte / Partition) . Die eigentlichen Daten sind auf der Festplatte überhaupt nicht verschoben worden, nur der Zeiger, damit das Dateisystem weiß, wo es zu finden ist.Dies ist der Grund, warum Verschiebevorgänge so schnell sind und es wahrscheinlich kein Problem gibt, ein laufendes Programm zu verschieben, da Sie das Programm selbst nicht tatsächlich verschieben.
quelle
Dies ist möglich, da das Verschieben eines Programms keine Auswirkungen auf die Ausführung von Prozessen hat, die durch das Starten des Programms gestartet wurden.
Sobald ein Programm gestartet ist, werden die auf der Festplatte befindlichen Bits gegen Überschreiben geschützt. Es ist jedoch nicht erforderlich, die umzubenennende Datei zu schützen, sie an einen anderen Speicherort im selben Dateisystem zu verschieben, der dem Umbenennen der Datei entspricht, oder sie zu verschieben in ein anderes Dateisystem, das dem Kopieren der Datei an einem anderen Ort entspricht, und dann das Entfernen der Datei.
Wenn Sie eine Datei entfernen, die gerade verwendet wird, weil auf einem Prozess ein Dateideskriptor geöffnet ist oder weil ein Prozess ihn ausführt, werden die Dateidaten nicht entfernt, auf die der Dateiknoten weiterhin verweist, sondern nur der Verzeichniseintrag. dh ein Pfad, von dem aus die Inode erreicht werden kann.
Beachten Sie, dass das Starten eines Programms nicht alles auf einmal in den (physischen) Speicher lädt. Im Gegenteil, es wird nur das strikte Minimum geladen, das zum Starten des Prozesses erforderlich ist. Die erforderlichen Seiten werden dann während der gesamten Lebensdauer des Prozesses bei Bedarf geladen. Dies wird als Bedarfs-Paging bezeichnet. Wenn der Arbeitsspeicher knapp wird, kann das Betriebssystem den Arbeitsspeicher freigeben, der diese Seiten enthält, sodass ein Prozess die gleiche Seite aus dem ausführbaren Inode mehrmals laden kann.
Der Grund, warum dies unter Windows nicht möglich war, ist wahrscheinlich darauf zurückzuführen, dass das zugrunde liegende Dateisystem (FAT) das Split-Konzept von Verzeichniseinträgen gegenüber Inodes nicht unterstützte. Diese Einschränkung war bei NTFS nicht mehr vorhanden, aber das Betriebssystemdesign wurde lange beibehalten, was zu der unangenehmen Einschränkung führte, dass beim Installieren einer neuen Version einer Binärdatei ein Neustart erforderlich war, was bei neueren Windows-Versionen nicht mehr der Fall ist.
quelle
Grundsätzlich wird in Unix und seiner Umgebung ein Dateiname (einschließlich des Verzeichnispfads, der zu ihm führt) verwendet, um eine Datei beim Öffnen zuzuordnen / zu finden (das Ausführen einer Datei ist eine Möglichkeit, sie auf eine Weise zu öffnen). Nach diesem Moment wird die Identität der Datei (über ihren "Inode") festgestellt und nicht mehr in Frage gestellt. Sie können die Datei entfernen, umbenennen und ihre Berechtigungen ändern. Solange jeder Prozess oder ein Dateipfad einen Handle für die Datei / Inode hat, wird es bleiben , um, funktioniert wie ein Rohr zwischen Prozessen (tatsächlich in historischen UNIX ein Rohr war ein nameless inode mit einer Größe , dass in der gerade eingebaut "direkte Blöcke" Plattenspeicherreferenz in der Inode, so etwas wie 10 Blöcke).
Wenn Sie einen PDF-Viewer für eine PDF-Datei geöffnet haben, können Sie diese Datei löschen und eine neue mit demselben Namen öffnen. Solange der alte Viewer geöffnet ist, kann weiterhin problemlos auf die alte Datei zugegriffen werden (es sei denn, sie wird aktiv überwacht) das Dateisystem, um festzustellen, wann die Datei unter ihrem ursprünglichen Namen verschwindet).
Programme, die temporäre Dateien benötigen, können eine solche Datei einfach unter einem bestimmten Namen öffnen und sie dann sofort entfernen (oder vielmehr ihren Verzeichniseintrag), solange sie noch geöffnet ist. Danach ist die Datei nicht mehr über den Namen zugänglich, aber alle Prozesse, die einen offenen Dateideskriptor für die Datei haben, können weiterhin darauf zugreifen. Wenn das Programm danach unerwartet beendet wird, wird die Datei entfernt und der Speicher automatisch zurückgefordert.
Der Pfad zu einer Datei ist also keine Eigenschaft der Datei selbst (in der Tat können feste Verknüpfungen mehrere unterschiedliche Pfade bereitstellen) und wird nur zum Öffnen und nicht für den fortgesetzten Zugriff durch Prozesse benötigt, die sie bereits geöffnet haben.
quelle