Wie verwende ich fork () in Unix? Warum nicht etwas von der Form fork (pointerToFunctionToRun)?

8

Ich habe Probleme, zu verstehen, wie man Unix fork() benutzt. Ich bin es gewohnt, bei der Parallelisierung Threads in meiner Anwendung zu erzeugen. Es ist immer etwas von der Form

%Vor%

Nun, als ich etwas über Unix fork() lernte, erhielt ich Beispiele für das Formular:

%Vor%

in dem der Code nach der Verzweigung "aufgeteilt" wird. Ich kann nicht verstehen, wie fork () nützlich sein kann. Warum hat fork () keine ähnliche Syntax wie oben CreateNewThread (), wo Sie ihm die Adresse einer Funktion übergeben, die Sie ausführen möchten?

Um etwas wie CreateNewThread () zu erreichen, müsste ich kreativ sein und etwas wie

machen %Vor%

Vielleicht ist das Problem, dass ich so gewohnt bin, Threads auf die .NET-Art zu erstellen, an die ich nicht klar denken kann. Was fehlt mir hier? Was sind die Vorteile von fork() über CreateNewThread() ?

PS: Ich weiß, dass fork() einen neuen Prozess erzeugt, während CreateNewThread() einen neuen Thread hervorbringt.

Danke

    
devoured elysium 12.11.2010, 01:46
quelle

8 Antworten

8

fork() sagt: "Kopiere den aktuellen Prozess in einen neuen Prozess und starte ihn von hier aus." Da der Code dann in zwei Prozessen ausgeführt wird, wird er tatsächlich zweimal zurückgegeben: einmal im übergeordneten Prozess (wobei er die Prozess-ID des untergeordneten Prozesses zurückgibt) und einmal im untergeordneten Element (wobei er null zurückgibt).

Es gibt eine Menge Einschränkungen bezüglich der Art und Weise, wie im untergeordneten Prozess nach fork() (siehe unten) sicher aufgerufen werden kann. Die Erwartung ist, dass der Aufruf fork() der erste Teil eines neuen Prozesses war, der eine neue ausführbare Datei mit einem eigenen Status ausführte. Teil zwei dieses Prozesses ist ein Aufruf von execve() oder einer seiner Varianten, der den Pfad zu einer ausführbaren Datei angibt, die in den gerade ausgeführten Prozess geladen werden soll, die Argumente, die diesem Prozess zur Verfügung gestellt werden, und die Umgebungsvariablen verarbeiten. (Es gibt nichts, was Sie daran hindern könnte, die aktuell laufende ausführbare Datei erneut auszuführen und ein Flag bereitzustellen, das es dazu bringt, dort aufzugreifen, wo der Elternteil aufgehört hat, falls Sie das wirklich wollen.)

Das UNIX fork()-exec() dance entspricht in etwa dem Windows CreateProcess() . Eine neuere Funktion ist noch mehr wie folgt: posix_spawn() .

Als praktisches Beispiel für die Verwendung von fork() sollten Sie eine Shell wie bash in Erwägung ziehen. fork() wird ständig von einer Befehlsshell verwendet. Wenn Sie der Shell mitteilen, dass sie ein Programm ausführen soll (z. B. echo "hello world" ), gibt sie sich selbstständig und führt dann das Programm aus. Eine Pipeline ist eine Sammlung von verzweigten Prozessen mit stdout und stdin , die entsprechend vom übergeordneten Objekt zwischen fork() und exec() eingerichtet wurden.

Wenn Sie einen neuen Thread erstellen möchten, sollten Sie die Posix-Thread-Bibliothek verwenden. Sie erstellen einen neuen Posix-Thread (Pthread) mit pthread_create() . Ihr CreateNewThread() Beispiel würde so aussehen:

%Vor%

Bevor Threads verfügbar waren, war fork() das, was UNIX dem Multithreading am nächsten brachte. Jetzt, da Threads verfügbar sind, ist die Verwendung von fork() fast vollständig darauf beschränkt, einen neuen Prozess zum Ausführen einer anderen ausführbaren Datei hervorzubringen.

