Es war einmal, als Sie zum Schreiben eines x86-Assemblers beispielsweise Anweisungen hatten, die besagten: "Laden Sie das EDX-Register mit dem Wert 5", "Erhöhen Sie das EDX-Register" usw.
Bei modernen CPUs mit 4 Kernen (oder sogar mehr) sieht es auf Maschinencodeebene nur so aus, als gäbe es 4 separate CPUs (dh gibt es nur 4 verschiedene "EDX" -Register)? Wenn ja, wenn Sie "Inkrementieren des EDX-Registers" sagen, was bestimmt, welches EDX-Register der CPU inkrementiert wird? Gibt es jetzt im x86-Assembler ein "CPU-Kontext" - oder "Thread" -Konzept?
Wie funktioniert die Kommunikation / Synchronisation zwischen den Kernen?
Wenn Sie ein Betriebssystem geschrieben haben, welcher Mechanismus wird über Hardware verfügbar gemacht, damit Sie die Ausführung auf verschiedenen Kernen planen können? Handelt es sich um spezielle privilegierte Anweisungen?
Wenn Sie eine optimierende Compiler- / Bytecode-VM für eine Multicore-CPU schreiben würden, was müssten Sie beispielsweise speziell über x86 wissen, damit Code generiert wird, der auf allen Kernen effizient ausgeführt wird?
Welche Änderungen wurden am x86-Maschinencode vorgenommen, um die Multi-Core-Funktionalität zu unterstützen?
Antworten:
Dies ist keine direkte Antwort auf die Frage, sondern eine Antwort auf eine Frage, die in den Kommentaren erscheint. Im Wesentlichen stellt sich die Frage, welche Unterstützung die Hardware für den Multithread-Betrieb bietet.
Nicholas Flynt hatte es richtig gemacht , zumindest in Bezug auf x86. In einer Umgebung mit mehreren Threads (Hyper-Threading, Multi-Core oder Multi-Prozessor ) beginnt der Bootstrap-Thread (normalerweise Thread 0 in Core 0 in Prozessor 0) mit dem Abrufen von Code von der Adresse
0xfffffff0
. Alle anderen Threads werden in einem speziellen Ruhezustand namens Wait-for-SIPI gestartet . Im Rahmen seiner Initialisierung sendet der primäre Thread über den APIC einen speziellen Interprozessor-Interrupt (IPI), der als SIPI (Startup IPI) bezeichnet wird, an jeden Thread, der sich in WFS befindet. Das SIPI enthält die Adresse, von der dieser Thread mit dem Abrufen von Code beginnen soll.Dieser Mechanismus ermöglicht es jedem Thread, Code von einer anderen Adresse auszuführen. Alles, was benötigt wird, ist Software-Unterstützung für jeden Thread, um seine eigenen Tabellen und Messaging-Warteschlangen einzurichten. Das Betriebssystem verwendet diese , um die eigentliche Multithread-Planung durchzuführen.
In Bezug auf die eigentliche Baugruppe gibt es, wie Nicholas schrieb, keinen Unterschied zwischen den Baugruppen für eine Anwendung mit einem oder mehreren Threads. Jeder logische Thread hat einen eigenen Registersatz. Schreiben Sie also:
wird nur
EDX
für den aktuell ausgeführten Thread aktualisiert . Es gibt keine Möglichkeit, einenEDX
anderen Prozessor mit einer einzigen Montageanweisung zu ändern . Sie benötigen eine Art Systemaufruf, um das Betriebssystem aufzufordern, einen anderen Thread anzuweisen, Code auszuführen, der seinen eigenen aktualisiertEDX
.quelle
Beispiel für ein minimal lauffähiges Intel x86-Baremetall
Lauffähiges Bare-Metal-Beispiel mit allen erforderlichen Boilerplates . Alle wichtigen Teile werden unten behandelt.
Getestet unter Ubuntu 15.10 QEMU 2.3.0 und Lenovo ThinkPad T400 als echter Hardware-Gast .
Das Intel Manual Volume 3 System Programming Guide - 325384-056US September 2015 behandelt SMP in den Kapiteln 8, 9 und 10.
Tabelle 8-1. "Broadcast INIT-SIPI-SIPI-Sequenz und Auswahl von Timeouts" enthält ein Beispiel, das im Grunde nur funktioniert:
Auf diesem Code:
Die meisten Betriebssysteme machen die meisten dieser Vorgänge ab Ring 3 (Benutzerprogramme) unmöglich.
Sie müssen also Ihren eigenen Kernel schreiben, um frei damit spielen zu können: Ein Userland Linux-Programm funktioniert nicht.
Zunächst wird ein einzelner Prozessor ausgeführt, der als Bootstrap-Prozessor (BSP) bezeichnet wird.
Es muss die anderen (als Application Processors (AP) bezeichnet) durch spezielle Interrupts, sogenannte Inter Processor Interrupts (IPI), aufwecken .
Diese Interrupts können durch Programmieren des Advanced Programmable Interrupt Controller (APIC) über das Interrupt-Befehlsregister (ICR) erfolgen.
Das Format des ICR ist dokumentiert unter: 10.6 "AUSGABE VON INTERPROCESSOR-INTERRUPTS"
Das IPI erfolgt, sobald wir an das ICR schreiben.
ICR_LOW ist in 8.4.4 "MP-Initialisierungsbeispiel" definiert als:
Der magische Wert
0FEE00300
ist die Speicheradresse des ICR, wie in Tabelle 10-1 "Local APIC Register Address Map" dokumentiert.Im Beispiel wird die einfachste Methode verwendet: Sie richtet den ICR so ein, dass Broadcast-IPIs gesendet werden, die an alle anderen Prozessoren außer dem aktuellen geliefert werden.
Es ist aber auch möglich und von einigen empfohlen , Informationen über die Prozessoren über spezielle Datenstrukturen abzurufen, die vom BIOS eingerichtet wurden, wie z. B. ACPI-Tabellen oder Intels MP-Konfigurationstabelle, und nur diejenigen zu aktivieren, die Sie einzeln benötigen.
XX
in000C46XXH
codiert die Adresse des ersten Befehls, den der Prozessor ausführen wird als:Denken Sie daran, dass CS Adressen mit multipliziert
0x10
, sodass die tatsächliche Speicheradresse des ersten Befehls wie folgt lautet:Wenn zum Beispiel
XX == 1
der Prozessor bei startet0x1000
.Wir müssen dann sicherstellen, dass an diesem Speicherort 16-Bit-Realmoduscode ausgeführt werden kann, z. B.:
Die Verwendung eines Linkerskripts ist eine weitere Möglichkeit.
Die Verzögerungsschleifen sind ein nerviger Teil, um an die Arbeit zu gehen: Es gibt keine supereinfache Möglichkeit, solche Schlafzeiten präzise durchzuführen.
Mögliche Methoden sind:
Verwandte Themen : Wie kann ich eine Nummer auf dem Bildschirm anzeigen und mit der DOS x86-Assembly eine Sekunde lang schlafen?
Ich denke, der anfängliche Prozessor muss sich im geschützten Modus befinden, damit dies funktioniert, wenn wir an eine Adresse schreiben,
0FEE00300H
die für 16-Bit zu hoch istUm zwischen Prozessoren zu kommunizieren, können wir einen Spinlock für den Hauptprozess verwenden und die Sperre vom zweiten Kern aus ändern.
Wir sollten sicherstellen, dass das Zurückschreiben des Speichers erfolgt, z
wbinvd
. B. durch .Geteilter Zustand zwischen Prozessoren
8.7.1 "Status der logischen Prozessoren" sagt:
Die Cache-Freigabe wird unter folgender Adresse erläutert:
Intel-Hyperthreads haben eine größere Cache- und Pipeline-Freigabe als separate Kerne: /superuser/133082/hyper-threading-and-dual-core-whats-the-difference/995858#995858
Linux-Kernel 4.2
Die Hauptinitialisierungsaktion scheint bei zu sein
arch/x86/kernel/smpboot.c
.ARM Minimal Runnable Baremetal Beispiel
Hier stelle ich ein minimal lauffähiges ARMv8 aarch64-Beispiel für QEMU bereit:
GitHub stromaufwärts .
Zusammenbauen und ausführen:
In diesem Beispiel setzen wir CPU 0 in eine Spinlock-Schleife und sie wird nur beendet, wenn CPU 1 den Spinlock freigibt.
Nach dem Spinlock führt CPU 0 dann einen Semihost-Exit-Aufruf durch , wodurch QEMU beendet wird.
Wenn Sie QEMU mit nur einer CPU starten
-smp 1
, hängt die Simulation für immer am Spinlock.CPU 1 wird mit der PSCI-Schnittstelle aufgeweckt, weitere Details unter: ARM: Start / Wakeup / Bringup die anderen CPU-Kerne / APs und Startadresse für die Ausführung übergeben?
Die Upstream-Version hat auch einige Verbesserungen, damit sie auf gem5 funktioniert, sodass Sie auch mit Leistungsmerkmalen experimentieren können.
Ich habe es nicht auf echter Hardware getestet und bin mir nicht sicher, wie portabel dies ist. Die folgende Raspberry Pi-Bibliographie könnte von Interesse sein:
Dieses Dokument enthält einige Anleitungen zur Verwendung von ARM-Synchronisationsprimitiven, mit denen Sie unterhaltsame Dinge mit mehreren Kernen ausführen können : http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
Getestet unter Ubuntu 18.10, GCC 8.2.0, Binutils 2.31.1, QEMU 2.12.0.
Nächste Schritte für eine bequemere Programmierbarkeit
Die vorherigen Beispiele aktivieren die sekundäre CPU und führen eine grundlegende Speichersynchronisierung mit dedizierten Anweisungen durch. Dies ist ein guter Anfang.
Um die Programmierung von Multicore-Systemen wie POSIX zu vereinfachen
pthreads
, müssten Sie sich jedoch auch mit den folgenden Themen befassen:Setup unterbricht und führt einen Timer aus, der regelmäßig entscheidet, welcher Thread jetzt ausgeführt wird. Dies wird als präventives Multithreading bezeichnet .
Ein solches System muss auch Thread-Register speichern und wiederherstellen, wenn sie gestartet und gestoppt werden.
Es ist auch möglich, nicht präemptive Multitasking-Systeme zu haben. Möglicherweise müssen Sie jedoch Ihren Code so ändern, dass jeder Thread (z. B. bei einer
pthread_yield
Implementierung) nachgibt , und es wird schwieriger, die Arbeitslast auszugleichen.Hier sind einige vereinfachte Beispiele für Bare-Metal-Timer:
mit Gedächtniskonflikten umgehen. Insbesondere benötigt jeder Thread einen eindeutigen Stapel, wenn Sie in C oder anderen Hochsprachen codieren möchten.
Sie könnten Threads einfach auf eine feste maximale Stapelgröße beschränken, aber der schönere Weg, damit umzugehen, ist das Paging, das effiziente Stapel mit "unbegrenzter Größe" ermöglicht.
Hier ist ein naives aarch64-Baremetall-Beispiel, das explodieren würde, wenn der Stapel zu tief wächst
Das sind einige gute Gründe, den Linux-Kernel oder ein anderes Betriebssystem zu verwenden :-)
Grundelemente für die Userland-Speichersynchronisation
Obwohl das Starten / Stoppen / Verwalten von Threads im Allgemeinen außerhalb des Bereichs des Benutzerlandes liegt, können Sie Assembly-Anweisungen von Userland-Threads verwenden, um Speicherzugriffe ohne potenziell teurere Systemaufrufe zu synchronisieren.
Sie sollten natürlich lieber Bibliotheken verwenden, die diese Grundelemente auf niedriger Ebene portabel umschließen. Der C ++ - Standard selbst hat große Fortschritte bei den
<mutex>
und<atomic>
-Headern und insbesondere bei gemachtstd::memory_order
. Ich bin mir nicht sicher, ob es alle möglichen erreichbaren Speichersemantiken abdeckt, aber es könnte sein.Die subtilere Semantik ist besonders relevant im Zusammenhang mit sperrenfreien Datenstrukturen , die in bestimmten Fällen Leistungsvorteile bieten können. Um diese zu implementieren, müssen Sie wahrscheinlich etwas über die verschiedenen Arten von Speicherbarrieren lernen: https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/
Boost bietet beispielsweise einige sperrenfreie Container-Implementierungen unter: https://www.boost.org/doc/libs/1_63_0/doc/html/lockfree.html
Solche Benutzerlandanweisungen scheinen auch verwendet zu werden, um den Linux-
futex
Systemaufruf zu implementieren , der eines der Hauptsynchronisationsprimitive in Linux ist.man futex
4.15 lautet:Der Syscall-Name selbst bedeutet "Fast Userspace XXX".
Hier ist ein minimal nutzloses C ++ x86_64 / aarch64-Beispiel mit Inline-Assembly, das die grundlegende Verwendung solcher Anweisungen hauptsächlich zum Spaß veranschaulicht:
main.cpp
GitHub stromaufwärts .
Mögliche Ausgabe:
Daraus sehen wir, dass der x86 LOCK-Präfix / aarch64-
LDADD
Befehl die Addition atomar gemacht hat: Ohne sie haben wir bei vielen der Adds Race-Bedingungen, und die Gesamtzahl am Ende ist geringer als die synchronisierten 20000.Siehe auch:
Getestet in Ubuntu 19.04 amd64 und mit QEMU aarch64 Benutzermodus.
quelle
#include
(nimmt es als Kommentar), NASM, FASM, YASM kennen die AT & T-Syntax nicht, also können es nicht sie sein ... also was ist das?gcc
,#include
kommt vom C-Präprozessor. Verwenden Sie dieMakefile
im Abschnitt Erste Schritte erläuterten Informationen : github.com/cirosantilli/x86-bare-metal-examples/blob/… Wenn dies nicht funktioniert, öffnen Sie ein GitHub-Problem.Nach meinem Verständnis ist jeder "Kern" ein vollständiger Prozessor mit einem eigenen Registersatz. Grundsätzlich startet das BIOS Sie mit einem laufenden Kern, und dann kann das Betriebssystem andere Kerne "starten", indem es sie initialisiert und auf den auszuführenden Code usw. zeigt.
Die Synchronisierung erfolgt durch das Betriebssystem. Im Allgemeinen führt jeder Prozessor einen anderen Prozess für das Betriebssystem aus. Daher entscheidet die Multithreading-Funktionalität des Betriebssystems, welcher Prozess welchen Speicher berührt und was im Falle einer Speicherkollision zu tun ist.
quelle
Die inoffiziellen SMP-FAQ
Es war einmal, als Sie zum Schreiben eines x86-Assemblers Anweisungen hatten, die besagten: "Laden Sie das EDX-Register mit dem Wert 5", "Erhöhen Sie das EDX-Register" usw. Bei modernen CPUs mit 4 Kernen (oder sogar mehr) sieht es auf der Ebene des Maschinencodes nur so aus, als gäbe es 4 separate CPUs (dh gibt es nur 4 verschiedene "EDX" -Register)?
Genau. Es gibt 4 Registersätze, einschließlich 4 separater Befehlszeiger.
Wenn ja, wenn Sie "Inkrementieren des EDX-Registers" sagen, was bestimmt, welches EDX-Register der CPU inkrementiert wird?
Die CPU, die diese Anweisung ausgeführt hat, natürlich. Stellen Sie sich 4 völlig unterschiedliche Mikroprozessoren vor, die sich einfach den gleichen Speicher teilen.
Gibt es jetzt im x86-Assembler ein "CPU-Kontext" - oder "Thread" -Konzept?
Nein. Der Assembler übersetzt nur Anweisungen wie immer. Keine Änderungen dort.
Wie funktioniert die Kommunikation / Synchronisation zwischen den Kernen?
Da sie denselben Speicher gemeinsam nutzen, ist dies hauptsächlich eine Frage der Programmlogik. Obwohl es jetzt einen Interprozessor-Interrupt- Mechanismus gibt, ist dieser nicht erforderlich und war ursprünglich in den ersten Dual-CPU-x86-Systemen nicht vorhanden.
Wenn Sie ein Betriebssystem geschrieben haben, welcher Mechanismus wird über Hardware verfügbar gemacht, damit Sie die Ausführung auf verschiedenen Kernen planen können?
Der Scheduler ändert sich tatsächlich nicht, außer dass er kritische Abschnitte und die Arten der verwendeten Sperren etwas sorgfältiger behandelt. Vor SMP würde der Kernelcode schließlich den Scheduler aufrufen, der die Ausführungswarteschlange überprüft und einen Prozess auswählt, der als nächster Thread ausgeführt werden soll. (Prozesse für den Kernel ähneln Threads.) Der SMP-Kernel führt exakt denselben Code aus, einen Thread nach dem anderen. Jetzt muss die Sperrung kritischer Abschnitte SMP-sicher sein, um sicherzustellen, dass zwei Kerne nicht versehentlich ausgewählt werden können die gleiche PID.
Handelt es sich um besonders privilegierte Anweisungen?
Nein. Die Kerne laufen nur alle im selben Speicher mit denselben alten Anweisungen.
Wenn Sie eine optimierende Compiler- / Bytecode-VM für eine Multicore-CPU schreiben würden, was müssten Sie beispielsweise speziell über x86 wissen, damit Code generiert wird, der auf allen Kernen effizient ausgeführt wird?
Sie führen den gleichen Code wie zuvor aus. Es ist der Unix- oder Windows-Kernel, der geändert werden musste.
Sie können meine Frage wie folgt zusammenfassen: "Welche Änderungen wurden am x86-Maschinencode vorgenommen, um die Multi-Core-Funktionalität zu unterstützen?"
Nichts war notwendig. Die ersten SMP-Systeme verwendeten genau den gleichen Befehlssatz wie Uniprozessoren. Jetzt gab es eine große Entwicklung der x86-Architektur und unzählige neue Anweisungen, um die Dinge schneller zu machen, aber für SMP waren keine erforderlich .
Weitere Informationen finden Sie in der Intel Multiprocessor-Spezifikation .
Update: Alle nachfolgenden Fragen können beantwortet werden, indem einfach vollständig akzeptiert wird, dass eine n- Wege-Multicore-CPU fast 1 genau dasselbe ist wie n separate Prozessoren, die sich nur denselben Speicher teilen. 2 Es wurde eine wichtige Frage nicht gestellt: Wie wird ein Programm geschrieben, das auf mehr als einem Kern ausgeführt wird, um mehr Leistung zu erzielen? Und die Antwort lautet: Es wird mit einer Thread-Bibliothek wie Pthreads geschrieben. Einige Thread-Bibliotheken verwenden "grüne Threads", die für das Betriebssystem nicht sichtbar sind, und diese erhalten keine separaten Kerne. Solange die Thread-Bibliothek Kernel-Thread-Funktionen verwendet, ist Ihr Thread-Programm automatisch mehrkernig.
1. Aus Gründen der Abwärtskompatibilität wird beim Zurücksetzen nur der erste Kern gestartet, und es müssen einige Dinge vom Typ Treiber ausgeführt werden, um die verbleibenden zu starten.
2. Sie teilen sich natürlich auch alle Peripheriegeräte.
quelle
Als jemand, der optimierende Compiler- / Bytecode-VMs schreibt, kann ich Ihnen hier möglicherweise helfen.
Sie müssen nichts spezielles über x86 wissen, damit Code generiert wird, der auf allen Kernen effizient ausgeführt wird.
Möglicherweise müssen Sie jedoch über cmpxchg und Freunde Bescheid wissen, um Code zu schreiben, der auf allen Kernen korrekt ausgeführt wird . Multicore-Programmierung erfordert die Verwendung von Synchronisation und Kommunikation zwischen Ausführungsthreads.
Möglicherweise müssen Sie etwas über x86 wissen, damit Code generiert wird, der auf x86 im Allgemeinen effizient ausgeführt wird.
Es gibt noch andere Dinge, die Sie lernen sollten:
Sie sollten sich mit den Funktionen des Betriebssystems (Linux oder Windows oder OSX) vertraut machen, mit denen Sie mehrere Threads ausführen können. Sie sollten sich mit Parallelisierungs-APIs wie OpenMP und Threading Building Blocks oder OSX 10.6 "Snow Leopard", dem kommenden "Grand Central", vertraut machen.
Sie sollten überlegen, ob Ihr Compiler automatisch parallelisiert werden soll oder ob der Autor der von Ihrem Compiler kompilierten Anwendungen seinem Programm spezielle Syntax- oder API-Aufrufe hinzufügen muss, um die mehreren Kerne nutzen zu können.
quelle
Jeder Core wird aus einem anderen Speicherbereich ausgeführt. Ihr Betriebssystem zeigt einen Kern auf Ihr Programm und der Kern führt Ihr Programm aus. Ihr Programm wird nicht wissen, dass es mehr als einen Kern gibt oder auf welchem Kern es ausgeführt wird.
Es gibt auch keine zusätzlichen Anweisungen, die nur dem Betriebssystem zur Verfügung stehen. Diese Kerne sind identisch mit Single-Core-Chips. Auf jedem Core wird ein Teil des Betriebssystems ausgeführt, der die Kommunikation mit gemeinsamen Speicherbereichen übernimmt, die für den Informationsaustausch verwendet werden, um den nächsten auszuführenden Speicherbereich zu finden.
Dies ist eine Vereinfachung, gibt Ihnen jedoch eine grundlegende Vorstellung davon, wie es gemacht wird. Mehr über Multicores und Multiprozessoren auf Embedded.com bietet viele Informationen zu diesem Thema ... Dieses Thema wird sehr schnell kompliziert!
quelle
Der Assemblycode wird in Maschinencode übersetzt, der auf einem Kern ausgeführt wird. Wenn Sie möchten, dass es Multithread-fähig ist, müssen Sie Betriebssystemprimitive verwenden, um diesen Code auf verschiedenen Prozessoren mehrmals oder verschiedene Codeteile auf verschiedenen Kernen zu starten. Jeder Kern führt einen separaten Thread aus. Jeder Thread sieht nur einen Kern, auf dem er gerade ausgeführt wird.
quelle
Es wird überhaupt nicht in Maschinenanweisungen gemacht; Die Kerne geben vor, unterschiedliche CPUs zu sein, und haben keine besonderen Funktionen, um miteinander zu kommunizieren. Sie kommunizieren auf zwei Arten:
Sie teilen sich den physischen Adressraum. Die Hardware übernimmt die Cache-Kohärenz, sodass eine CPU in eine Speicheradresse schreibt, die eine andere liest.
Sie teilen sich einen APIC (Programmable Interrupt Controller). Dies ist ein Speicher, der dem physischen Adressraum zugeordnet ist und von einem Prozessor verwendet werden kann, um die anderen zu steuern, sie ein- oder auszuschalten, Interrupts zu senden usw.
http://www.cheesecake.org/sac/smp.html ist eine gute Referenz mit einer dummen URL.
quelle
Der Hauptunterschied zwischen einer Single- und einer Multithread-Anwendung besteht darin, dass die erstere einen Stapel und die letztere einen für jeden Thread hat. Code wird etwas anders generiert, da der Compiler davon ausgeht, dass die Daten- und Stapelsegmentregister (ds und ss) nicht gleich sind. Dies bedeutet, dass die Indirektion durch die ebp- und esp-Register, die standardmäßig das ss-Register verwenden, nicht auch standardmäßig ds ist (weil ds! = SS). Umgekehrt wird die Indirektion durch die anderen Register, die standardmäßig ds verwenden, nicht standardmäßig ss.
Die Threads teilen alles andere, einschließlich Daten- und Codebereiche. Sie teilen auch lib-Routinen, stellen Sie also sicher, dass sie threadsicher sind. Eine Prozedur, die einen Bereich im RAM sortiert, kann mit mehreren Threads versehen werden, um die Arbeit zu beschleunigen. Die Threads greifen dann auf Daten in demselben physischen Speicherbereich zu, vergleichen sie und ordnen sie an und führen denselben Code aus, verwenden jedoch unterschiedliche lokale Variablen, um ihren jeweiligen Teil der Sortierung zu steuern. Dies liegt natürlich daran, dass die Threads unterschiedliche Stapel haben, in denen die lokalen Variablen enthalten sind. Diese Art der Programmierung erfordert eine sorgfältige Abstimmung des Codes, damit die Kollisionen zwischen den Kerndaten (in Caches und RAM) reduziert werden, was wiederum zu einem Code führt, der mit zwei oder mehr Threads schneller ist als mit nur einem. Natürlich ist ein nicht abgestimmter Code mit einem Prozessor oft schneller als mit zwei oder mehr. Das Debuggen ist schwieriger, da der Standard-Haltepunkt "int 3" nicht anwendbar ist, da Sie einen bestimmten Thread und nicht alle unterbrechen möchten. Debug-Register-Haltepunkte lösen dieses Problem auch nicht, es sei denn, Sie können sie auf dem bestimmten Prozessor festlegen, der den bestimmten Thread ausführt, den Sie unterbrechen möchten.
Bei anderen Multithread-Codes können unterschiedliche Threads in verschiedenen Teilen des Programms ausgeführt werden. Diese Art der Programmierung erfordert nicht die gleiche Art der Abstimmung und ist daher viel einfacher zu erlernen.
quelle
Was zu jeder Multiprozessor-fähigen Architektur im Vergleich zu den vorangegangenen Einzelprozessor-Varianten hinzugefügt wurde, sind Anweisungen zum Synchronisieren zwischen Kernen. Außerdem haben Sie Anweisungen zum Umgang mit Cache-Kohärenz, Leeren von Puffern und ähnlichen Operationen auf niedriger Ebene, mit denen sich ein Betriebssystem befassen muss. Bei gleichzeitigen Multithread-Architekturen wie IBM POWER6, IBM Cell, Sun Niagara und Intel "Hyperthreading" werden häufig neue Anweisungen zum Priorisieren zwischen Threads angezeigt (z. B. Festlegen von Prioritäten und explizites Ausgeben des Prozessors, wenn nichts zu tun ist). .
Die grundlegende Single-Thread-Semantik ist jedoch dieselbe. Sie fügen lediglich zusätzliche Funktionen für die Synchronisierung und Kommunikation mit anderen Kernen hinzu.
quelle