it-swarm.com.de

Der Unterschied zwischen fork (), vfork (), exec () und clone ()

Ich habe versucht, den Unterschied zwischen diesen vier bei Google zu finden, und ich habe erwartet, dass es eine riesige Menge an Informationen dazu gibt, aber es gab wirklich keinen soliden Vergleich zwischen den vier Anrufen.

Ich habe mich daran gemacht, auf einen Blick einen Überblick über die Unterschiede zwischen diesen Systemaufrufen zu verschaffen. Sind all diese Informationen korrekt/vermisse ich etwas Wichtiges?

Fork: Der Fork-Aufruf erstellt im Grunde genommen ein Duplikat des aktuellen Prozesses, das in fast jeder Hinsicht identisch ist (nicht alles wird kopiert, z. B. in einigen Implementierungen über Ressourcenbeschränkungen, aber die Idee ist, eine Kopie so nah wie möglich zu erstellen möglich).

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ückgabecodes von fork erkennen, welcher davon ist - das untergeordnete Element erhält 0, das übergeordnete Element die PID des untergeordneten Elements. Dies setzt natürlich voraus, dass der Fork-Aufruf funktioniert. Andernfalls 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 hält an, bis der untergeordnete Prozess entweder beendet wird oder execve () aufruft. An diesem Punkt wird der übergeordnete Prozess fortgesetzt.

Dies bedeutet, dass der untergeordnete Prozess eines vfork () darauf achten muss, dass die Variablen des übergeordneten Prozesses nicht unerwartet geändert werden. Insbesondere darf der untergeordnete Prozess nicht von der Funktion zurückkehren, die den vfork () -Aufruf enthält, und es darf nicht exit () aufgerufen werden (wenn er beendet werden muss, sollte _exit () verwendet werden; dies gilt 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 Prozessraum und führt es vom Einstiegspunkt aus. exec () ersetzt den aktuellen Prozess durch eine 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 : Clone erstellt als Fork einen neuen Prozess. Im Gegensatz zu fork können mit diesen Aufrufen Teile des Ausführungskontexts des untergeordneten Prozesses für den aufrufenden Prozess freigegeben werden, z. B. der Speicherbereich, die Tabelle der Dateideskriptoren und die Tabelle der Signalhandler.

Wenn der untergeordnete Prozess mit Clone erstellt wird, führt er die Funktionsanwendung fn (arg) aus. (Dies unterscheidet sich von der Verzweigung, bei der die Ausführung im untergeordneten Element ab dem Punkt des ursprünglichen Verzweigungsaufrufs 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ückgegeben wird, 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:

Vielen Dank, dass Sie sich die Zeit genommen haben, dies zu lesen! :)

186
user476033
  • vfork() ist eine veraltete Optimierung. Vor einer guten Speicherverwaltung hat fork() eine vollständige Kopie des übergeordneten Speichers erstellt, was ziemlich teuer war. da in vielen Fällen auf eine fork() eine exec() folgt, die die aktuelle Speicherzuordnung verwirft und eine neue erstellt, war dies ein unnötiger Aufwand. Heutzutage kopiert fork() den Speicher nicht; Es wird einfach als "Kopie beim Schreiben" festgelegt, daher ist fork() + exec() genauso effizient wie vfork() + exec().

  • clone() ist der von fork() verwendete Systemaufruf. Mit einigen Parametern wird ein neuer Prozess erstellt, mit anderen ein Thread. Der Unterschied besteht darin, welche Datenstrukturen (Speicherplatz, Prozessorstatus, Stack, PID, offene Dateien usw.) gemeinsam genutzt werden oder nicht.

147
Javier
  • 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 von fork(), die verwendet werden soll, wenn execve() direkt nach fork() aufgerufen wird. Es stellte sich heraus, dass es in Nicht-MMU-Systemen gut funktioniert (wo fork() nicht effizient arbeiten kann) und wenn fork() Prozesse mit großem Speicherbedarf ausgeführt werden, um ein kleines Programm auszuführen (denken Sie an Java) Runtime.exec()). POSIX hat die posix_spawn() standardisiert, um diese beiden neueren Verwendungen von vfork() zu ersetzen.
  • posix_spawn() entspricht fork()/execve() und erlaubt auch ein bisschen fd-Jonglieren dazwischen. Es soll fork()/execve() ersetzen, hauptsächlich für Nicht-MMU-Plattformen.
  • pthread_create() erstellt einen neuen Thread.
  • clone() ist ein Linux-spezifischer Aufruf, mit dem alles von fork() bis pthread_create() implementiert werden kann. Es gibt viel Kontrolle. Inspiriert von rfork().
  • rfork() ist ein Plan-9-spezifischer Aufruf. Es soll ein allgemeiner Aufruf sein, der mehrere Grade des Teilens zwischen vollständigen Prozessen und Threads ermöglicht.
