Warum ist es möglich, ein laufendes Programm in Ubuntu zu verschieben?

24

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.

n0.ob
quelle
2
So ziemlich ein Cross-Site-Betrüger: stackoverflow.com/a/196910/1394393 .
jpmc26
1
Sie können rename(2)unter OS X keine ausführbare Datei ausführen? Was passiert, verstehst du EBUSYoder so? Warum funktioniert es nicht? Die Manpage rename (2) dokumentiert ETXTBUSYdiesen Systemaufruf nicht und spricht nur davon EBUSY, dass Verzeichnisumbenennungen möglich sind. Daher wusste ich nicht, dass ein POSIX-System das Umbenennen von ausführbaren Dateien überhaupt nicht zulassen könnte.
Peter Cordes
3
macOS-Apps können auch während der Ausführung verschoben werden, nur nicht in den Papierkorb. Ich nehme an, dass einige Apps danach fehlerhaft sind, z. B. wenn sie Datei-URLs in ihren binären oder gebündelten Ressourcen als Variable speichern, anstatt eine solche URL über NSBundle et al. Zu generieren. Ich vermute, es liegt an der POSIX-Konformität von macOS.
Constantino Tsarouhas
1
Es funktioniert tatsächlich so, wie Linux es beabsichtigt. Sie sollten wissen, was Sie tun. : P
userDepth
2
Ich denke, eine andere Art, darüber nachzudenken, ist, warum sollte es nicht möglich sein? Nur weil Windows Sie nicht zulässt, bedeutet dies nicht zwangsläufig, dass dies aufgrund der Funktionsweise von Prozessen oder dergleichen grundsätzlich nicht möglich ist.
Thomas

Antworten:

32

Lass es mich zusammenfassen.

Wenn Sie eine ausführbare Datei ausführen, wird eine Folge von Systemaufrufen ausgeführt, vor allem fork()und execve():

  • 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 Linker open()s und mmap()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/PIDdes 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/exesie 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.

rmeigentlich nur ist unlink()-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()) und unlink()schlägt dann aufgrund unzureichender Berechtigungen fehl. Die Datei bleibt also in beiden Dateisystemen !!

heemayl
quelle
5
Ihr virtueller und physischer Speicher sind etwas verwirrend. Ihre Beschreibung der Art und Weise, wie das Programm in den physischen Speicher geladen wird, ist ungenau. Der Systemaufruf exec kopiert keinesfalls die verschiedenen Abschnitte einer ausführbaren Datei in den physischen Speicher, sondern lädt nur denjenigen, den er zum Starten des Prozesses benötigt. In der Folge werden die erforderlichen Seiten bei Bedarf geladen, möglicherweise lange später. Die ausführbaren Dateibytes sind Teil des virtuellen Arbeitsspeichers des Prozesses und können während der gesamten Lebensdauer des Prozesses gelesen und möglicherweise erneut gelesen werden.
Juli
@jlliagre Bearbeitet, ich hoffe es ist jetzt geklärt. Vielen Dank.
Heemayl
6
"Der Prozess verwendet das Dateisystem nicht mehr" -Anweisung ist immer noch fraglich.
Juli
2
Das grundlegende Verständnis, dass eine bestimmte Datei im Dateisystem nicht direkt durch den Dateinamen identifiziert wird, sollte viel klarer sein.
Thorbjørn Ravn Andersen
2
Das sind immer noch Ungenauigkeiten in Ihrem Update. Die mmapund unmapSystemaufrufe 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.
Juli
14

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.

Kieferhai
quelle
1
Wenn Sie eine Verknüpfung in Windows verwenden, um die ausführbare Datei zu starten, können Sie die Verknüpfung auch löschen, wenn Sie sie vielleicht so vergleichen möchten. = 3
Ray
2
Nein, das wäre wie das Löschen eines symbolischen Links. In anderen Kommentaren wird angegeben, dass das Verhalten auf die Unterstützung älterer FAT-Dateisysteme zurückzuführen ist. Das klingt nach einem wahrscheinlichen Grund.
jawtheshark
1
Dies hat nichts speziell mit Inodes zu tun . NTFS verwendet MFT-Datensätze, um den Dateizustand zu verfolgen, und FAT verwendet dafür Verzeichniseinträge, aber Linux funktioniert mit diesen Dateisystemen immer noch genauso - aus Benutzersicht.
Ruslan
13

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.

Bakuriu
quelle
1
Das ist nicht richtig. execve()gibt kein FD zurück, sondern führt einfach das Programm aus. So zum Beispiel, wenn Sie laufen tail -f /foo.logdann sie eine FD ( /proc/PID/fd/<fd_num>) in Verbindung mit tailfür die foo.logaber nicht für die ausführbare Datei selbst tail, nicht auf seinen Eltern auch. Dies gilt auch für die einzelnen ausführbaren Dateien.
Heemayl
@heemayl habe ich nicht erwähnt, execvedeshalb 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 Sie execveirgendwann 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.
Bakuriu
Ich spreche über diesen Teil , wenn das Programm von einer einzigen ausführbaren Datei besteht , wenn Sie Ausführung des Programm starten Fein im Verzeichnis unabhängig von jeder Änderung ausgeführt werden soll: in demselben oder einem anderen Dateisystem umbenennen kann keinen Einfluss auf die offenen Handler , werden Sie unbedingt sprechen about execve()und ein FD, wenn in diesem Fall kein FD beteiligt ist.
Heemayl
2
Sie benötigen kein Datei-Handle, um einen Verweis auf die Datei zu haben - das Zuordnen von Seiten ist ebenfalls ausreichend.
Simon Richter
1
Unix hat keine "Datei-Handles". open()gibt eine Datei - Descriptor , die heemayl spricht hier mit ist execve(). Ja, ein laufender Prozess hat einen Verweis auf seine ausführbare Datei, aber das ist kein Dateideskriptor. Selbst wenn es munmap()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ühren ETXTBUSY, aber möglicherweise funktionieren.
Peter Cordes
7

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.

I_GNU_it_all_along
quelle
Ihre Antwort scheint zu implizieren, dass das Verschieben einer ausführbaren Binärdatei in ein anderes Dateisystem die Ausführung von Prozessen beeinflusst, die von dieser Binärdatei gestartet werden.
Juli
6

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.

jlliagre
quelle
1
Ich glaube, dass neuere Versionen von Windows die verwendeten Binärdateien ersetzen können, ohne dass ein Neustart erforderlich ist.
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Ich frage mich, warum alle Updates noch neu
gestartet werden müssen
1
Sie tun es nicht. Schauen Sie genauer hin. Obwohl Binärdateien aktualisiert werden können, kann der Kernel dies (meines Wissens) nicht und erfordert einen Neustart, der durch eine neuere Version ersetzt werden muss. Dies gilt für die meisten Betriebssystemkerne. Klügere Leute als ich haben kpatch für Linux geschrieben, das einen Linux-Kernel während des Betriebs patchen kann - siehe en.wikipedia.org/wiki/Kpatch
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Ich meinte "alle Windows-Updates"
Braiam
@Braiam ja - ich auch. Bitte schauen Sie genauer hin.
Thorbjørn Ravn Andersen
4

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.

user584745
quelle