Welche Themen teilen sich im Allgemeinen?


20

Nun, das ist eine allgemeine Frage. Und wenn jemand es implementierungsspezifisch machen möchte, dann bevorzuge ich Unix-bezogene Sachen. Aber zuerst müssen folgende Probleme allgemein bekannt sein:

Ich habe gelesen, dass ein einzelner Prozess mehrere Threads haben kann. Mehrere Threads desselben Prozesses teilen sich die Dinge. Ich möchte wissen, was sie teilen und was nicht. Der betrachtete Prozess besteht aus Adressraum, Stack, Heap, globalen Variablen, Code, Daten und Betriebssystemressourcen. Was wird von den Threads gemeinsam genutzt? Ich habe folgende Vermutungen:

  1. Globale Variablen - Ich habe gelesen, dass Thread-Shares globale Variablen sind. Auch während der Programmierung in Java und C # habe ich Threads erstellt, um Variablen auf Klassenebene gemeinsam zu nutzen. Ich glaube also, dass die Threads globale Variablen gemeinsam haben (obwohl ich nicht sicher bin, ob die Konzepte in Programmiersprachen auf hoher Ebene sich in Fakten auf niedriger Betriebssystemebene übersetzen lassen).

  2. Heap - Da die globale Variable im Heap gespeichert ist, wird der Heap zwischen den Threads geteilt.

  3. Stack - Da jeder Thread eine eigene Ausführungssequenz / einen eigenen Ausführungscode haben kann, muss er einen eigenen Stack haben, auf den er den Inhalt seines Programmzählers pushen / einfügen kann (wenn beispielsweise Funktionsaufrufe und Rückgaben auftreten). Threads desselben Prozesses teilen sich also nicht den Stapel.

Jetzt bin ich mir nicht sicher, wie ich die folgenden Dinge teilen soll

  1. Adressraum - Nicht sicher, was genau unter Adressraum zählt. Der Adressraum wird jedoch im Allgemeinen im Kontext von Prozessen und nicht von Threads verwendet. Und da sich alle Threads desselben Prozesses im selben Adressraum wie der übergeordnete Prozess befinden, wird gesagt, dass sich Threads den Adressraum teilen. (Aber dann behalten sie unterschiedlichen Stapel innerhalb des gleichen Adressraums bei?)

  2. Betriebssystemressourcen - Ich denke, dies kann sehr implementierungsspezifisch sein. Beispielsweise kann der übergeordnete Prozess ausgewählten Threads und nicht allen Threads die gleiche Datei zuweisen. Oder irre ich mich und Betriebssystemressourcen bedeuten etwas anderes als Dateien?

  3. Code - Threads können unterschiedlichen Code haben, so dass das Teilen von Code nicht immer der Fall ist.

  4. Daten - Unsicher, was unter Daten zu beachten ist. Stellen Sie jedoch sicher, dass die globalen Variablen von den Threads gemeinsam genutzt werden. Und stellen Sie sicher, dass lokale Variablen nicht auf ähnliche Weise gemeinsam genutzt werden.

Insgesamt bin ich sehr verwirrt über vage Begriffe, Super-Generalisierungen in den Büchern über Betriebssysteme und zusätzliche implementierungsspezifische Details, die online zur Verfügung gestellt werden. Also versuche ich eine Antwort zu finden, die mich befriedigen kann.

Antworten:


13

Im Allgemeinen hat jeder Thread seine eigenen Register (einschließlich seines eigenen Programmzählers), seinen eigenen Stapelzeiger und seinen eigenen Stapel. Alles andere wird von den Threads gemeinsam genutzt, die einen Prozess gemeinsam nutzen.

Insbesondere besteht ein Prozess im Allgemeinen aus einer Reihe von Threads, die sich einen Adressraum, einen Heap, statische Daten und Codesegmente sowie Dateideskriptoren * teilen .

Ein Adressraum ist einfach die Zuordnung von logischen Adressen zu bestimmten Teilen des physischen Speichers. Wenn wir also sagen, dass alle Threads in einem Prozess denselben Adressraum haben, meinen wir, dass beim Zugriff auf eine Variable fooim globalen Bereich alle Threads dieselbe Variable sehen. In ähnlicher Weise können die Threads zu einem bestimmten Zeitpunkt alle einen anderen Punkt im Code ausführen, es ist ihnen jedoch gestattet, die globale Funktion aufzurufen bar(), die für jeden Thread im Prozess der gleichen Funktion entspricht.

Die meisten modernen Betriebssysteme haben den Begriff des Thread-lokalen Speichers hinzugefügt. Hierbei handelt es sich um Variablen des globalen Bereichs, die nicht gemeinsam genutzt werden. Das übliche Beispiel für die Verwendung ist für die Variable errno. Dies ist eine einzelne Variable mit globalem Gültigkeitsbereich, aber in den meisten modernen Betriebssystemen erhält jeder Thread eine eigene lokale Kopie, sodass ein Fehler in einem Bibliotheksaufruf für einen Thread das Verhalten anderer Threads nicht beeinträchtigt.

