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?
Was ist der Zweck von fork()?
Tod Gamblin
fork()
So erstellen Sie neue Prozesse in Unix. Wenn du anrufst fork
erstellen 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
:
- Dein Hülse Verwendet
fork
um die Programme auszuführen, die Sie über die Befehlszeile aufrufen. - 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. - 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. 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.- Verwendung von Skriptsprachen
fork
indirekt, um untergeordnete Prozesse zu starten. Zum Beispiel jedes Mal, wenn Sie einen Befehl wie verwendensubprocess.Popen
in Python, dufork
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
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
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)
-
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
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
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
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 gelesenfork()
weil ich möchte, dass mein C-Code ein Python-Skript ausführt.– Bohnentaxi
13. Januar 2016 um 16:39 Uhr
Damit all die Essphilosophen nicht verhungern.
– kenj0418
12. Juni 2009 um 4:51 Uhr