unten: Die Einschränkungen bestehen darin, dass fork() Multithreading voraussetzt, sodass nur der Thread, der fork() aufruft, im untergeordneten Prozess ausgeführt wird. Pro POSIX :

  

Ein Prozess soll mit einem einzigen Thread erstellt werden. Wenn ein Multithreadprozess fork () aufruft, muss der neue Prozess eine Replik des aufrufenden Threads und seinen gesamten Adressraum enthalten, möglicherweise einschließlich der Status von Mutexen und anderen Ressourcen. Um Fehler zu vermeiden, darf der Kindprozess daher nur async-signalsichere Operationen ausführen, bis eine der exec-Funktionen aufgerufen wird. [THR] [Option Start] Mit der Funktion pthread_atfork () können Fork-Handler eingerichtet werden, um Anwendungsinvarianten über fork () -Aufrufe hinweg zu pflegen. [Ende der Option]

     

Wenn die Anwendung fork () von einem Signalhandler aufruft und einer der von pthread_atfork () registrierten fork-Handler eine Funktion aufruft, die nicht asynch-signal-sicher ist, ist das Verhalten undefiniert.

Da jede Bibliotheksfunktion, die Sie aufrufen, einen Thread in Ihrem Auftrag erzeugt haben könnte, ist die paranoide Annahme, dass Sie immer darauf beschränkt sind, async-signalsichere Operationen im Kindprozess zwischen dem Aufruf von fork() und exec() .     

Jeremy W. Sherman 12.11.2010, 02:22
quelle
7

Abgesehen von der Geschichte gibt es einige grundlegende Unterschiede in Bezug auf den Besitz von Ressourcen und die Lebensdauer zwischen Prozessen und Threads.

Wenn Sie forkieren, belegt der neue Prozess einen vollständig separaten Speicherplatz. Das ist ein sehr wichtiger Unterschied zum Erstellen eines neuen Threads. In Multithread-Anwendungen müssen Sie berücksichtigen, wie Sie auf freigegebene Ressourcen zugreifen und diese bearbeiten. Verarbeitete, die gegabelt wurden, müssen Ressourcen explizit freigeben, indem sie Interprozess-Mittel wie gemeinsamen Speicher, Pipes, Remoteprozeduraufrufe, Semaphore usw. verwenden.

Ein weiterer Unterschied ist, dass fork () 'ed-Kinder ihre Eltern überleben können, während alle Threads sterben, wenn der Prozess endet.

In einer Client-Server-Architektur, in der sehr, sehr lange Uptime erwartet wird, könnte die Verwendung von fork () anstelle von Threads eine gültige Strategie zur Bekämpfung von Speicherlecks sein. Anstatt sich Gedanken über das Aufräumen von Speicherlecks in Ihren Threads zu machen, geben Sie einfach einen neuen untergeordneten Prozess aus, um jede Clientanforderung zu verarbeiten, und töten dann das untergeordnete Element, wenn es fertig ist. Die einzige Quelle von Speicherlecks wäre dann der Elternprozess, der Ereignisse versendet.

Eine Analogie: Sie können sich vorstellen, dass Threads als öffnende Tabs in einem einzigen Browserfenster angezeigt werden, während das Forking wie das Öffnen separater Browserfenster aussieht.

    
Kim Burgaard 12.11.2010 02:22
quelle
5

Es wäre sinnvoller zu fragen, warum CreateNewThread nicht nur eine Thread-ID zurückgibt wie fork() ... nachdem fork() einen Präzedenzfall gesetzt hat. Deine Meinung ist nur dadurch gefärbt, dass du eins vor dem anderen gesehen hast. Machen Sie einen Schritt zurück und betrachten Sie, dass fork() den Prozess dupliziert und die Ausführung fortsetzt ... was ist besser als bei der nächsten Anweisung? Warum Dinge komplizieren, indem man einen Funktionsaufruf hinzufügt (und dann einen, der nur void * nimmt)?

