
Wenn wir unter Unix einen neuen Prozess erstellen möchten, verzweigen wir den aktuellen Prozess und erstellen einen neuen untergeordneten Prozess, der genau mit dem übergeordneten Prozess identisch ist. Anschließend führen wir einen „exec“-Systemaufruf aus, um alle Daten des übergeordneten Prozesses durch die Daten des neuen Prozesses zu ersetzen.
Warum erstellen wir zunächst eine Kopie des übergeordneten Prozesses und nicht direkt einen neuen Prozess?
Antwort1
Die kurze Antwort lautet: fork
in Unix, weil es einfach war, es in das damals bestehende System zu integrieren, und weil einVorgängersystem in Berkeleyhatte das Konzept der Gabeln verwendet.
AusDie Entwicklung des Unix-Time-Sharing-Systems(der entsprechende Text wurdehervorgehoben):
Die Prozesssteuerung in ihrer modernen Form wurde innerhalb weniger Tage entworfen und implementiert. Es ist erstaunlich, wie einfach sie sich in das bestehende System einfügte; gleichzeitig ist leicht zu erkennen, wieEinige der etwas ungewöhnlichen Merkmale des Designs sind gerade deshalb vorhanden, weil sie kleine, leicht codierte Änderungen an dem darstellten, was existierte. Ein gutes Beispiel ist die Trennung der Fork- und Exec-Funktionen. Das gängigste Modell für die Erstellung neuer Prozesse besteht darin, ein Programm anzugeben, das der Prozess ausführen soll. Unter Unix führt ein geforkter Prozess dasselbe Programm wie sein übergeordneter Prozess weiter aus, bis er ein explizites Exec ausführt. Die Trennung der Funktionen ist sicherlich nicht einzigartig für Unix, undTatsächlich war es im Berkeley-Time-Sharing-System vorhanden, das Thompson wohlbekannt warDennoch scheint es vernünftig anzunehmen, dasses existiert in Unix hauptsächlich wegen der Leichtigkeit, mit der Fork implementiert werden konnte, ohne viel anderes zu ändern. Das System verarbeitete bereits mehrere (also zwei) Prozesse; es gab eine Prozesstabelle, und die Prozesse wurden zwischen dem Hauptspeicher und der Festplatte ausgetauscht. Die anfängliche Implementierung von Fork erforderte nur
1) Erweiterung der Prozesstabelle
2) Hinzufügen eines Fork-Aufrufs, der den aktuellen Prozess unter Verwendung der bereits vorhandenen Swap-IO-Grundelemente in den Swap-Bereich der Festplatte kopierte und einige Anpassungen an der Prozesstabelle vornahm.
Tatsächlich erforderte der Fork-Aufruf des PDP-7 genau 27 Zeilen Assemblercode. Natürlich waren auch andere Änderungen am Betriebssystem und an den Benutzerprogrammen erforderlich, und einige davon waren ziemlich interessant und unerwartet. Aberein kombinierter Fork-Exec wäre deutlich komplizierter gewesen, und sei es nur, weil exec als solches nicht existierte; seine Funktion wurde bereits unter Verwendung expliziter IO von der Shell ausgeführt.
Seit diesem Dokument hat sich Unix weiterentwickelt. fork
gefolgt von exec
ist nicht mehr die einzige Möglichkeit, ein Programm auszuführen.
vgabelwurde als effizientere Fork-Funktion für den Fall entwickelt, dass der neue Prozess direkt nach der Fork-Funktion eine Exec-Funktion ausführen soll. Nach der Ausführung einer Vfork-Funktion teilen sich der übergeordnete und der untergeordnete Prozess denselben Datenraum, und der übergeordnete Prozess wird angehalten, bis der untergeordnete Prozess entweder ein Programm ausführt oder beendet wird.
posix_spawnerstellt einen neuen Prozess und führt eine Datei in einem einzigen Systemaufruf aus. Es werden eine Reihe von Parametern benötigt, mit denen Sie die geöffneten Dateien des Anrufers selektiv freigeben und dessen Signaldisposition und andere Attribute in den neuen Prozess kopieren können.
Antwort2
[Ich wiederhole einen Teil meiner Antwort vonHier.]
Warum nicht einfach einen Befehl haben, der einen neuen Prozess von Grund auf erstellt? Ist es nicht absurd und ineffizient, etwas zu kopieren, das gleich wieder ersetzt wird?
Tatsächlich wäre dies aus mehreren Gründen wahrscheinlich nicht so effizient:
Die von erzeugte "Kopie"
fork()
ist ein bisschen abstrakt, da der Kernel eineKopieren beim SchreibenSystem; alles, was wirklich erstellt werden muss, ist eine virtuelle Speicherzuordnung. Wenn die Kopie dann sofort aufruftexec()
, müssen die meisten Daten, die kopiert worden wären, wenn sie durch die Aktivität des Prozesses geändert worden wären, nie kopiert/erstellt werden, da der Prozess nichts tut, was ihre Verwendung erfordert.Verschiedene wichtige Aspekte des Kindprozesses (z. B. seine Umgebung) müssen nicht einzeln dupliziert oder auf der Grundlage einer komplexen Kontextanalyse usw. festgelegt werden. Es wird einfach davon ausgegangen, dass sie mit denen des aufrufenden Prozesses identisch sind, und dies ist das ziemlich intuitive System, mit dem wir vertraut sind.
Um Nr. 1 etwas genauer zu erklären: Speicher, der „kopiert“ wird, aber anschließend nie wieder aufgerufen wird, wird nie wirklich kopiert, zumindest in den meisten Fällen. Eine Ausnahme in diesem Zusammenhangkönntewäre, wenn Sie einen Prozess aufgespalten hätten, dann hätte der übergeordnete Prozess beendet, bevor der untergeordnete Prozess sich selbst durch ersetzt hätte exec()
. Ich sagekönnteweil ein Großteil des übergeordneten Elements zwischengespeichert werden könnte, wenn ausreichend freier Speicher vorhanden ist, und ich bin nicht sicher, in welchem Ausmaß dies ausgenutzt würde (was von der Betriebssystemimplementierung abhängen würde).
Natürlich bedeutet das nicht, dass die Verwendung einer Kopiemehreffizienter als die Verwendung eines leeren Blattes – außer dass „das leere Blatt“ nicht buchstäblich nichts ist und eine Zuordnung beinhalten muss. Das System könnte eine generische leere/neue Prozessvorlage haben, die es auf die gleiche Weise kopiert, 1 , aber das würde dann im Vergleich zur Copy-on-Write-Abzweigung nicht wirklich etwas einsparen. Nr. 1 zeigt also nur, dass die Verwendung eines „neuen“ leeren Prozesses nicht effizienter wäre.
Punkt 2 erklärt, warum die Verwendung des Forks wahrscheinlich effizienter ist. Die Umgebung eines untergeordneten Prozesses wird von seinem übergeordneten Prozess übernommen, selbst wenn es sich um eine völlig andere ausführbare Datei handelt. Wenn der übergeordnete Prozess beispielsweise eine Shell und der untergeordnete Prozess ein Webbrowser ist, $HOME
ist dies für beide gleich, aber da jeder von beiden ihn später ändern könnte, müssen es zwei separate Kopien sein. Die im untergeordneten Prozess wird vom Original erstellt fork()
.
1. Eine Strategie, die im wörtlichen Sinne vielleicht nicht viel Sinn ergibt, aber ich will damit sagen, dass zum Erstellen eines Prozesses mehr gehört, als nur sein Abbild von der Festplatte in den Speicher zu kopieren.
Antwort3
Ich denke, der Grund, warum Unix nur die fork
Funktion hatte, neue Prozesse zu erstellen, ist ein Ergebnis derUnix-Philosophie
Sie erstellen eine Funktion, die eine Sache gut macht. Sie erstellt einen untergeordneten Prozess.
Was man mit dem neuen Prozess macht, bleibt dann dem Programmierer überlassen. Er kann eine der exec*
Funktionen verwenden und ein anderes Programm starten, oder er kann exec weglassen und die beiden Instanzen desselben Programms verwenden, was sinnvoll sein kann.
Sie erhalten also einen größeren Freiheitsgrad, da Sie verwenden können
- Fork ohne Exec*
- fork mit exec* oder
- einfach exec* ohne Fork
und außerdem müssen Sie sich nur die fork
und die exec*
Funktionsaufrufe merken, was in den 1970er Jahren noch erforderlich war.
Antwort4
Die Funktion fork() dient nicht nur zum Kopieren des Vaterprozesses, sie gibt auch einen Wert zurück, der darauf hinweist, dass es sich bei dem Prozess um den Vater- oder den Sohnprozess handelt. Das folgende Bild erklärt, wie Sie fork() als Vater und Sohn verwenden können:
wie gezeigt gibt fork() die ID des Sohnprozesses zurück, wenn der Prozess der Vater ist, PID
andernfalls gibt es0
Sie können dies beispielsweise nutzen, wenn Sie einen Prozess (Webserver) haben, der die Anfragen empfängt und bei jeder Anfrage einen Prozess son process
zum Verarbeiten dieser Anfrage erstellt. Hier haben der Vater und seine Söhne unterschiedliche Aufgaben.
Also, nein, das Ausführen einer Kopie eines Prozesses ist nicht dasselbe wie fork().