74
ninjalj
  1. 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, beginnen sich die virtuellen Adressräume immer mehr zu unterscheiden, 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.
  2. vfork() - erstellt einen neuen untergeordneten Prozess, der eine "schnelle" Kopie des übergeordneten Prozesses ist. Im Gegensatz zum Systemaufruf fork() verwenden untergeordnete und übergeordnete Prozesse denselben virtuellen Adressraum. HINWEIS! Unter Verwendung des gleichen virtuellen Adressraums verwenden sowohl das übergeordnete als auch das untergeordnete Element den gleichen Stapel, den Stapelzeiger und den Anweisungszeiger, wie im Fall des klassischen fork()! 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 entweder exec() aufruft (einen neuen virtuellen Adressraum erstellen und auf einen anderen Stapel wechseln). oder _exit() (Beendigung der Prozessausführung). vfork() ist die Optimierung von fork() für das Modell "fork-and-exec". Es kann 4-5 mal schneller als die fork() ausgeführt werden, da im Gegensatz zu der fork() (auch mit COW im Hinterkopf) die Implementierung des vfork()-Systemaufrufs umfasst nicht die Erstellung eines neuen Adressraums (Zuweisung und Einrichtung neuer Seitenverzeichnisse).
  3. 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 von diesen gemeinsam genutzt werden. Infolgedessen können mit diesem Systemaufruf alle Arten von Ausführungsentitäten erstellt werden, beginnend mit Threads und endend mit vollständig unabhängigen Prozessen. Tatsächlich ist der Systemaufruf clone() die Basis, die für die Implementierung von pthread_create() und der gesamten Familie der Systemaufrufe fork() verwendet wird.
  4. 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 die Kontrolle niemals an den Aufrufer zurück und dient zum Laden eines neuen Programms in den bereits vorhandenen Prozess. Dieser Systemaufruf mit fork() Systemaufruf bildet zusammen ein klassisches UNIX-Prozessverwaltungsmodell mit dem Namen "fork-and-exec".
39
ZarathustrA

Die Funktionen fork (), vfork () und clone () rufen alle do_fork () auf, um die eigentliche Arbeit zu erledigen, jedoch mit unterschiedlichen Parametern.

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

Für fork haben das Kind und der Vater die unabhängige Seitentabelle VM=), aber da die Effizienz von fork keine Seiten wirklich kopiert, werden nur alle beschreibbaren Seiten so eingestellt, dass sie nur für den untergeordneten Prozess lesbar sind Wenn ein untergeordneter 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.

Für vfork ist das virtuelle Gedächtnis genau das Kind und der Vater - nur deswegen 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 dann eine neue Seitentabelle besitzt. Hier ist der Code (in do_fork ()), in dem der Vater schläft.

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

Hier ist der Code (in mm_release (), der von exit () und execve ()) aufgerufen wird und den Vater weckt.

up(tsk->p_opptr->vfork_sem);

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 erstellt, die unterschiedliche Ressourcen gemeinsam mit dem übergeordneten Prozess nutzen. Wir können auch sagen, dass vfork () und clone () Threads erstellen können (eigentlich sind sie Prozesse, da sie eine unabhängige task_struct haben), da sie die VM Seitentabelle mit dem Vaterprozess teilen.

6
user991800

Unterschiede zwischen fork () und vfork () Unterschied zwischen fork-vfork the difference between fork and vfork

Das Verhalten von Vfork () wird im folgenden Programm näher erläutert.

[email protected] ~}$ cat vfork_advanced.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int n =10;
    pid_t pid = vfork(); //creating the child process
    if (pid == 0)          //if this is a chile process
    {
        printf("Child process started\n");
    }
    else//parent process execution
    {
        printf("Now i am coming back to parent process\n");
    }
    printf("value of n: %d \n",n); //sample printing to check "n" value
    return 0;
}
[email protected] ~}$ cc vfork_advanced.c
[email protected] ~}$ ./a.out
Child process started
value of n: 10
Now i am coming back to parent process
value of n: 594325573
a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.
Aborted

Hinweis: Auch wenn Sie beobachten, ist das Ergebnis von vfork nicht definiert. Der Wert von "n" wurde zum ersten Mal als 10 gedruckt, was erwartet wird. Beim nächsten Mal in dem übergeordneten Prozess hat es jedoch einen Abfallwert gedruckt.