Ihr Kommentar zu Mike sagt: "Ich kann nicht verstehen, in welchen Kontexten Sie es verwenden möchten." Grundsätzlich verwenden Sie es, wenn Sie möchten:

  • Führen Sie einen anderen Prozess mit der exec-Funktionsfamilie
  • aus
  • selbständig eine Parallelverarbeitung durchführen (in Bezug auf Speichernutzung, Signalverarbeitung, Ressourcen, Sicherheit)
    • zum Beispiel kann jeder Prozess intrusive Grenzen der Anzahl der Dateideskriptoren haben, die er verwalten kann, oder auf einem 32-Bit-System - die Menge an Speicher: ein zweiter Prozess kann sich die Arbeit teilen, während er seine eigenen Ressourcen bekommt >

BTW / die Verwendung von UNIX / Linux bedeutet nicht, dass Sie Threads für fork () ing-Prozesse aufgeben müssen ... Sie können pthread_create () und verwandte Funktionen verwenden, wenn Sie mit dem Threading-Paradigma vertraut sind. p>     

Tony Delroy 12.11.2010 01:51
quelle
2

Den Unterschied zwischen dem Erstellen eines Prozesses und einem Thread für eine Sekunde beiseite legen: Grundsätzlich ist fork () ein fundamentaleres Primitiv. Während SpawnNewThread etwas Hintergrundarbeit leisten muss, um den Programmzähler an die richtige Stelle zu bringen, macht fork keine solche Arbeit, sondern kopiert (oder kopiert virtuell) Ihren Programmspeicher und setzt den Zähler fort.

    
Mike Axiak 12.11.2010 01:51
quelle
1

Fork war sehr, sehr lange bei uns. Gabel wurde gedacht, bevor die Idee, "einen Thread zu starten, der eine bestimmte Funktion ausführt", ein Schimmer in jedermanns Auge war.

Leute benutzen fork nicht, weil es 'besser' ist, wir benutzen es, weil es die einzige und nicht privilegierte Prozess-Erstellungsfunktion im Benutzermodus ist, die über alle Variationen von Linux hinweg funktioniert. Wenn Sie einen Prozess erstellen möchten, müssen Sie fork aufrufen. Und für einige Zwecke ist ein Prozess das, was Sie brauchen, kein Thread.

Sie könnten erwägen, die frühen Papiere zu diesem Thema zu recherchieren.

    
bmargulies 12.11.2010 01:54
quelle
1

Es ist erwähnenswert, dass die Verarbeitung nicht genau dasselbe ist wie multi threading . Der neue Prozess, der von fork erstellt wird, hat wenig Kontext mit dem alten, was sich von dem für Threads unterscheidet.

Sehen wir uns also das Unixy -Thread -System an: pthread_create hat eine ähnliche Semantik wie CreateNewThread .

Oder, um es umzudrehen, schauen wir uns die Fenster (oder Java oder ein anderes System, das seinen Lebensunterhalt mit Threads verdient) an, um einen Prozess zu erzeugen, der identisch ist mit dem, den Sie gerade ausführen (was fork tut) auf Unix) ... naja, wir könnten außer, dass es keinen gibt: das ist nicht Teil des All-Threads-All-Time-Modells. (Was ist keine schlechte Sache, wohlgemerkt, nur anders).

    
dmckee 12.11.2010 02:21
quelle
1

Du fork , wenn du mehr als eine Sache gleichzeitig haben willst. Es heißt Multitasking und ist wirklich nützlich.

Hier ist zum Beispiel ein Telnetish-ähnliches Programm:

%Vor%

Sehen Sie, wie einfach das ist?

    
tchrist 12.11.2010 02:10
quelle
0

Fork () wird am häufigsten verwendet, um einen Server für jeden neuen Client, der connect () s verwendet, zu klonen (weil der neue Prozess alle Dateideskriptoren in welchem ​​Zustand auch immer erbt). Aber ich habe es auch verwendet, um einen neuen (lokal laufenden) Dienst auf Anforderung von einem Kunden zu initiieren. Dieses Schema wird am besten mit zwei Aufrufen von fork () ausgeführt - einer bleibt in der übergeordneten Sitzung, bis der Server aktiv ist und eine Verbindung herstellen kann, der andere (ich mache ihn vom untergeordneten Server ab) wird zum Server und verlässt die Sitzung des Elterns so kann es zB nicht mehr von SIGQUIT erreicht werden.

    
Greg Jaxon 15.11.2013 19:30
quelle

Tags und Links