* Es gibt einige zusätzliche Prozesszustände, die von allen Threads in einem Prozess gemeinsam genutzt werden, z. B. die Prozess-ID, die Signalverarbeitung und Dateisperren. Eine vollständige Liste der von Threads gemeinsam genutzten Prozesszustände finden Sie in der Dokumentation der jeweiligen Threading-Implementierung. Zum Beispiel die Manpage pthreads .


4

Die Themen kommen aus zwei Perspektiven: Betriebssysteme und Programmiersprachen. In beiden Fällen variieren die Attribute eines Threads.

Eine minimale Definition eines Threads ist, dass es sich um Dinge handelt, die nacheinander ablaufen.

In einem typischen Maschinenausführungsmodell verfügt jeder Thread über einen eigenen Satz von Allzweckregistern und einen eigenen Programmzähler. Wenn der Computer ein bestimmtes Register als Stapelzeiger ausgibt, gibt es eine Kopie pro Thread.

Aus Sicht des Betriebssystems muss ein Betriebssystem mindestens so viel tun, um Threads zu unterstützen, dass es die Möglichkeit bietet, zwischen ihnen zu wechseln. Dies kann entweder automatisch geschehen ( präventives Multitasking oder nur, wenn der Thread eine explizite Anforderung stellt (kooperatives Multitasking; in diesem Fall werden Threads manchmal als Fasern bezeichnet ) Das Wechseln zwischen Threads erfordert mindestens das Speichern der Registerwerte des alten Threads und das Wiederherstellen der Registerwerte des neuen Threads.

In einem Multitasking-Betriebssystem, das die Isolierung zwischen Aufgaben (oder Prozessen) ermöglicht , können Sie diese Begriffe als Synonyme in einem Betriebssystemkontext behandeln. Jede Aufgabe verfügt über eigene Ressourcen, insbesondere über einen Adressraum, aber auch über offene Dateien, Berechtigungen usw. Die Isolierung hat bereitgestellt durch das Betriebssystem zu Kernel , ein Unternehmen , das ist über Prozesse. Jede Aufgabe hat normalerweise mindestens einen Thread - eine Aufgabe, die keinen Code ausführt, ist wenig nützlich. Das Betriebssystem unterstützt möglicherweise mehrere Threads in derselben Task. Zum Beispiel das Original-Unix nicht. Eine Aufgabe kann immer noch mehrere Threads ausführen, indem zwischen ihnen gewechselt wird. Hierfür sind keine besonderen Berechtigungen erforderlich. Dies wird als " Benutzer-Threads" bezeichnet”, Insbesondere im Unix-Kontext. Heutzutage bieten die meisten Unix-Systeme Kernel-Threads, insbesondere weil nur so mehrere Threads desselben Prozesses auf verschiedenen Prozessoren ausgeführt werden können.

Abgesehen von der Rechenzeit sind die meisten Betriebssystemressourcen an Aufgaben und nicht an Threads gebunden. Einige Betriebssysteme (z. B. Linux) begrenzen Stapel explizit. In diesem Fall hat jeder Thread einen eigenen Thread. Aber es gibt Betriebssysteme, in denen der Kernel nichts über Stacks weiß, sie sind nur ein Teil des Haufens, was das betrifft. Der Kernel verwaltet in der Regel auch einen Kernelkontext für jeden Thread. Hierbei handelt es sich um eine Datenstruktur, die Informationen darüber enthält, was der Thread gerade tut. Auf diese Weise kann der Kernel mehrere Threads gleichzeitig verarbeiten, die in einem Systemaufruf blockiert wurden.

Für das Betriebssystem führen die Threads einer Task denselben Code aus, befinden sich jedoch an unterschiedlichen Positionen in diesem Code (unterschiedliche Programmzählerwerte). Es kann vorkommen oder nicht, dass bestimmte Teile des Codes eines Programms immer in einem bestimmten Thread ausgeführt werden, aber es gibt normalerweise allgemeinen Code (z. B. Dienstprogrammfunktionen), der von einem beliebigen Thread aus aufgerufen werden kann. Alle Threads sehen dieselben Daten, andernfalls würden sie als unterschiedliche Aufgaben betrachtet. Wenn auf einige Daten nur von einem bestimmten Thread zugegriffen werden kann, ist dies in der Regel ausschließlich die Programmiersprache, nicht das Betriebssystem.

In den meisten Programmiersprachen wird der Speicher von Threads desselben Programms gemeinsam genutzt. Dies ist ein gemeinsames Speichermodell für die gleichzeitige Programmierung. Es ist sehr beliebt, aber auch sehr fehleranfällig, da der Programmierer vorsichtig sein muss, wenn mehrere Threads auf dieselben Daten zugreifen können, da Race-Bedingungen auftreten können. Beachten Sie, dass auch lokale Variablen von mehreren Threads gemeinsam genutzt werden können: „Lokale Variable“ bedeutet (normalerweise) eine Variable, deren Name nur während einer Ausführung einer Funktion gültig ist. Ein anderer Thread kann jedoch einen Zeiger auf diese Variable abrufen und darauf zugreifen.

Es gibt auch Programmiersprachen, in denen jeder Thread seinen eigenen Speicher hat, und die Kommunikation zwischen ihnen erfolgt durch Senden von Nachrichten über Kommunikationskanäle. Dies ist das Message-Passing- Modell für die gleichzeitige Programmierung. Erlangist die Hauptprogrammiersprache, die sich auf die Weitergabe von Nachrichten konzentriert. Die Ausführungsumgebung ist sehr einfach zu handhaben und unterstützt Programme, die mit vielen kurzlebigen Threads geschrieben wurden, im Gegensatz zu den meisten anderen Programmiersprachen, bei denen das Erstellen eines Threads eine relativ teure Operation ist und die Laufzeitumgebung keine sehr großen Threads unterstützen kann Anzahl der Threads gleichzeitig. Die sequentielle Teilmenge von Erlang (der Teil der Sprache, der innerhalb eines Threads vorkommt, insbesondere die Datenmanipulation) ist (meistens) rein funktional; Auf diese Weise kann ein Thread eine Nachricht an einen anderen Thread senden, der einige Daten enthält, und keiner der Threads muss sich Sorgen machen, dass die Daten vom anderen Thread geändert werden, während er sie verwendet.

Einige Sprachen kombinieren die beiden Modelle, indem sie thread-lokalen Speicher mit oder ohne Typsystem anbieten, um den thread-lokalen Speicherort von den globalen zu unterscheiden. Thread-lokaler Speicher ist normalerweise eine praktische Funktion, mit der ein Variablenname verschiedene Speicherorte in verschiedenen Threads bestimmen kann.

Einige (schwierige) Folgemaßnahmen, die für das Verständnis der Themen von Interesse sein können:

  • Was muss ein Kernel mindestens tun, um mehrere Threads zu unterstützen?
  • Was ist in einer Multiprozessorumgebung erforderlich, um einen Thread von einem Prozessor auf einen anderen zu migrieren?
  • Was wäre nötig, um kooperatives Multithreading ( Coroutinen ) in Ihrer bevorzugten Programmiersprache ohne Unterstützung durch das Betriebssystem und ohne die integrierte Unterstützung zu implementieren ? (Beachten Sie, dass die meisten Programmiersprachen nicht über die erforderlichen Grundfunktionen verfügen, um Coroutinen in einem einzelnen Thread zu implementieren.)
  • Wie könnte eine Programmiersprache aussehen, wenn sie Parallelität, aber kein (explizites) Konzept von Threads hätte? (Paradebeispiel: der Pi-Kalkül .)

Das ist das interessanteste, was ich seit Monaten gelesen habe.
JSON

2

Das hängt davon ab. Wenn Sie Threads in Betracht ziehen, wie sie beispielsweise von POSIX (und von Unix-Systemen angeboten) oder von Windows (nicht mit dem späteren Zeitpunkt vertraut) definiert wurden, müssen Sie speziell nachfragen, dann erhalten Sie eine Antwort (im Wesentlichen wie in @WanderingLogic beschrieben). Linux hat eine eigene Vorstellung von Threads, die den nicht standardmäßigen clone(2)Systemaufruf verwenden. Es bietet eine fein abgestimmte Kontrolle darüber, was Eltern und Kind gemeinsam haben. Es geht so weit, das Interne zu haben fork(2)und im vfork(2)Wesentlichen zu umschließen clone(2)und es mit bestimmten Flags aufzurufen, dh, Sie können "Threads" erstellen, die mit dem übergeordneten Element so gut wie nichts gemeinsam haben. Details finden Sie auf der Manual-Seite. Sie sind online verfügbar, z . B. hier . Ja, Linux bietet POSIX-ähnliche Threads, aber noch viel mehr.


0

Themen teilen:

  • Adressraum
  • Haufen
  • Statische Daten
  • Codesegmente
  • Dateideskriptoren
  • Globale Variablen
  • Untergeordnete Prozesse
  • Ausstehende Alarme
  • Signale und Signalhandler
  • Buchhaltungsinformationen

Threads haben ihre eigenen:

  • Programm zähler
  • Register
  • Stapel
  • Zustand
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.