2011年03月24日
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ3:Binder Thead)
今回はBinder Threadについて。
Eclipseでアプリケーションのデバッグをするとアプリケーションのプロセスの中に、"Binder Thread #1", "Binder Thread #2" などの名前のスレッドがあるのがわかります。これらはいったい何をしているのでしょうか。
Binder Threadのソースコード
まずはこのスレッドの名前を頼りにソースを検索してみます。
$ cd frameworks $ grep -r '"Binder Thread #' * 2>/dev/null base/libs/binder/ProcessState.cpp: sprintf(buf, "Binder Thread #%d", s);
あっさり見つかりました。
frameworks/base/libs/binder/ProcessState.cpp
frameworks/base/libs/binder/IPCThreadState.cpp
このあたりのソースを見るとこのスレッドはPoolThreadのクラスで定義されていて、そのメインループは IPCThreadState::joinThreadPool であることがわかります。IPCThreadState::talkWithDriverの中から/dev/binderにioctlのシステムコールでカーネルとやりとりしています。
Binder Threadはいつ生成されるのか
PoolThread::run が呼ばれる経路をさかのぼってみました。
frameworks/base/cmds/app_main/app_main.cpp AppRuntime::onStarted() spproc = ProcessState::self(); ... proc->startThreadPool(); --> frameworks/base/libs/binder/ProcessState.cpp ProcessState::startThreadPool() ... mThreadPoolStarted = true; spawnPooledThread(true); --> void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { int32_t s = android_atomic_add(1, &mThreadPoolSeq); char buf[32]; sprintf(buf, "Binder Thread #%d", s); LOGV("Spawning new pooled thread, name=%s\n", buf); sp t = new PoolThread(isMain); t->run(buf); } }
AppRuntime::onStarted() はZygoteからforkされた後すぐに呼び出されます。
このBinder Threadが他のプロセスからのリモートメソッド呼び出しを受け付けて実行します。
最初はBinder Threadはひとつだけですが、処理要求があった時にThreadの数を増やして要求待ちのスレッドの数がゼロにならないようにしています。要求待ちがタイムアウトした時にはThreadの数を減らしますが、一番最初に起動したmain threadだけは残るようになっています。
Proxyクラスからの/dev/binderのアクセス
/dev/binderへのアクセスはIPCThreadState::talkWithDriver で一本化されています。
リモートメソッド呼び出しの要求を出す側(Proxyクラス)からは以下の経路で使われています。
frameworks/base/libs/binder/BpBinder.cpp BpBinder::transact( .. ) --> frameworks/base/libs/binder/IPCThreadState.cpp IPCThreadState::transact( .. ) --> frameworks/base/libs/binder/IPCThreadState.cpp IPCThreadState::waitForResponse( .. ) --> frameworks/base/libs/binder/IPCThreadState.cpp IPCThreadState::talkWithDriver(bool doRecieve)
Binder ThreadでのonTransaction( .. )までの経路
Binder Threadでリモートメソッド呼び出しの要求を受け取ってJavaのonTransactionメソッドを呼び出します。前々回では説明を省略しました。この経路はマジカルで私も理解できたわけではありませんが、以下のような経路をたどっていると思います。
frameworks/base/libs/binder/IPCThreadState.cpp IPCThreadState::joinThreadPool --> frameworks/base/libs/binder/IPCThreadState.cpp IPCThreadState::executeCommand(int32_t cmd) ... switch(cmd) { ... case BR_TRANSACTION: ... spb ((BBinder*)tr.cookie); const status_t error = b->transact( .. ); --> rameworks/base/libs/binder/Binder.cpp BBinder::transact( .. ) ... err = onTransact( .. ); --> frameworks/base/core/jni/android_util_Binder.cpp class JavaBBinder : public BBinder { ... virtual status_t onTransact( .. ) ... JNIEnv* env = javavm_to_jnienv(mVM); ... jboolean res = env->CallBooleanMethod(mObject, .. ) JNIでJavaのBinder#execTransactメソッドを呼び出す --> frameworks/base/core/java/android/os/Binder.java // Entry point from android_util_Binder.cpp's onTransact private boolean execTransact( .. ) { ... try { res = onTransact(code, data, reply, flags); } catch (RemoteException e) { ... } --> BinderのサブクラスのonTransact( .. )の呼び出し。 例えば ActivityManagerNative#onTransact( .. )
余談になりますが、JNIからのJavaのメソッドの呼び出しの時には、privateやprotectedなどの修飾子は意味を持ちません。
上の例でも android.os.Binder#execTransact( .. )はprivateのメソッドですが、JNI経由で呼び出されています。
関連するページ
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ2:AIDL)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ3:Binder Thread)