wake-up-neo.com

Unterschiede zwischen Fork und Exec

Was sind die Unterschiede zwischen fork und exec?

182
Sashi

Die Verwendung von fork und exec veranschaulicht den Geist von UNIX, indem es eine sehr einfache Möglichkeit bietet, neue Prozesse zu starten.

Der fork-Aufruf macht im Wesentlichen ein Duplikat des aktuellen Prozesses aus, wobei fast in jeder Hinsicht identisch ist (in einigen Implementierungen wird nicht alles kopiert, z. B. die Ressourcengrenzwerte, aber es geht darum, eine möglichst nahe 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 feststellen, welcher der beiden ist. Das Kind erhält 0, der Elternteil erhält die PID des Kindes. Dies ist natürlich alles vorausgesetzt, der Aufruf von fork funktioniert - wenn nicht, wird kein untergeordnetes Element erstellt und das übergeordnete Element erhält einen Fehlercode.

Der Aufruf exec 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 aus.

Daher werden fork und exec häufig nacheinander verwendet, um ein neues Programm als untergeordnetes Element eines aktuellen Prozesses auszuführen. Shells tun dies normalerweise, wenn Sie versuchen, ein Programm wie find auszuführen - die Shell verzahnt sich, dann lädt das Kind das find-Programm in den Speicher, wobei alle Befehlszeilenargumente, Standard-E/A usw. eingerichtet werden.

Sie müssen jedoch nicht zusammen verwendet werden. Es ist durchaus akzeptabel, dass ein Programm fork selbst ohne execing ist, wenn das Programm beispielsweise sowohl übergeordneten als auch untergeordneten Code enthält (Sie müssen vorsichtig sein, was Sie tun, jede Implementierung kann Einschränkungen aufweisen). Dies wurde für Daemons, die einfach einen TCP - Port abhören und fork eine Kopie von sich selbst verwenden, um eine bestimmte Anforderung zu verarbeiten, während das übergeordnete Element wieder zuhören möchte.

Ebenso müssen Programme, die wissen, dass sie fertig sind und nur ein anderes Programm ausführen möchten, nicht fork, exec und dann wait für das Kind benötigen. Sie können das Kind einfach direkt in ihren Prozessraum laden.

Einige UNIX-Implementierungen haben ein optimiertes fork, das das verwendet, was sie als Copy-on-Write bezeichnen. Dies ist ein Trick, um das Kopieren des Prozessbereichs in fork zu verzögern, bis das Programm versucht, etwas in diesem Bereich zu ändern. Dies ist nützlich für Programme, die nur fork und nicht exec verwenden, da sie nicht den gesamten Prozessbereich kopieren müssen.

Wenn exec nach fork aufgerufen wird (und dies geschieht meistens), führt dies zu einem Schreibvorgang in den Prozessbereich, und es wird dann für den untergeordneten Prozess kopiert.

Beachten Sie, dass es eine ganze Familie von exec-Aufrufen gibt (execl, execle, execve und so weiter), aber exec bedeutet hier jeden von ihnen.

Das folgende Diagramm zeigt die typische fork/exec-Operation, bei der die bash-Shell zum Auflisten eines Verzeichnisses mit dem Befehl ls verwendet wird:

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V
334
paxdiablo

fork() teilt den aktuellen Prozess in zwei Prozesse auf. Oder anders ausgedrückt: Ihr Nice lineares Programm, das leicht zu verstehen ist, wird plötzlich zu zwei separaten Programmen, die einen Code ausführen:

 int pid = fork();

 if (pid == 0)
 {
     printf("I'm the child");
 }
 else
 {
     printf("I'm the parent, my child is %i", pid);
     // here we can kill the child, but that's not very parently of us
 }

Das kann dir den Atem versetzen. Jetzt haben Sie einen Code, bei dem ein ziemlich identischer Status von zwei Prozessen ausgeführt wird. Der untergeordnete Prozess erbt den gesamten Code und Speicher des Prozesses, den er gerade erstellt hat, einschließlich des Beginns des fork()-Aufrufs. Der einzige Unterschied ist der Rückgabecode fork(), der Ihnen mitteilt, ob Sie Elternteil oder Kind sind. Wenn Sie das übergeordnete Element sind, ist der Rückgabewert die ID des untergeordneten Elements.

exec ist ein bisschen leichter zu verstehen. Sie sagen einfach exec, dass sie einen Prozess mit der Ziel-Programmdatei ausführen, und Sie haben nicht zwei Prozesse, die denselben Code ausführen oder den gleichen Status erben. Wie @Steve Hawkins sagt, kann exec verwendet werden, nachdem Sie fork verwendet haben, um die aktuelle Zieldatei im aktuellen Prozess auszuführen.

49
Doug T.

Ich denke, einige Konzepte aus "Advanced Unix Programming" von Marc Rochkind waren hilfreich, um die verschiedenen Rollen von fork()exec() zu verstehen, insbesondere für jemanden, der das Windows CreateProcess()-Modell verwendet hat:

Ein _/program ist eine Sammlung von Anweisungen und Daten, die in einer regulären Datei auf der Festplatte gespeichert werden. (ab 1.1.2 Programme, Prozesse und Threads)

.

Um ein Programm auszuführen, wird der Kernel zunächst aufgefordert, ein neues process zu erstellen. Hierbei handelt es sich um eine Umgebung, in der ein Programm ausgeführt wird. (auch ab 1.1.2 Programme, Prozesse und Threads)

.

Es ist unmöglich, die Exec- oder Fork-Systemaufrufe zu verstehen, ohne die Unterscheidung zwischen einem Prozess und einem Programm vollständig zu verstehen. Wenn diese Bedingungen für Sie neu sind, möchten Sie möglicherweise Abschnitt 1.1.2 erneut lesen. Wenn Sie jetzt fortfahren möchten, fassen wir die Unterscheidung in einem Satz zusammen: Ein Prozess ist eine Ausführungsumgebung, die aus Anweisungs-, Benutzerdaten- und Systemdatensegmenten sowie vielen anderen zur Laufzeit erfassten Ressourcen besteht Ein Programm ist jedoch eine Datei mit Anweisungen und Daten, die zum Initialisieren der Anweisungs- und Benutzerdatensegmente eines Prozesses verwendet werden. (ab 5.3 exec Systemaufrufe)

Wenn Sie den Unterschied zwischen einem Programm und einem Prozess verstanden haben, kann das Verhalten von fork() und exec() function wie folgt zusammengefasst werden:

  • fork() erstellt ein Duplikat des aktuellen Prozesses
  • exec() ersetzt das Programm im aktuellen Prozess durch ein anderes Programm

(Dies ist im Wesentlichen eine vereinfachte "for Dummies" -Version der viel detaillierteren Antwort von paxdiablo )

29
Michael Burr

Gabel erstellt eine Kopie eines aufrufenden Prozesses. folgt im Allgemeinen der Struktur enter image description here

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

(Für untergeordneten Prozesstext (Code) ist data, stack der aufrufende Prozess.). Der untergeordnete Prozess führt Code im if-Block aus.

EXEC ersetzt den aktuellen Prozess durch Code, Daten, Stack des neuen Prozesses Im Allgemeinen folgt die Struktur enter image description here

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exec(foo);

  exit(0);

}

