Was ist der Zweck von fork()?

Lesezeit: 11 Minuten

In vielen Programmen und Manpages von Linux habe ich gesehen, dass Code verwendet wird fork(). Warum müssen wir verwenden fork() und was ist ihr zweck?

  • Damit all die Essphilosophen nicht verhungern.

    – kenj0418

    12. Juni 2009 um 4:51 Uhr

Benutzeravatar von Todd Gamblin
Tod Gamblin

fork() So erstellen Sie neue Prozesse in Unix. Wenn du anrufst forkerstellen Sie eine Kopie Ihres eigenen Prozesses, der über einen eigenen verfügt Adressraum. Dadurch können mehrere Tasks unabhängig voneinander ausgeführt werden, als ob sie jeweils den vollen Arbeitsspeicher der Maschine für sich hätten.

Hier sind einige Beispiele für die Verwendung von fork:

  1. Dein Hülse Verwendet fork um die Programme auszuführen, die Sie über die Befehlszeile aufrufen.
  2. Webserver wie Apache verwenden fork um mehrere Serverprozesse zu erstellen, von denen jeder Anforderungen in seinem eigenen Adressraum verarbeitet. Wenn einer stirbt oder Speicher verloren geht, bleiben andere davon unberührt, sodass er als Mechanismus für die Fehlertoleranz fungiert.
  3. Google Chrome Verwendet fork um jede Seite in einem separaten Prozess zu behandeln. Dadurch wird verhindert, dass clientseitiger Code auf einer Seite Ihren gesamten Browser herunterfährt.
  4. fork wird verwendet, um Prozesse in einigen parallelen Programmen (wie denjenigen, die mit MPI). Beachten Sie, dass sich dies von der Verwendung unterscheidet Fädendie keinen eigenen Adressraum haben und existieren innerhalb ein Prozess.
  5. Verwendung von Skriptsprachen fork indirekt, um untergeordnete Prozesse zu starten. Zum Beispiel jedes Mal, wenn Sie einen Befehl wie verwenden subprocess.Popen in Python, du fork einen untergeordneten Prozess und lesen Sie seine Ausgabe. Dadurch können Programme zusammenarbeiten.

Typische Verwendung von fork in einer Shell könnte so aussehen:

int child_process_id = fork();
if (child_process_id) {
    // Fork returns a valid pid in the parent process.  Parent executes this.

    // wait for the child process to complete
    waitpid(child_process_id, ...);  // omitted extra args for brevity

    // child process finished!
} else {
    // Fork returns 0 in the child process.  Child executes this.

    // new argv array for the child process
    const char *argv[] = {"arg1", "arg2", "arg3", NULL};

    // now start executing some other program
    exec("/path/to/a/program", argv);
}

Die Shell erzeugt einen untergeordneten Prozess mit exec und wartet, bis es abgeschlossen ist, und fährt dann mit seiner eigenen Ausführung fort. Beachten Sie, dass Sie auf diese Weise nicht fork verwenden müssen. Sie können immer viele untergeordnete Prozesse hervorbringen, wie es ein paralleles Programm tun könnte, und jeder kann gleichzeitig ein Programm ausführen. Grundsätzlich verwenden Sie jedes Mal, wenn Sie neue Prozesse in einem Unix-System erstellen fork(). Für das Windows-Äquivalent werfen Sie einen Blick auf CreateProcess.

Wenn Sie weitere Beispiele und eine längere Erklärung wünschen, Wikipedia hat eine anständige Zusammenfassung. Und Hier sind einige Folien Hier erfahren Sie, wie Prozesse, Threads und Nebenläufigkeit in modernen Betriebssystemen funktionieren.

  • Seltsamerweise heißt es CreateProcess() – diese verrückten Windows-Typen 🙂

    – paxdiablo

    12. Juni 2009 um 5:50 Uhr

  • Ich habe bis jetzt nie bemerkt, dass “Shell Fork verwendet, um die Programme auszuführen, die Sie von der Befehlszeile aus aufrufen”!

    – Laser

    24. März 2010 um 17:04 Uhr

  • Der Link zu Google Slides ist defekt

    – piertoni

    30. November 2015 um 8:22 Uhr

  • Das sagen alle Antworten fork() ist der Weg, einen neuen Prozess in UNIX zu erstellen, aber um pedantisch zu sein, es gibt mindestens einen anderen: posix_spawn().

    – Davislor

    27. Juli 2018 um 10:40 Uhr

  • Aber ja, interessant, mit posix_spawn wäre vermutlich viel effizienter beim Kompilieren für Cygwin oder erfordert weniger Emulationsarbeit, indem es einfach CreateProcess verwendet, anstatt einen Fork vorzutäuschen.

    – Peter Cordes

    23. Oktober 2021 um 17:59 Uhr

