Ich habe nach dem Unterschied zwischen diesen vier bei Google gesucht und erwartet, dass es eine große Menge an Informationen dazu gibt, aber es gab wirklich keinen soliden Vergleich zwischen den vier Anrufen.
Ich habe versucht, eine Art grundlegenden Überblick über die Unterschiede zwischen diesen Systemaufrufen zu erhalten, und hier ist, was ich bekommen habe. Sind alle diese Informationen korrekt / fehlt mir etwas Wichtiges?
Fork
: Der Fork-Aufruf erstellt im Grunde genommen ein Duplikat des aktuellen Prozesses, das in fast jeder Hinsicht identisch ist (in einigen Implementierungen wird nicht alles kopiert, z. B. Ressourcenbeschränkungen, aber die Idee ist, eine möglichst enge Kopie zu erstellen).
Der neue Prozess (untergeordnet) erhält eine andere Prozess-ID (PID) und hat die PID des alten Prozesses (übergeordnet) als übergeordnete PID (PPID). Da die beiden Prozesse jetzt genau denselben Code ausführen, können sie anhand des Rückkehrcodes von fork erkennen, welcher Code welcher ist - das Kind erhält 0, das Elternteil erhält die PID des Kindes. Dies alles ist natürlich vorausgesetzt, dass der Fork-Aufruf funktioniert. Wenn nicht, wird kein untergeordnetes Element erstellt und das übergeordnete Element erhält einen Fehlercode.
Vfork
: Der grundlegende Unterschied zwischen vfork und fork besteht darin, dass beim Erstellen eines neuen Prozesses mit vfork () der übergeordnete Prozess vorübergehend angehalten wird und der untergeordnete Prozess möglicherweise den Adressraum des übergeordneten Prozesses ausleiht. Dieser seltsame Zustand setzt sich fort, bis der untergeordnete Prozess entweder beendet wird oder execve () aufruft. An diesem Punkt wird der übergeordnete Prozess fortgesetzt.
Dies bedeutet, dass der untergeordnete Prozess einer vfork () vorsichtig sein muss, um zu vermeiden, dass Variablen des übergeordneten Prozesses unerwartet geändert werden. Insbesondere darf der untergeordnete Prozess nicht von der Funktion zurückkehren, die den Aufruf von vfork () enthält, und er darf nicht exit () aufrufen (wenn er beendet werden muss, sollte er _exit () verwenden; dies gilt tatsächlich auch für das untergeordnete Element einer normalen Gabel ()).
Exec :
Der Exec-Aufruf ist eine Möglichkeit, den gesamten aktuellen Prozess durch ein neues Programm zu ersetzen. Es lädt das Programm in den aktuellen Prozessbereich und führt es vom Einstiegspunkt aus. exec () ersetzt den aktuellen Prozess durch die ausführbare Datei, auf die die Funktion zeigt. Die Steuerung kehrt niemals zum ursprünglichen Programm zurück, es sei denn, es liegt ein exec () - Fehler vor.
Clone :
Klonen als Gabel erzeugt einen neuen Prozess. Im Gegensatz zu Fork ermöglichen diese Aufrufe dem untergeordneten Prozess, Teile seines Ausführungskontexts mit dem aufrufenden Prozess zu teilen, z. B. den Speicherplatz, die Tabelle der Dateideskriptoren und die Tabelle der Signalhandler.
Wenn der untergeordnete Prozess mit dem Klon erstellt wird, führt er die Funktionsanwendung fn (arg) aus. (Dies unterscheidet sich von Fork, bei dem die Ausführung im untergeordneten Element ab dem Zeitpunkt des ursprünglichen Fork-Aufrufs fortgesetzt wird.) Das Argument fn ist ein Zeiger auf eine Funktion, die vom untergeordneten Prozess zu Beginn seiner Ausführung aufgerufen wird. Das Argument arg wird an die Funktion fn übergeben.
Wenn die Funktionsanwendung fn (arg) zurückkehrt, wird der untergeordnete Prozess beendet. Die von fn zurückgegebene Ganzzahl ist der Exit-Code für den untergeordneten Prozess. Der untergeordnete Prozess kann auch explizit durch Aufrufen von exit (2) oder nach Empfang eines schwerwiegenden Signals beendet werden.
Informationen erhalten Form:
- Unterschiede zwischen Gabel und Exec
- http://www.allinterview.com/showanswers/59616.html
- http://www.unixguide.net/unix/programming/1.1.2.shtml
- http://linux.about.com/library/cmd/blcmdl2_clone.htm
Vielen Dank, dass Sie sich die Zeit genommen haben, dies zu lesen! :) :)
fork()
ist als unter Linux und wahrscheinlich allen BSDs) den Adressraum seiner Eltern ausleiht. Alles, was es tut, außer anzurufenexecve()
oder_exit()
, hat ein großes Potenzial, die Eltern durcheinander zu bringen. Insbesondereexit()
ruftatexit()
Handler und andere „Finalizers“, zum Beispiel: es stdio Streams spült. Die Rückkehr von einemvfork()
Kind würde möglicherweise (dieselbe Einschränkung wie zuvor) den Stapel der Eltern durcheinander bringen.fork
Syscall aufruft ?Antworten:
vfork()
ist eine veraltete Optimierung. Vor einer guten Speicherverwaltung wurdefork()
eine vollständige Kopie des Speichers der Eltern erstellt, sodass dies ziemlich teuer war. Da in vielen Fällen afork()
folgteexec()
, wodurch die aktuelle Speicherzuordnung verworfen und eine neue erstellt wird, war dies ein unnötiger Aufwand. Heutzutage wirdfork()
der Speicher nicht kopiert. Es wird einfach als "Kopieren beim Schreiben" festgelegt, sodassfork()
+exec()
genauso effizient ist wievfork()
+exec()
.clone()
ist der vonfork()
. Mit einigen Parametern wird ein neuer Prozess erstellt, mit anderen wird ein Thread erstellt. Der Unterschied zwischen ihnen besteht darin, welche Datenstrukturen (Speicherplatz, Prozessorstatus, Stapel, PID, geöffnete Dateien usw.) gemeinsam genutzt werden oder nicht.quelle
vfork
vermeidet die Notwendigkeit, vorübergehend viel mehr Speicher zu belegen, nur damit ausgeführt werden kannexec
, und es ist immer noch effizienter alsfork
, wenn auch nicht annähernd so hoch. Auf diese Weise kann vermieden werden, dass der Speicher überlastet werden muss, damit ein großes Programm einen untergeordneten Prozess auslösen kann. Dies ist also nicht nur eine Leistungssteigerung, sondern könnte es überhaupt machbar machen.exec
.execve()
Ersetzt das aktuelle ausführbare Image durch ein anderes, das aus einer ausführbaren Datei geladen wurde.fork()
erstellt einen untergeordneten Prozess.vfork()
ist eine historisch optimierte Version vonfork()
, die verwendet werden soll, wennexecve()
sie direkt danach aufgerufen wirdfork()
. Es stellte sich heraus, dass es in Nicht-MMU-Systemen (in denenfork()
nicht effizient gearbeitet werden kann) und infork()
Prozessen mit großem Speicherbedarf gut funktioniert , um ein kleines Programm auszuführen (denken Sie an JavaRuntime.exec()
). POSIX hat das standardisiertposix_spawn()
, um diese beiden letztgenannten moderneren Anwendungen zu ersetzenvfork()
.posix_spawn()
macht das Äquivalent von afork()/execve()
und erlaubt auch etwas fd Jonglieren dazwischen. Es soll ersetzenfork()/execve()
, hauptsächlich für Nicht-MMU-Plattformen.pthread_create()
erstellt einen neuen Thread.clone()
ist ein Linux-spezifischer Aufruf, mit dem alles vonfork()
bis implementiert werden kannpthread_create()
. Es gibt viel Kontrolle. Inspiriert amrfork()
.rfork()
ist ein Plan-9-spezifischer Anruf. Es soll ein generischer Aufruf sein, der mehrere Grade der gemeinsamen Nutzung zwischen vollständigen Prozessen und Threads ermöglicht.quelle
fork()
- Erstellt einen neuen untergeordneten Prozess, der eine vollständige Kopie des übergeordneten Prozesses ist. Untergeordnete und übergeordnete Prozesse verwenden unterschiedliche virtuelle Adressräume, die anfänglich von denselben Speicherseiten gefüllt werden. Wenn dann beide Prozesse ausgeführt werden, unterscheiden sich die virtuellen Adressräume immer mehr, da das Betriebssystem ein verzögertes Kopieren von Speicherseiten durchführt, die von einem dieser beiden Prozesse geschrieben werden, und eine unabhängige Kopie der geänderten Seiten von zuweist Speicher für jeden Prozess. Diese Technik wird als Copy-On-Write (COW) bezeichnet.vfork()
- Erstellt einen neuen untergeordneten Prozess, bei dem es sich um eine "schnelle" Kopie des übergeordneten Prozesses handelt. Im Gegensatz zum Systemaufruffork()
teilen sich untergeordnete und übergeordnete Prozesse denselben virtuellen Adressraum. HINWEIS! Bei Verwendung des gleichen virtuellen Adressraums verwenden sowohl das übergeordnete als auch das untergeordnete Element denselben Stapel, den Stapelzeiger und den Anweisungszeiger, wie im Fall des Klassikersfork()
! Um unerwünschte Interferenzen zwischen übergeordneten und untergeordneten Elementen zu vermeiden, die denselben Stapel verwenden, wird die Ausführung des übergeordneten Prozesses eingefroren, bis das untergeordnete Element entwederexec()
(Erstellen eines neuen virtuellen Adressraums und Übergang zu einem anderen Stapel) oder_exit()
(Beenden der Prozessausführung) aufruft ).vfork()
ist die Optimierung derfork()
für „fork-and-exec“ -Modell. Es kann 4-5 mal schneller als das durchgeführt werdenfork()
, weil im Gegensatz zumfork()
(Selbst wenn COW im Auge behalten wird) beinhaltet die Implementierung einesvfork()
Systemaufrufs nicht die Erstellung eines neuen Adressraums (die Zuweisung und Einrichtung neuer Seitenverzeichnisse).clone()
- Erstellt einen neuen untergeordneten Prozess. Verschiedene Parameter dieses Systemaufrufs geben an, welche Teile des übergeordneten Prozesses in den untergeordneten Prozess kopiert werden müssen und welche Teile zwischen ihnen geteilt werden. Infolgedessen kann dieser Systemaufruf verwendet werden, um alle Arten von Ausführungsentitäten zu erstellen, beginnend mit Threads und endend durch völlig unabhängige Prozesse. Tatsächlich ist derclone()
Systemaufruf die Basis, die für die Implementierungpthread_create()
und die gesamte Familie derfork()
Systemaufrufe verwendet wird.exec()
- setzt den gesamten Speicher des Prozesses zurück, lädt und analysiert die angegebene ausführbare Binärdatei, richtet einen neuen Stapel ein und übergibt die Steuerung an den Einstiegspunkt der geladenen ausführbaren Datei. Dieser Systemaufruf gibt niemals die Kontrolle an den Aufrufer zurück und dient zum Laden eines neuen Programms in den bereits vorhandenen Prozess. Dieser Systemaufruf mitfork()
Systemaufruf bildet zusammen ein klassisches UNIX-Prozessmanagementmodell namens "fork-and-exec".quelle
vfork
so schwach sind, dass es legal wäre,vfork
ein Synonym für zu erstellenfork
(und POSIX.1-2008 wirdvfork
vollständig aus der Spezifikation entfernt). Wenn Sie Ihren Code auf einem System testen, das sie synonymisiert (z. B. die meisten Post-4.4-BSDs außer NetBSD, Linux-Kernel vor 2.2.0-pre6 usw.), funktioniert dies möglicherweise auch dann, wenn Sie gegen denvfork
Vertrag verstoßen und dann explodieren wenn Sie es woanders ausführen. Einige von denen, die es mit simulierenfork
(z. B. OpenBSD), garantieren immer noch, dass der Elternteil erst dann wieder läuft, wenn das Kindexec
oder die Kinder_exit
. Es ist lächerlich nicht tragbar.Fork (), vfork () und clone () rufen alle do_fork () auf, um die eigentliche Arbeit zu erledigen, jedoch mit unterschiedlichen Parametern.
Für Fork haben das Kind und der Vater die unabhängige VM-Seitentabelle, aber da Fork aus Effizienzgründen keine Seiten wirklich kopiert, werden nur alle beschreibbaren Seiten so eingestellt, dass sie für den untergeordneten Prozess schreibgeschützt sind. Wenn der untergeordnete Prozess etwas auf diese Seite schreiben möchte, tritt eine Seitenausnahme auf und der Kernel weist eine neue Seite zu, die mit Schreibberechtigung von der alten Seite geklont wurde. Das nennt man "copy on write".
Für vfork ist der virtuelle Speicher genau von Kind und Vater - nur deshalb können Vater und Kind nicht gleichzeitig wach sein, da sie sich gegenseitig beeinflussen. Der Vater wird also am Ende von "do_fork ()" schlafen und aufwachen, wenn das Kind exit () oder execve () aufruft, da es seitdem eine neue Seitentabelle besitzt. Hier ist der Code (in do_fork ()), den der Vater schläft.
Hier ist der Code (in mm_release (), der von exit () und execve () aufgerufen wird und den Vater weckt.
Für sys_clone () ist es flexibler, da Sie beliebige clone_flags eingeben können. Also ruft pthread_create () diesen Systemaufruf mit vielen clone_flags auf:
int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);
Zusammenfassung: Mit fork (), vfork () und clone () werden untergeordnete Prozesse mit unterschiedlichen Ressourcen für die gemeinsame Nutzung von Ressourcen mit dem Vaterprozess erstellt. Wir können auch sagen, dass vfork () und clone () Threads erstellen können (tatsächlich sind sie Prozesse, da sie unabhängige task_struct haben), da sie die VM-Seitentabelle mit dem Vaterprozess gemeinsam nutzen.
quelle
In fork () wird entweder der untergeordnete oder der übergeordnete Prozess basierend auf der CPU-Auswahl ausgeführt. In vfork () wird das untergeordnete Element sicherlich zuerst ausgeführt. Nachdem das Kind beendet wurde, wird das Elternteil ausgeführt.
quelle
vfork()
kann einfach als implementiert werdenfork()
.pid
). Das Betriebssystem kann den neuen Prozess so planen, dass er parallel ausgeführt wird, wenn dies sinnvoll ist (z. B. mehrere Prozessoren). Wenn Sie diese Prozesse aus irgendeinem Grund in einer bestimmten seriellen Reihenfolge ausführen müssen, benötigen Sie eine zusätzliche Synchronisation, die das Forking nicht bietet. Ehrlich gesagt, würden Sie wahrscheinlich gar nicht erst eine Gabel wollen.vfork()
läuft das Kind zuerst. Es ist in den Manpages; Die Hinrichtung der Eltern wird ausgesetzt, bis das Kind entweder stirbt oderexec
s. Und ninjalj schlägt den Kernel-Quellcode nach. Es gibt keine Möglichkeit zu implementierenvfork()
,fork()
da sie unterschiedliche Argumente ando_fork()
den Kernel übergeben. Sie können jedochvfork
mit demclone
Syscall