//parent code

wait(cpid);

// end

(Nach dem Aufruf des Unix-Kernel durch exec wird der untergeordnete Prozesstext gelöscht, Daten werden gestapelt und mit foo-prozessbezogenen Text/Daten gefüllt.). Der untergeordnete Prozess hat daher einen anderen Code (foo's code {nicht identisch mit dem übergeordneten})

27
Sandesh Kobal

Sie werden zusammen verwendet, um einen neuen untergeordneten Prozess zu erstellen. Beim Aufruf von fork wird zunächst eine Kopie des aktuellen Prozesses (des untergeordneten Prozesses) erstellt. Dann wird exec vom untergeordneten Prozess aus aufgerufen, um die Kopie des übergeordneten Prozesses durch den neuen Prozess zu "ersetzen".

Der Prozess läuft ungefähr so ​​ab:

child = fork();  //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail

if (child < 0) {
    std::cout << "Failed to fork GUI process...Exiting" << std::endl;
    exit (-1);
} else if (child == 0) {       // This is the Child Process
    // Call one of the "exec" functions to create the child process
    execvp (argv[0], const_cast<char**>(argv));
} else {                       // This is the Parent Process
    //Continue executing parent process
}
7
Steve Hawkins

fork () erstellt eine Kopie des aktuellen Prozesses. Die Ausführung im neuen untergeordneten Element beginnt unmittelbar nach dem Aufruf von fork (). Nach der Gabel () sind sie identisch mit Ausnahme des Rückgabewerts der Gabel () - Funktion. (RTFM für weitere Details.) Die beiden Prozesse können dann noch weiter auseinander gehen, wobei einer den anderen nicht beeinflussen kann, außer möglicherweise über gemeinsam genutzte Dateizugriffspunkte.

exec () ersetzt den aktuellen Prozess durch einen neuen. Es hat nichts mit fork () zu tun, außer, dass exec () häufig fork () folgt, wenn ein anderer untergeordneter Prozess gestartet werden soll, anstatt den aktuellen zu ersetzen.

4
Warren Young

Der Hauptunterschied zwischen fork() und exec() ist, dass

Der Systemaufruf fork() erstellt einen Klon des aktuell laufenden Programms. Das ursprüngliche Programm setzt die Ausführung mit der nächsten Codezeile nach dem Aufruf der Funktion fork () fort. Der Klon beginnt auch mit der Ausführung in der nächsten Codezeile. Sehen Sie sich den folgenden Code an, den ich von http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/ erhalten habe.

#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
    printf("--beginning of program\n");
    int counter = 0;
    pid_t pid = fork();
    if (pid == 0)
    {
        // child process
        int i = 0;
        for (; i < 5; ++i)
        {
            printf("child process: counter=%d\n", ++counter);
        }
    }
    else if (pid > 0)
    {
        // parent process
        int j = 0;
        for (; j < 5; ++j)
        {
            printf("parent process: counter=%d\n", ++counter);
        }
    }
    else
    {
        // fork failed
        printf("fork() failed!\n");
        return 1;
    }
    printf("--end of program--\n");
    return 0;
}