Benutzeravatar von Daniel C. Sobral
Daniel C. Sobral

Mit fork() erstellt Unix neue Prozesse. An dem Punkt, an dem Sie fork() aufgerufen haben, wird Ihr Prozess geklont, und zwei verschiedene Prozesse setzen die Ausführung von dort aus fort. Bei einem von ihnen, dem Kind, gibt fork() 0 zurück. Bei dem anderen, dem Elternteil, gibt fork() die PID (Prozess-ID) des Kindes zurück.

Wenn Sie beispielsweise Folgendes in eine Shell eingeben, ruft das Shell-Programm fork() auf und führt dann den von Ihnen übergebenen Befehl (in diesem Fall telnetd) im untergeordneten Programm aus, während das übergeordnete Programm ebenfalls erneut die Eingabeaufforderung anzeigt als Nachricht, die die PID des Hintergrundprozesses angibt.

$ telnetd &

Aus dem Grund, warum Sie neue Prozesse erstellen, kann Ihr Betriebssystem viele Dinge gleichzeitig tun. Aus diesem Grund können Sie ein Programm ausführen und während es ausgeführt wird, zu einem anderen Fenster wechseln und etwas anderes tun.

  • @varDumper Guter Fang!

    – Daniel C. Sobral

    30. Juli 2018 um 16:28 Uhr

Benutzeravatar von Wadih M
Wadih M.

fork() wird verwendet, um einen untergeordneten Prozess zu erstellen. Wenn eine fork()-Funktion aufgerufen wird, wird ein neuer Prozess gestartet und der Aufruf der fork()-Funktion gibt einen anderen Wert für das untergeordnete und das übergeordnete Element zurück.

Wenn der Rückgabewert 0 ist, wissen Sie, dass Sie der untergeordnete Prozess sind, und wenn der Rückgabewert eine Zahl ist (die zufällig die ID des untergeordneten Prozesses ist), wissen Sie, dass Sie der übergeordnete Prozess sind. (und wenn es eine negative Zahl ist, ist der Fork fehlgeschlagen und es wurde kein untergeordneter Prozess erstellt)

http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

  • Es sei denn, der Rückgabewert ist -1, in diesem Fall ist die Funktion fork() fehlgeschlagen.

    – Jonathan Leffler

    12. Juni 2009 um 5:36 Uhr

Benutzeravatar von Nave
Kirchenschiff

fork() wird grundsätzlich verwendet, um einen untergeordneten Prozess für den Prozess zu erstellen, in dem Sie diese Funktion aufrufen. Immer wenn Sie eine fork() aufrufen, gibt sie eine Null für die Kind-ID zurück.

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

Auf diese Weise können Sie unterschiedliche Aktionen für Eltern und Kinder bereitstellen und die Multithreading-Funktion nutzen.

fork() erstellt einen neuen Kindprozess, der mit dem Elternprozess identisch ist. Alles, was Sie danach im Code ausführen, wird also von beiden Prozessen ausgeführt – sehr nützlich, wenn Sie beispielsweise einen Server haben und mehrere Anfragen verarbeiten möchten.

  • Warum erstellen Sie ein Kind, das mit dem Elternteil identisch ist, was nützt es?

    Kar

    12. Juni 2009 um 4:57 Uhr

  • Es ist wie der Aufbau einer Armee gegen einen einzelnen Soldaten. Sie verzweigen sich, damit Ihr Programm mehr Anfragen gleichzeitig verarbeiten kann, anstatt eine nach der anderen.

    – Wolkenkopf

    12. Juni 2009 um 5:03 Uhr

  • fork() gibt 0 für das Kind und die PID des Kindes für das Elternteil zurück. Das Kind kann dann einen Aufruf wie exec() verwenden, um seinen Zustand durch ein neues Programm zu ersetzen. So werden Programme gestartet.

    – Tod Gamblin

    12. Juni 2009 um 5:03 Uhr

  • Die Prozesse sind fast identisch, aber es gibt viele subtile Unterschiede. Die eklatanten Unterschiede sind aktuelle PID und übergeordnete PID. Es gibt Probleme im Zusammenhang mit gehaltenen Sperren und gehaltenen Semaphoren. Die manpage fork() für POSIX listet 25 Unterschiede zwischen Eltern und Kind auf.

    – Jonathan Leffler

    12. Juni 2009 um 5:42 Uhr

  • @kar: Sobald Sie zwei Prozesse haben, können sie von dort aus separat fortgesetzt werden, und einer von ihnen könnte sich selbst (exex()) vollständig durch ein anderes Programm ersetzen.

    – Vatine

    26. September 2010 um 12:28 Uhr

