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/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 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 !!