Dieses Programm deklariert eine Zählervariable, die vor fork()ing auf Null gesetzt wird. Nach dem Fork-Aufruf laufen zwei Prozesse parallel, die beide ihre eigene Version des Zählers erhöhen. Jeder Prozess wird vollständig ausgeführt und beendet. Da die Prozesse parallel ablaufen, können wir nicht wissen, welcher Prozess zuerst abgeschlossen wird. Wenn Sie dieses Programm ausführen, wird ein Ausdruck ähnlich dem unten gezeigten gedruckt, die Ergebnisse können jedoch von Lauf zu Lauf variieren.

--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3
--end of program--
child process: counter=4
child process: counter=5
--end of program--

Die exec()-Familie von Systemaufrufen ersetzt den aktuell ausgeführten Code eines Prozesses durch einen anderen Code. Der Prozess behält seine PID bei, aber er wird zu einem neuen Programm. Betrachten Sie zum Beispiel den folgenden Code: 

#include <stdio.h> 
#include <unistd.h> 
main() {
 char program[80],*args[3];
 int i; 
printf("Ready to exec()...\n"); 
strcpy(program,"date"); 
args[0]="date"; 
args[1]="-u"; 
args[2]=NULL; 
i=execvp(program,args); 
printf("i=%d ... did it work?\n",i); 
} 

Dieses Programm ruft die Funktion execvp() auf, um den Code durch das Datumsprogramm zu ersetzen. Wenn der Code in einer Datei mit dem Namen exec1.c gespeichert ist, wird bei der Ausführung die folgende Ausgabe erzeugt: 

Ready to exec()... 
Tue Jul 15 20:17:53 UTC 2008 

Das Programm gibt die Zeile ―Ready to exec () aus. . . ‖ Und nach dem Aufruf der Funktion execvp () wird der Code durch das Datumsprogramm ersetzt. Beachten Sie, dass die Zeile -. . . hat es funktioniert‖ wird nicht angezeigt, da der Code an dieser Stelle ersetzt wurde. Stattdessen sehen wir die Ausgabe von ―date -u.‖ 

2

enter image description herefork():

Es wird eine Kopie des laufenden Prozesses erstellt. Der laufende Prozess heißt übergeordneter Prozess und der neu erstellte Prozess heißt untergeordneter Prozess . Die Möglichkeit, die beiden zu unterscheiden, besteht darin, den zurückgegebenen Wert zu betrachten:

  1. fork() gibt die Prozesskennung (pid) des untergeordneten Prozesses im übergeordneten Prozess zurück

  2. fork() gibt 0 im Kind zurück.

exec():

Es initiiert einen neuen Prozess innerhalb eines Prozesses. Es lädt ein neues Programm in den aktuellen Prozess und ersetzt das vorhandene.

fork() + exec():

Wenn Sie ein neues Programm starten, müssen Sie zuerst fork(), einen neuen Prozess erstellen und dann exec() (d. H. In den Speicher laden und ausführen), die Programm-Binärdatei, die ausgeführt werden soll.

int main( void ) 
{
    int pid = fork();
    if ( pid == 0 ) 
    {
        execvp( "find", argv );
    }

    //Put the parent to sleep for 2 sec,let the child finished executing 
    wait( 2 );

    return 0;
}
1
Yogeesh H T

Das beste Beispiel für das Konzept von fork() und exec() ist dasShell, das Befehlsinterpreterprogramm, das Benutzer normalerweise nach dem Anmelden am System ausführen. Die Shell interpretiert das erste Wort von Befehlszeile als Befehl Name

Bei vielen Befehlen führen der Shell forks und der untergeordnete Prozess execs den Befehl aus, der dem Namen zugeordnet ist, der die übrigen Wörter in der Befehlszeile als Parameter für den Befehl.

Die Shell erlaubt drei Arten von Befehlen. Erstens kann ein Befehl eine ausführbare Datei sein , die Objektcode enthält, der durch Kompilierung von Quellcode (beispielsweise einem C-Programm) erzeugt wurde. Zweitens kann ein Befehl eine ausführbare Datei sein, die Eine Folge von Shell-Befehlszeilen enthält. Schließlich kann ein Befehl ein interner Shell-Befehl sein (anstelle einer ausführbaren Datei ex -> cd , ls usw.).

0
krpra