Wann immer wir in Unix einen neuen Prozess erstellen möchten, verzweigen wir den aktuellen Prozess und erstellen einen neuen untergeordneten Prozess, der genau dem übergeordneten Prozess entspricht. Dann führen wir einen Systemaufruf exec durch, um alle Daten des übergeordneten Prozesses durch die Daten des neuen Prozesses zu ersetzen.
Warum erstellen wir zuerst eine Kopie des übergeordneten Prozesses und nicht direkt einen neuen Prozess?
process
architecture
fork
Sarthak
quelle
quelle
Antworten:
Die kurze Antwort lautet: Unter
fork
Unix, weil es zu der Zeit leicht war, sich in das bestehende System einzufügen, und weil ein Vorgängersystem bei Berkeley das Konzept der Gabeln verwendet hatte.Aus der Evolution des Unix-Timesharing-Systems (relevanter Text wurde hervorgehoben ):
Seit dieser Veröffentlichung hat sich Unix weiterentwickelt.
fork
gefolgt vonexec
ist nicht mehr die einzige Möglichkeit, ein Programm auszuführen.vfork wurde als effizienterer Fork für den Fall entwickelt, dass der neue Prozess direkt nach dem Fork einen Exec ausführen soll. Nach einer vfork-Operation teilen sich der übergeordnete und der untergeordnete Prozess denselben Datenbereich und der übergeordnete Prozess wird angehalten, bis der untergeordnete Prozess ein Programm ausführt oder beendet.
posix_spawn erstellt einen neuen Prozess und führt eine Datei in einem einzigen Systemaufruf aus. Es sind eine Reihe von Parametern erforderlich, mit denen Sie die geöffneten Dateien des Anrufers selektiv freigeben und seine Signalverteilung und andere Attribute in den neuen Prozess kopieren können.
quelle
posix_spawn()
dieselben Post-Fork-Repumbing-Jobs auszuführen, die auch mitfork()
Inline-Code problemlos ausgeführt werden können, sind ein überzeugendes Argument fürfork()
die einfachere Verwendung.[Ich werde einen Teil meiner Antwort von hier aus wiederholen .]
Warum nicht einfach einen Befehl haben, der einen neuen Prozess von Grund auf neu erstellt? Ist es nicht absurd und ineffizient, eine zu kopieren, die nur sofort ersetzt wird?
In der Tat wäre das wahrscheinlich aus ein paar Gründen nicht so effizient:
Die von erzeugte "Kopie"
fork()
ist eine Art Abstraktion, da der Kernel ein Copy-on-Write- System verwendet . alles, was wirklich erstellt werden muss, ist eine virtuelle Speicherkarte. Wenn die Kopie dann sofort aufgerufen wirdexec()
, müssen die meisten Daten, die kopiert worden wären, wenn sie durch die Aktivität des Prozesses geändert worden wären, niemals tatsächlich kopiert / erstellt werden, da der Prozess nichts tut, was seine Verwendung erfordert.Verschiedene wichtige Aspekte des untergeordneten Prozesses (z. B. seine Umgebung) müssen nicht einzeln dupliziert oder basierend auf einer komplexen Kontextanalyse usw. festgelegt werden. Es wird lediglich davon ausgegangen, dass sie mit denen des aufrufenden Prozesses identisch sind Dies ist das ziemlich intuitive System, mit dem wir vertraut sind.
Um # 1 ein wenig weiter zu erläutern, wird der Speicher, auf den "kopiert", aber später nie zugegriffen wird, zumindest in den meisten Fällen nie wirklich kopiert. Eine Ausnahme in diesem Kontext kann sein, wenn Sie einen Prozess gegabelt haben und dann den übergeordneten Prozess beendet haben, bevor sich das untergeordnete Element durch ersetzt hat
exec()
. Ich sage möglicherweise, weil ein Großteil des übergeordneten Elements zwischengespeichert werden könnte, wenn genügend freier Speicher vorhanden ist, und ich bin nicht sicher, in welchem Umfang dies ausgenutzt würde (was von der Betriebssystemimplementierung abhängen würde).Natürlich tut das nicht auf der Oberfläche um eine Kopie mit mehr effizienter als die Verwendung ein unbeschriebenes Blatt - mit Ausnahme von „der unbeschriebenes Blatt“ nichts nicht wörtlich ist, und die Zuweisung beinhalten muss. Das System könnte eine generische leere / neue Prozessvorlage haben, die auf die gleiche Weise kopiert wird, 1 die dann jedoch nichts im Vergleich zur Kopie beim Schreiben wirklich rettet. Die Nummer 1 zeigt also nur, dass die Verwendung eines "neuen" leeren Prozesses nicht effizienter wäre.
Punkt 2 erklärt, warum die Verwendung der Gabel wahrscheinlich effizienter ist. Die Umgebung eines Kindes wird von seiner Eltern geerbt, auch wenn es sich um eine völlig andere ausführbare Datei handelt. Wenn beispielsweise der übergeordnete Prozess eine Shell und der untergeordnete ein Webbrowser ist,
$HOME
ist dies für beide immer noch derselbe. Da jedoch beide Prozesse dies später ändern könnten, müssen dies zwei separate Kopien sein. Derjenige im Kind wird vom Original produziertfork()
.1. Eine Strategie, die wörtlich nicht viel Sinn macht, aber ich möchte betonen, dass das Erstellen eines Prozesses mehr beinhaltet als das Kopieren des Images vom Datenträger in den Speicher.
quelle
fork()
kann es sehr schnell (wie GL erwähnt, in der Größenordnung von 27 Montagelinien). Wenn Sie in die andere Richtung blicken und einen "Prozess von Grund auf neu erstellen" möchten,fork()
kostet dies nur ein kleines bisschen mehr, als wenn Sie von einem leeren erstellten Prozess ausgehen (27 Assemblierungszeilen + Kosten für das Schließen von Dateihandles). So lassen sichfork
sowohl Gabel als auch Schaffen gutcreate
handhaben , während sich Schaffen nur gut handhaben lässt.fork
tatsächlich der gesamte Prozessspeicher kopiert, und es war sehr teuer.Ich denke, der Grund, warum Unix nur die
fork
Funktion hatte, neue Prozesse zu erstellen, ist ein Ergebnis der Unix-PhilosophieSie bauen eine Funktion auf, die eine Sache gut macht. Es wird ein untergeordneter Prozess erstellt.
Was man mit dem neuen Prozess macht, liegt dann beim Programmierer. Er kann eine der
exec*
Funktionen verwenden und ein anderes Programm starten, oder er kann nicht exec verwenden und die beiden Instanzen desselben Programms verwenden, was nützlich sein kann.So erhalten Sie einen größeren Freiheitsgrad, da Sie verwenden können
und außerdem müssen Sie sich nur die
fork
und dieexec*
Funktionsaufrufe merken , die Sie in den 1970er Jahren machen mussten.quelle
Es gibt zwei Philosophien für die Erstellung von Prozessen: Gabelung mit Vererbung und Erstellung mit Argumenten. Unix benutzt natürlich fork. (OSE zum Beispiel und VMS verwenden die create-Methode.) Unix verfügt über VIELE vererbbare Eigenschaften und weitere werden regelmäßig hinzugefügt. Über die Vererbung können diese neuen Merkmale hinzugefügt werden, ohne dass bestehende Programme geändert werden müssen! Bei Verwendung eines Modells zum Erstellen mit Argumenten würde das Hinzufügen neuer Merkmale das Hinzufügen neuer Argumente zum Aufruf "create" bedeuten. Das Unix-Modell ist einfacher.
Es bietet auch das äußerst nützliche Modell „Fork-without-Exec“, bei dem sich ein Prozess in mehrere Teile aufteilen kann. Dies war von entscheidender Bedeutung, als es keine Form von asynchroner E / A gab, und ist nützlich, wenn mehrere CPUs in einem System ausgenutzt werden. (Pre-Threads.) Ich habe dies im Laufe der Jahre viel getan, auch in letzter Zeit. Im Wesentlichen können mehrere 'Programme' in einem einzigen Programm zusammengefasst werden, so dass absolut kein Platz für Korruption, Versionsinkongruenzen usw. vorhanden ist.
Das Fork / Exec-Modell bietet einem bestimmten Kind auch die Möglichkeit, eine radikal verrückte Umgebung zu erben, die zwischen Fork und Exec eingerichtet wurde. Vor allem Dinge wie geerbte Dateideskriptoren. (Eine Erweiterung von stdio fd's.) Das Erstellungsmodell bietet nicht die Möglichkeit, etwas zu erben, das von den Erstellern des Erstellungsaufrufs nicht vorgesehen war.
Einige Systeme unterstützen auch die dynamische Kompilierung von nativem Code, wobei der Prozess tatsächlich ein eigenes Programm für nativen Code schreibt. Mit anderen Worten, es möchte ein neues Programm, das sich selbst im laufenden Betrieb schreibt, ohne den Quellcode- / Compiler- / Linker-Zyklus durchlaufen zu müssen und Speicherplatz zu belegen. (Ich glaube, es gibt ein Verilog-Sprachsystem, das dies tut.) Das Fork-Modell unterstützt dies, das Create-Modell normalerweise nicht.
quelle
Die Funktion fork () kopiert nicht nur den Vaterprozess, sondern gibt einen Wert zurück, der darauf hinweist, dass der Prozess der Vater- oder der Sohnprozess ist. In der folgenden Abbildung wird erläutert, wie Sie fork () als Vater und a verwenden können Sohn:
Wie gezeigt, wenn der Prozess der Vater ist, gibt fork () die Sohn-Prozess-ID zurück,
PID
ansonsten wird sie zurückgegeben0
Sie können es beispielsweise verwenden, wenn Sie einen Prozess (Webserver) haben, der die Anforderungen empfängt und bei jeder Anforderung einen erstellt
son process
, um diese Anforderung zu verarbeiten. Hier haben der Vater und seine Söhne unterschiedliche Aufgaben.Also, keine Kopie eines Prozesses ausführen ist nicht die exakte Sache wie fork ().
quelle
fork
wenn Sie annehmen , dass Sie wollenfork
in erster LinieDie E / A-Umleitung lässt sich am einfachsten nach dem Verzweigen und vor dem Ausführen implementieren. Das Kind ist sich dessen bewusst, dass es das Kind ist. Es kann Dateideskriptoren schließen, neue öffnen, dup () oder dup2 (), um sie auf die richtige fd-Nummer usw. zu bringen, ohne dass dies Auswirkungen auf das Elternteil hat. Danach kann das neue Programm in der angepassten Umgebung ausgeführt werden. Eventuell werden Änderungen an der Umgebungsvariablen vorgenommen (dies wirkt sich auch nicht auf die übergeordnete Komponente aus).
quelle
Ich denke, jeder hier weiß, wie Gabel funktioniert, aber die Frage ist, warum wir mit Gabel ein genaues Duplikat des übergeordneten Elements erstellen müssen. Antwort ==> Nehmen Sie ein Beispiel für einen Server (ohne Verzweigung), während Client-1 auf den Server zugreift, wenn zur gleichen Zeit der zweite Client-2 angekommen ist und auf den Server zugreifen möchte, der Server jedoch dem neu eingetroffenen nicht die Berechtigung erteilt client-2, da der Server beschäftigt ist, um client-1 zu bedienen, sodass client-2 warten muss. Nachdem alle Dienste für client-1 abgeschlossen sind, kann client-2 jetzt auf den Server zugreifen client-3 muss warten, bis alle Dienste für client-2 abgeschlossen sind. Nehmen Sie das Szenario an, in dem Tausende von Clients gleichzeitig auf den Server zugreifen müssen. Dann müssen alle Clients warten Warten Sie (Server ist ausgelastet !!).
Dies wird vermieden, indem (mithilfe von fork) eine exakte doppelte Kopie (dh ein untergeordnetes Objekt) des Servers erstellt wird, wobei jedes untergeordnete Objekt (dh die exakte doppelte Kopie des übergeordneten Objekts, dh des Servers) dem neu angekommenen Client zugeordnet ist, sodass alle Clients gleichzeitig auf denselben zugreifen Server.
quelle
fork
Aufruf bietet, der den übergeordneten Prozess kopiert, besteht darin, dass Sie nicht zwei separate Programme haben müssen, sondern separate Programme (z. B.inetd
), die das System modularer machen können.