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**(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は自動的にすべてを書き換えます:
References
Last updated