macOS GCD - Grand Central Dispatch
Grundlegende Informationen
Grand Central Dispatch (GCD), auch bekannt als libdispatch (libdispatch.dyld
), ist sowohl in macOS als auch in iOS verfügbar. Es handelt sich um eine von Apple entwickelte Technologie zur Optimierung der Anwendungsunterstützung für die gleichzeitige (mehrfädige) Ausführung auf Mehrkern-Hardware.
GCD stellt FIFO-Warteschlangen bereit, an die Ihre Anwendung Aufgaben in Form von Blockobjekten übergeben kann.
Die Blöcke, die an Dispatch-Warteschlangen übergeben werden, werden auf einem vom System vollständig verwalteten Thread-Pool ausgeführt. GCD erstellt automatisch Threads zur Ausführung der Aufgaben in den Dispatch-Warteschlangen und plant diese Aufgaben so, dass sie auf den verfügbaren Kernen ausgeführt werden.
Zusammenfassend können Prozesse zur Ausführung von Code parallel Codeblöcke an GCD senden, die sich um deren Ausführung kümmern. Daher erstellen Prozesse keine neuen Threads; GCD führt den übergebenen Code mit seinem eigenen Thread-Pool aus (der bei Bedarf erhöht oder verringert werden kann).
Dies ist sehr hilfreich, um die parallele Ausführung erfolgreich zu verwalten, da die Anzahl der Threads, die Prozesse erstellen, erheblich reduziert wird und die parallele Ausführung optimiert wird. Dies ist ideal für Aufgaben, die eine große Parallelität erfordern (Brute-Forcing?) oder für Aufgaben, die den Hauptthread nicht blockieren sollten: Beispielsweise behandelt der Hauptthread auf iOS UI-Interaktionen, sodass alle anderen Funktionen, die die App zum Stillstand bringen könnten (Suchen, auf eine Website zugreifen, eine Datei lesen...), auf diese Weise verwaltet werden.
Blöcke
Ein Block ist ein in sich geschlossener Abschnitt des Codes (wie eine Funktion mit Argumenten, die einen Wert zurückgeben) und kann auch gebundene Variablen angeben.
Auf Compiler-Ebene existieren Blöcke jedoch nicht, sie sind os_object
s. Jedes dieser Objekte besteht aus zwei Strukturen:
Blockliteral:
Es beginnt mit dem Feld
isa
, das auf die Klasse des Blocks zeigt:NSConcreteGlobalBlock
(Blöcke aus__DATA.__const
)NSConcreteMallocBlock
(Blöcke im Heap)NSConcreateStackBlock
(Blöcke im Stack)Es hat
flags
(die Felder im Blockbeschreibung anzeigen) und einige reservierte BytesDer Funktionszeiger zum Aufruf
Ein Zeiger auf die Blockbeschreibung
Importierte Blockvariablen (falls vorhanden)
Blockbeschreibung: Ihre Größe hängt von den vorhandenen Daten ab (wie in den vorherigen Flags angegeben)
Es hat einige reservierte Bytes
Die Größe davon
Es wird normalerweise einen Zeiger auf eine Objective-C-Style-Signatur haben, um zu wissen, wie viel Platz für die Parameter benötigt wird (Flag
BLOCK_HAS_SIGNATURE
)Wenn Variablen referenziert werden, wird dieser Block auch Zeiger auf einen Kopierhelfer (der den Wert am Anfang kopiert) und einen Entsorgungshelfer (der ihn freigibt) haben.
Warteschlangen
Eine Dispatch-Warteschlange ist ein benanntes Objekt, das die FIFO-Reihenfolge von Blöcken für die Ausführung bereitstellt.
Blöcke werden in Warteschlangen eingestellt, um ausgeführt zu werden, und diese unterstützen 2 Modi: DISPATCH_QUEUE_SERIAL
und DISPATCH_QUEUE_CONCURRENT
. Natürlich wird die serielle Warteschlange keine Probleme mit Rennbedingungen haben, da ein Block erst ausgeführt wird, wenn der vorherige beendet ist. Aber der andere Typ der Warteschlange könnte sie haben.
Standardwarteschlangen:
.main-thread
: Vondispatch_get_main_queue()
.libdispatch-manager
: GCD-Warteschlangen-Manager.root.libdispatch-manager
: GCD-Warteschlangen-Manager.root.maintenance-qos
: Aufgaben mit niedrigster Priorität.root.maintenance-qos.overcommit
.root.background-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_BACKGROUND
.root.background-qos.overcommit
.root.utility-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
.root.utility-qos.overcommit
.root.default-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_DEFAULT
.root.background-qos.overcommit
.root.user-initiated-qos
: Verfügbar alsDISPATCH_QUEUE_PRIORITY_HIGH
.root.background-qos.overcommit
.root.user-interactive-qos
: Höchste Priorität.root.background-qos.overcommit
Beachten Sie, dass das System entscheidet, welche Threads zu welchen Warteschlangen zu einem bestimmten Zeitpunkt gehören (mehrere Threads können in derselben Warteschlange arbeiten oder derselbe Thread kann zu einem bestimmten Zeitpunkt in verschiedenen Warteschlangen arbeiten).
Attribute
Beim Erstellen einer Warteschlange mit dispatch_queue_create
ist das dritte Argument ein dispatch_queue_attr_t
, das normalerweise entweder DISPATCH_QUEUE_SERIAL
(das tatsächlich NULL ist) oder DISPATCH_QUEUE_CONCURRENT
ist, das ein Zeiger auf eine dispatch_queue_attr_t
-Struktur ist, die es ermöglicht, einige Parameter der Warteschlange zu steuern.
Dispatch-Objekte
Es gibt mehrere Objekte, die libdispatch verwendet, und Warteschlangen und Blöcke sind nur 2 davon. Es ist möglich, diese Objekte mit dispatch_object_create
zu erstellen:
block
data
: Datenblöckegroup
: Gruppe von Blöckenio
: Asynchrone I/O-Anforderungenmach
: Mach-Portsmach_msg
: Mach-Nachrichtenpthread_root_queue
: Eine Warteschlange mit einem pthread-Thread-Pool und keine Arbeitswarteschlangenqueue
semaphore
source
: Ereignisquelle
Objective-C
In Objective-C gibt es verschiedene Funktionen, um einen Block zur parallelen Ausführung zu senden:
dispatch_async: Sendet einen Block zur asynchronen Ausführung an eine Dispatch-Warteschlange und kehrt sofort zurück.
dispatch_sync: Sendet ein Blockobjekt zur Ausführung und kehrt zurück, nachdem dieser Block ausgeführt wurde.
dispatch_once: Führt ein Blockobjekt nur einmal während der Lebensdauer einer Anwendung aus.
dispatch_async_and_wait: Sendet ein Arbeitsobjekt zur Ausführung und kehrt erst zurück, nachdem es ausgeführt wurde. Im Gegensatz zu
dispatch_sync
respektiert diese Funktion alle Attribute der Warteschlange, wenn sie den Block ausführt.
Diese Funktionen erwarten diese Parameter: dispatch_queue_t
queue,
dispatch_block_t
block
Dies ist die Struktur eines Blocks:
Und dies ist ein Beispiel zur Verwendung von Parallelismus mit dispatch_async
:
Swift
libswiftDispatch
ist eine Bibliothek, die Swift-Bindungen zum Grand Central Dispatch (GCD)-Framework bereitstellt, das ursprünglich in C geschrieben wurde.
Die Bibliothek libswiftDispatch
kapselt die C GCD-APIs in eine benutzerfreundlichere Schnittstelle für Swift, was es einfacher und intuitiver für Swift-Entwickler macht, mit GCD zu arbeiten.
DispatchQueue.global().sync{ ... }
DispatchQueue.global().async{ ... }
let onceToken = DispatchOnce(); onceToken.perform { ... }
async await
var (data, response) = await URLSession.shared.data(from: URL(string: "https://api.example.com/getData"))
Codebeispiel:
Frida
Das folgende Frida-Skript kann verwendet werden, um sich in mehrere dispatch
-Funktionen einzuhaken und den Warteschlangennamen, den Backtrace und den Block zu extrahieren: https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js
Ghidra
Aktuell versteht Ghidra weder die ObjectiveC-Struktur dispatch_block_t
noch die swift_dispatch_block
.
Wenn Sie möchten, dass es sie versteht, könnten Sie sie einfach deklarieren:
Dann finden Sie eine Stelle im Code, wo sie verwendet werden:
Beachten Sie alle Verweise auf "block", um herauszufinden, wie Sie feststellen können, dass die Struktur verwendet wird.
Klicken Sie mit der rechten Maustaste auf die Variable -> Variablentyp ändern und wählen Sie in diesem Fall swift_dispatch_block
:
Ghidra wird automatisch alles neu schreiben:
Referenzen
Last updated