GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Was passiert, wenn sich ein Thread verzweigt?

Der neue Prozess ist das Kind des Hauptthreads, der den Thread erstellt hat. Ich denke.

fork erstellt einen neuen Prozess. Der Elternteil eines Prozesses ist ein anderer Prozess, kein Thread. Der Elternprozess des neuen Prozesses ist also der alte Prozess.

Beachten Sie, dass der Kindprozess wegen fork nur einen Thread haben wird dupliziert nur den (Stack für den) Thread, der fork aufruft . (Dies ist nicht ganz richtig:Der gesamte Speicher wird dupliziert, aber der untergeordnete Prozess wird nur einen aktiven Thread haben.)

Wenn der übergeordnete Prozess zuerst beendet wird, wird der neue Prozess an den Init-Prozess angehängt.

Wenn der Elternteil zuerst eine SIGHUP beendet Das Signal wird an das Kind gesendet. Wenn das Kind aufgrund von SIGHUP nicht aussteigt es wird init erhalten als neuer Elternteil. Siehe auch die Manpages für nohup und signal(7) für ein bisschen mehr Informationen zu SIGHUP .

Und sein übergeordneter Thread ist der Haupt-Thread, nicht der Thread, der ihn erstellt hat.

Das übergeordnete Element eines Prozesses ist ein Prozess, kein bestimmter Thread, daher ist es nicht sinnvoll zu sagen, dass der Haupt- oder untergeordnete Thread das übergeordnete Element ist. Der gesamte Prozess ist der Elternteil.

Ein letzter Hinweis:Das Mischen von Fäden und Gabel muss mit Vorsicht erfolgen. Einige der Fallstricke werden hier besprochen.


Korrigieren Sie mich, wenn ich falsch liege.

Wird gemacht :)

Als fork() ein POSIX-Systemaufruf ist, ist sein Verhalten wohldefiniert:

Ein Prozess soll mit einem einzelnen Thread erstellt werden . Wenn ein Multithreading-Prozess fork() aufruft, soll der neue Prozess eine Kopie des aufrufenden Threads und seines gesamten Adressraums enthalten, möglicherweise einschließlich der Zustände von Mutexe und anderen Ressourcen. Folglich darf der untergeordnete Prozess zur Vermeidung von Fehlern nur asynchronsignalsichere Operationen ausführen, bis eine der exec-Funktionen aufgerufen wird.

https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html

Ein gegabeltes Kind ist ein exaktes Duplikat seines Elternteils, jedoch nur der Thread, der fork() aufgerufen hat im Elternteil existiert noch im Kind und ist der neue Hauptthread dieses Kindes, bis Sie exec() aufrufen .

Die POSIX-Beschreibung "soll mit einem einzigen Thread erstellt werden" ist irreführend, da die meisten Implementierungen tatsächlich ein exaktes Duplikat des übergeordneten Prozesses erstellen, sodass alle anderen Threads und ihr Speicher ebenfalls dupliziert werden, was bedeutet, dass die Threads tatsächlich vorhanden sind , sie können einfach nicht mehr ausgeführt werden, da das System ihnen niemals CPU-Zeit zuweist; sie fehlen tatsächlich in der Kernel-Thread-Scheduler-Tabelle.

Ein einfacheres mentales Bild ist das folgende:

Wenn der Elternteil fork aufruft, wird der gesamte Prozess für einen Moment eingefroren, atomar dupliziert, und dann wird der Elternteil als Ganzes entfrostet, aber im Kind wird nur der eine Thread, der fork aufgerufen hat, entfrostet, alles andere bleibt eingefroren.

Deshalb ist es nicht sicher, bestimmte Systemaufrufe zwischen fork() auszuführen und exec() wie auch vom POSIX-Standard angegeben. Idealerweise sollten Sie nicht viel mehr tun, als vielleicht Dateideskriptoren schließen oder duplizieren, Signalhandler setzen oder wiederherstellen und dann exec() aufrufen .


Was passiert jedoch, wenn ein Thread mit fork() einen neuen Prozess erstellt?

Ein neuer Prozess wird durch Kopieren des aufrufenden Threads erstellt Adressraum (nicht der gesamte Adressraum des Prozesses ). Es wird allgemein als schlechte Idee angesehen, weil es sehr schwer ist, es richtig zu machen. POSIX sagt, dass der untergeordnete Prozess (der in einem Multithread-Programm erstellt wurde) nur asynchronsignalsichere Funktionen aufrufen kann, bis er einen der exec* aufruft Funktionen.