Benutzeravatar von cpp-coder
cpp-coder

Der Systemaufruf fork() wird verwendet, um Prozesse zu erstellen. Es akzeptiert keine Argumente und gibt eine Prozess-ID zurück. Der Zweck von fork() besteht darin, einen neuen Prozess zu erstellen, der zum untergeordneten Prozess des Aufrufers wird. Nachdem ein neuer untergeordneter Prozess erstellt wurde, führen beide Prozesse die nächste Anweisung nach dem Systemaufruf fork() aus. Daher müssen wir den Elternteil vom Kind unterscheiden. Dies kann durch Testen des zurückgegebenen Werts von fork() erfolgen:

Wenn fork() einen negativen Wert zurückgibt, war die Erstellung eines untergeordneten Prozesses nicht erfolgreich. fork() gibt eine Null an den neu erstellten untergeordneten Prozess zurück. fork() gibt einen positiven Wert, die Prozess-ID des Kindprozesses, an den Elternprozess zurück. Die zurückgegebene Prozess-ID ist vom Typ pid_t, definiert in sys/types.h. Normalerweise ist die Prozess-ID eine Ganzzahl. Darüber hinaus kann ein Prozess die Funktion getpid() verwenden, um die diesem Prozess zugewiesene Prozess-ID abzurufen. Daher kann nach dem Systemaufruf von fork() ein einfacher Test feststellen, welcher Prozess der Kindprozess ist. Bitte beachten Sie, dass Unix eine exakte Kopie des Adressraums des Elternteils anfertigt und an das Kind weitergibt. Daher haben die Eltern- und Kindprozesse getrennte Adressräume.

Lassen Sie es uns anhand eines Beispiels verstehen, um die obigen Punkte zu verdeutlichen. Dieses Beispiel unterscheidet nicht zwischen übergeordneten und untergeordneten Prozessen.

#include  <stdio.h>
#include  <string.h>
#include  <sys/types.h>

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

Angenommen, das obige Programm wird bis zum Aufruf von fork() ausgeführt.

Wenn der Aufruf von fork() erfolgreich ausgeführt wird, erstellt Unix zwei identische Kopien von Adressräumen, eine für den Elternteil und die andere für den Kindteil. Beide Prozesse beginnen ihre Ausführung bei der nächsten Anweisung nach dem Aufruf von fork(). In diesem Fall beginnen beide Prozesse ihre Ausführung bei der Zuweisung

pid = .....;

Beide Prozesse beginnen ihre Ausführung direkt nach dem Systemaufruf fork(). Da beide Prozesse identische, aber getrennte Adressräume haben, haben die vor dem Aufruf von fork() initialisierten Variablen in beiden Adressräumen die gleichen Werte. Da jeder Prozess seinen eigenen Adressraum hat, sind alle Änderungen unabhängig von den anderen. Mit anderen Worten, wenn der Elternprozess den Wert seiner Variablen ändert, wirkt sich die Änderung nur auf die Variable im Adressraum des Elternprozesses aus. Andere Adressräume, die durch Aufrufe von fork() erstellt wurden, sind davon nicht betroffen, obwohl sie identische Variablennamen haben.

