2011年03月31日
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ5:カーネルドライバ)
今回はカーネル側のbinderのドライバ(/dev/binder)の話。
プロセス間通信としてはJavaのRMIやGTKやBluetoothのスタックで使用されているD-Busがありますが、これらは通信の下位層としてはsocketを使用しています。このため特別なカーネルのドライバを必要としません。Androidのプロセス間通信は/dev/binderという独自のドライバを使います。それはなぜなのでしょうか?
/dev/binder を使っているところ
/dev/binderをopenしているところは以下の2箇所だけ。
- frameworks/base/libs/binder/ProcessState.cpp
- frameworks/cmds/servicemanager/binder.c
/dev/binder の使われ方の特徴
上記の2つのソースを見ると気が付くことがあります。
openした後にread, writeは使わない。全てioctlを使う。
(servicemanagerではmmapしているが、マップされたメモリを使用している形跡が無い。)
実際のプロセス間通信で使われるのは
ioctl(fd, BINDER_WRITE_READ, &bwr);
write用とread用の2つのバッファを含んだ構造体を渡して、コマンドの送信とステータスの受信を一度のシステムコール呼び出しで済ませています。
fdがスレッド間で共有されている
ProcessState.cppは複数のスレッドから使用されるのに、openは各プロセスで一回しか呼ばれずに得られたfile descriptor を同じプロセス内の複数のスレッドで共有しています。しかもそれを排他制御しているようには見えません。
通常のsocketならば、データの通信にはread, writeのシステムコールを使用します。そしてその内部状態(どこまで読み書きしたかなど)はfdに紐付けされて管理されます。そのため複数のスレッドで別々に通信を行うときにはそれぞれのスレッドでsocketを生成する必要があります。
Linuxカーネルのbinderのソースを見る
- drivers/staging/android/binder.c
メインラインのソースには含まれていないのでandroid用のパッチをあてる必要があります。
ソースの中を見ていくと
ioctl(fd, BINDER_WRITE_READ, &bwr);
の時の通信の内部状態はスレッドID(pid)に紐付けされて管理されています。これを呼び出すユーザーランド側の構造体bwrも、よく見てみるとTLS(Thread local Storage)になっています。このため、カーネルもユーザランドもその通信に関連する状態は全てThreadローカルになっているためにスレッド間の排他制御が不要になっているということなのでしょう。
おおまかなプロセス間通信の理解
カーネルのbinder.cはだいぶ入り組んでいるのでわかりにくいのですが、私はこのように理解しました。
プロセスAからプロセスBへのデータの送信は
- プロセスBのBinderThreadがioctl(fd, BINDER_WRITE_READ, &bwr)を呼び出してブロックされている状態とする。
- プロセスAがプロセスBに通信しようとしてioctl(fd, BINDER_WRITE_READ, ... )を呼び出す。
- カーネルはプロセスAの送りたいデータをカーネルの領域に一度預かる。(copy_from_userによるメモリコピー)
- カーネルはプロセスBのBinderThreadのブロックを解除。
- プロセスBのBinderThreadがスケジュールされたときに、カーネルの領域に預かったデータを渡す。(copy_to_userによるメモリコピー)
- プロセスBがプロセスAにステータスのデータを返すときには同じことを逆方向で。
socketのread/writeは大きなデータになる可能性もあるストリームを扱うようにできているのに対して、binderの主な用途はリモートメソッド呼び出しなので、送受信するデータのサイズは大きくないのでバッファに格納してまるごとコピーすることができます。
一方、socketは同一のマシン内のプロセス間の通信だけではなくて、ネットワークを介した外部のマシンのプロセスとの通信も透過的に行うことができます。binderは今のところ同一マシン内に特化しているようです。
まだ理解できていないところ
とりあえずソースコードを読むことでAndroidのプロセス間のメソッド呼び出しのしくみがわかってきたのですが、まだ以下のことが理解できていません。
- StrongPointer, WeakPointer のリファレンスの管理
C++で sp<T>, wp<T> で書くところです。おそらくBinderのライフサイクルが関係していると思うのですが。
- Binderはいつ終了するのか
servicemanagerにaddServiceはあるが、removeServiceは無い。終了時の処理を登録するようになっているが、これが呼ばれる契機は?
関連するページ
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ2:AIDL)
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ3:Binder Thread)