Wenn sein Elternprozess zuerst fertig ist, wird der neue Prozess an initprocess angehängt.

Der untergeordnete Prozess wird normalerweise vom Init-Prozess geerbt. Wenn der übergeordnete Prozess ein Steuerprozess ist (zB Shell), dann verlangt POSIX:

Wenn der Prozess ein steuernder Prozess ist, soll das SIGHUP-Signal an jeden Prozess in der Vordergrundprozessgruppe des steuernden Terminals gesendet werden, das zu dem aufrufenden Prozess gehört.

Dies gilt jedoch nicht für die meisten Prozesse, da die meisten Prozesse keine Kontrollprozesse sind.

Und sein übergeordneter Thread ist der Haupt-Thread, nicht der Thread, der ihn erstellt hat.

Der Elternteil des gegabelten Kinds ist immer der Prozess, der fork() aufgerufen hat. PPID ist also der untergeordnete Prozess, der die PID Ihres Programms sein wird.


Das Problem ergibt sich aus dem Verhalten von fork(2) selbst. Immer wenn ein neuer untergeordneter Prozess mit fork(2) erstellt wird, erhält der neue Prozess einen neuen Speicheradressraum, aber alles im Speicher wird vom alten Prozess kopiert (bei Copy-on-Write ist das nicht 100 % wahr, aber die Semantik ist dieselbe). P>

Wenn wir fork(2) in einer Multithread-Umgebung aufrufen, ist der Thread, der den Aufruf durchführt, jetzt der Haupt-Thread im neuen Prozess, und alle anderen Threads, die im Elternprozess liefen, sind tot. Und alles, was sie taten, wurde genau so gelassen, wie es kurz vor dem Call-to-Fork(2) war.

Stellen Sie sich nun vor, dass diese anderen Threads vor dem Aufruf von fork(2) fröhlich ihre Arbeit erledigt haben und ein paar Millisekunden später tot sind. Was wäre, wenn etwas, was diese jetzt toten Threads getan haben, nicht genau so belassen werden sollte, wie es war?

Lassen Sie mich Ihnen ein Beispiel geben. Nehmen wir an, unser Haupt-Thread (derjenige, der fork(2) aufrufen wird) hat geschlafen, während viele andere Threads glücklich etwas Arbeit erledigten. Speicher zuweisen, darauf schreiben, daraus kopieren, in Dateien schreiben, in eine Datenbank schreiben und so weiter. Wahrscheinlich haben sie Speicher mit so etwas wie malloc(3) zugewiesen. Nun, es stellt sich heraus, dass malloc(3) intern einen Mutex verwendet Gewindesicherheit zu gewährleisten. Und genau das ist das Problem.

Was ist, wenn einer dieser Threads malloc(3) verwendet hat und die Sperre des Mutex genau in dem Moment erworben hat, in dem der Haupt-Thread fork(2) aufgerufen hat? Im neuen untergeordneten Prozess wird die Sperre immer noch gehalten – von einem inzwischen toten Thread, der sie niemals zurückgeben wird.

Der neue untergeordnete Prozess wird keine Ahnung haben, ob es sicher ist, malloc(3) zu verwenden oder nicht. Im schlimmsten Fall wird es malloc(3) aufrufen und blockieren, bis es die Sperre erhält, was niemals passieren wird, da der Thread, der es zurückgeben soll, tot ist. Und das ist nur malloc(3). Denken Sie an all die anderen möglichen Mutexe und Sperren in Datenbanktreibern, Dateiverwaltungsbibliotheken, Netzwerkbibliotheken und so weiter.

Für eine vollständige Erklärung können Sie über diesen Link gehen.


Linux
  1. Was passiert, wenn ich den Befehl Cat /proc/cpuinfo ausführe?

  2. Was passiert, wenn das Bandbreitenlimit überschritten wird?

  3. Was ist ein unterbrechungsfreier Prozess?

  4. Welcher Prozess verwendet alle meine Festplatten-E / A

  5. Was tun, wenn Strg + C einen Prozess nicht beenden kann?

Linux – Was passiert, wenn Sie ohne Zielpfad synchronisieren?

Was passiert, wenn mv unterbrochen wird?

Was ist ein gestoppter Prozess unter Linux?

Was ist ein Loop-Gerät bei der Montage?

Was sind ausstehende Signale?

Was passiert mit einem Multithread-Linux-Prozess, wenn er ein Signal erhält?