Was ist der Grund für die Verwendung von Write anstelle von Printf? Das liegt daran, dass printf() „gepuffert“ ist, was bedeutet, dass printf() die Ausgabe eines Prozesses zusammenfasst. Während die Ausgabe für den übergeordneten Prozess gepuffert wird, kann der untergeordnete Prozess auch printf verwenden, um einige Informationen auszudrucken, die ebenfalls gepuffert werden. Da die Ausgabe nicht sofort an den Bildschirm gesendet wird, erhalten Sie möglicherweise nicht die richtige Reihenfolge des erwarteten Ergebnisses. Schlimmer noch, die Ausgabe der beiden Prozesse kann auf seltsame Weise gemischt werden. Um dieses Problem zu lösen, können Sie erwägen, das “ungepufferte” Schreiben zu verwenden.

Wenn Sie dieses Programm ausführen, sehen Sie möglicherweise Folgendes auf dem Bildschirm:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

Die Prozess-ID 3456 kann diejenige sein, die dem Elternteil oder dem Kind zugeordnet ist. Aufgrund der Tatsache, dass diese Prozesse gleichzeitig ausgeführt werden, werden ihre Ausgabeleitungen auf ziemlich unvorhersehbare Weise vermischt. Darüber hinaus wird die Reihenfolge dieser Zeilen durch den CPU-Scheduler bestimmt. Wenn Sie dieses Programm also erneut ausführen, erhalten Sie möglicherweise ein völlig anderes Ergebnis.

  • Warum erstellen Sie ein Kind, das mit dem Elternteil identisch ist, was nützt es?

    Kar

    12. Juni 2009 um 4:57 Uhr

  • Es ist wie der Aufbau einer Armee gegen einen einzelnen Soldaten. Sie verzweigen sich, damit Ihr Programm mehr Anfragen gleichzeitig verarbeiten kann, anstatt eine nach der anderen.

    – Wolkenkopf

    12. Juni 2009 um 5:03 Uhr

  • fork() gibt 0 für das Kind und die PID des Kindes für das Elternteil zurück. Das Kind kann dann einen Aufruf wie exec() verwenden, um seinen Zustand durch ein neues Programm zu ersetzen. So werden Programme gestartet.

    – Tod Gamblin

    12. Juni 2009 um 5:03 Uhr

  • Die Prozesse sind fast identisch, aber es gibt viele subtile Unterschiede. Die eklatanten Unterschiede sind aktuelle PID und übergeordnete PID. Es gibt Probleme im Zusammenhang mit gehaltenen Sperren und gehaltenen Semaphoren. Die manpage fork() für POSIX listet 25 Unterschiede zwischen Eltern und Kind auf.

    – Jonathan Leffler

    12. Juni 2009 um 5:42 Uhr

  • @kar: Sobald Sie zwei Prozesse haben, können sie von dort aus separat fortgesetzt werden, und einer von ihnen könnte sich selbst (exex()) vollständig durch ein anderes Programm ersetzen.

    – Vatine

    26. September 2010 um 12:28 Uhr

Benutzeravatar von Alex Brown
Alex Braun

Sie müssen Fork wahrscheinlich nicht in der täglichen Programmierung verwenden, wenn Sie Anwendungen schreiben.

Selbst wenn Sie möchten, dass Ihr Programm ein anderes Programm startet, um eine Aufgabe zu erledigen, gibt es andere einfachere Schnittstellen, die hinter den Kulissen Forks verwenden, wie z. B. “System” in C und Perl.

Wenn Sie beispielsweise möchten, dass Ihre Anwendung ein anderes Programm wie bc startet, um einige Berechnungen für Sie durchzuführen, können Sie „system“ verwenden, um es auszuführen. Das System führt eine „Verzweigung“ durch, um einen neuen Prozess zu erstellen, und dann eine „Ausführung“, um diesen Prozess in bc umzuwandeln. Sobald bc abgeschlossen ist, gibt das System die Kontrolle an Ihr Programm zurück.

Sie können auch andere Programme asynchron ausführen, aber ich kann mich nicht erinnern, wie.

Wenn Sie Server, Shells, Viren oder Betriebssysteme schreiben, möchten Sie eher Fork verwenden.

  • Danke für system(). Ich habe darüber gelesen fork() weil ich möchte, dass mein C-Code ein Python-Skript ausführt.

    – Bohnentaxi

    13. Januar 2016 um 16:39 Uhr


1421480cookie-checkWas ist der Zweck von fork()?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy