macOS GCD - Grand Central Dispatch
基本情報
Grand Central Dispatch (GCD)、またの名をlibdispatch (libdispatch.dyld
) は macOS と iOS の両方で利用可能です。これは、Appleが開発した技術であり、マルチコアハードウェア上での並行(マルチスレッド)実行を最適化するためのアプリケーションサポートを提供します。
GCD は、アプリケーションがブロックオブジェクトの形でFIFOキューにタスクを送信できるように提供し、システムによって完全に管理されるスレッドプールでタスクを実行します。GCD は、ディスパッチキューに送信されたブロックを実行するためにスレッドを自動的に作成し、それらのタスクを利用可能なコアで実行するようスケジュールします。
要するに、並列でコードを実行するために、プロセスはGCDにコードブロックを送信し、その実行を管理します。したがって、プロセスは新しいスレッドを作成しません。GCDは独自のスレッドプールで指定されたコードを実行します(必要に応じて増減する可能性があります)。
これは、並列実行を成功裏に管理するのに非常に役立ち、プロセスが作成するスレッドの数を大幅に減らし、並列実行を最適化します。これは、大規模な並列性(総当たり?)を必要とするタスクや、メインスレッドをブロックすべきでないタスクに最適です。たとえば、iOSのメインスレッドはUIの相互作用を処理するため、アプリがフリーズする可能性のある他の機能(検索、Webへのアクセス、ファイルの読み取りなど)はこの方法で処理されます。
ブロック
ブロックは自己完結型のコードセクション(引数を取り値を返す関数のようなもの)であり、バインド変数を指定することもできます。
ただし、コンパイラレベルではブロックは存在せず、os_object
です。これらのオブジェクトのそれぞれは2つの構造体で構成されています:
ブロックリテラル:
ブロックのクラスを指す**
isa
**フィールドで始まります:NSConcreteGlobalBlock
(__DATA.__const
からのブロック)NSConcreteMallocBlock
(ヒープ内のブロック)NSConcreateStackBlock
(スタック内のブロック)flags
(ブロック記述子に存在するフィールドを示す)といくつかの予約バイト呼び出すための関数ポインタ
ブロック記述子へのポインタ
インポートされた変数(ある場合)
ブロック記述子: データに応じてサイズが異なります(前述のフラグで示されている)
いくつかの予約バイト
サイズ
通常、パラメータに必要なスペースの量を知るためにObjective-Cスタイルのシグネチャへのポインタが含まれます(フラグ
BLOCK_HAS_SIGNATURE
)変数が参照されている場合、このブロックには値をコピーするコピー補助プログラム(開始時に値をコピーする)と解放補助プログラム(解放する)へのポインタも含まれます。
キュー
ディスパッチキューは、ブロックの実行のためのFIFO順序を提供する名前付きオブジェクトです。
ブロックは実行されるためにキューに設定され、これらはDISPATCH_QUEUE_SERIAL
とDISPATCH_QUEUE_CONCURRENT
の2つのモードをサポートします。もちろん、シリアルは競合状態が発生しないため、前のブロックが終了するまで次のブロックは実行されません。しかし、もう一方のタイプのキューはそれを持つかもしれません。
デフォルトのキュー:
.main-thread
:dispatch_get_main_queue()
から.libdispatch-manager
: GCDのキューマネージャ.root.libdispatch-manager
: GCDのキューマネージャ.root.maintenance-qos
: 最低優先度のタスク.root.maintenance-qos.overcommit
.root.background-qos
:DISPATCH_QUEUE_PRIORITY_BACKGROUND
として利用可能.root.background-qos.overcommit
.root.utility-qos
:DISPATCH_QUEUE_PRIORITY_NON_INTERACTIVE
として利用可能.root.utility-qos.overcommit
.root.default-qos
:DISPATCH_QUEUE_PRIORITY_DEFAULT
として利用可能.root.background-qos.overcommit
.root.user-initiated-qos
:DISPATCH_QUEUE_PRIORITY_HIGH
として利用可能.root.background-qos.overcommit
.root.user-interactive-qos
: 最高の優先度.root.background-qos.overcommit
システムがどのスレッドが各キューを各時点で処理するかを決定します(複数のスレッドが同じキューで作業するか、同じスレッドが異なるキューで作業する可能性があります)
属性
**dispatch_queue_create
**でキューを作成する際、3番目の引数はdispatch_queue_attr_t
であり、通常はDISPATCH_QUEUE_SERIAL
(実際にはNULL)またはDISPATCH_QUEUE_CONCURRENT
(キューのいくつかのパラメータを制御できるdispatch_queue_attr_t
構造体へのポインタ)です。
ディスパッチオブジェクト
libdispatchが使用するオブジェクトにはいくつかあり、キューとブロックはそのうちの2つです。これらのオブジェクトはdispatch_object_create
で作成できます:
block
data
: データブロックgroup
: ブロックのグループio
: 非同期I/Oリクエストmach
: Machポートmach_msg
: Machメッセージpthread_root_queue
: pthreadスレッドプールとワークキューを持つキューqueue
semaphore
source
: イベントソース
Objective-C
Objective-Cでは、並列で実行するためにブロックを送信するための異なる関数があります:
dispatch_async: ブロックを非同期でディスパッチキューに送信し、すぐに返します。
dispatch_sync: ブロックオブジェクトを実行するために送信し、そのブロックの実行が終了した後に返ります。
dispatch_once: アプリケーションのライフタイム中にブロックオブジェクトを1回だけ実行します。
dispatch_async_and_wait: ワークアイテムを実行し、その実行が終了するまでのみ返ります。
dispatch_sync
とは異なり、この関数はブロックを実行する際にキューのすべての属性を尊重します。
これらの関数は次のパラメータを期待します: dispatch_queue_t
queue,
dispatch_block_t
block
これがブロックの構造体です:
そして、dispatch_async
を使用して並列処理を行う例が以下になります:
Swift
libswiftDispatch
は、元々Cで書かれたGrand Central Dispatch(GCD)フレームワークへのSwiftバインディングを提供するライブラリです。
**libswiftDispatch
**ライブラリは、CのGCD APIをよりSwift向けにラップし、Swift開発者がGCDとより簡単かつ直感的に作業できるようにします。
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"))
コード例:
Frida
次のFridaスクリプトを使用して、複数のdispatch
関数にフックし、キュー名、バックトレース、およびブロックを抽出できます:https://github.com/seemoo-lab/frida-scripts/blob/main/scripts/libdispatch.js
Ghidra
現在、GhidraはObjectiveCの**dispatch_block_t
構造体もswift_dispatch_block
**構造体も理解していません。
したがって、それらを理解させたい場合は、単に宣言することができます:
次に、コード内でそれらが使用されている場所を見つけます:
"block"に関するすべての参照をメモして、構造体が使用されている方法を理解できます。
変数を右クリック -> 変数の型を変更し、この場合は**swift_dispatch_block
**を選択します:
Ghidraは自動的にすべてを書き換えます:
参考文献
Last updated