Upload
lb
View
239
Download
1
Embed Size (px)
Citation preview
Binderのはじめの一歩とAndroidのプロセス間通信(IPC)
Android IPCのとりあえず1回目
2010/10/31@l_b__
横浜Androidプラットフォーム部第3回勉強会
目次
●Androidのプロセス間通信●Binderって?●何故Binder?●Android独自のIPC●Binderの歴史●Binderの仕組み●実際に使ってみる●Ashmemって?●Ashmemの関数●実際にAshmemを使ってみる●次回予告
Androidのプロセス間通信
● AndroidのNative側でプロセス間通信(IPC,InterProcess Communication)を行うには以下の方法がある。1. Binder2. Ashmem(Anonymous Shared Memory)3. UNIXドメインソケット4. TCP/UDPソケット5.名前付きパイプ
※ただ、Androidソース内に名前付きパイプを使っている箇所は無い。
Binderって?
●Android独自のプロセス間通信の一つ。●POSIXメッセージの置き換え。●プロセス間で小さいデータ(数百〜数千byte程度)を高速にやり取りするために使用。
●何故POSIXのIPCがあるのにBinderという機構が導入されたのか。
何故Binder
●AndroidはSystemV(UNIXの1種)のIPCをサポートしていない!
●NDKの/docs/system/libc/SYSV-IPC.TXTにサポートされない理由の記述あり。○カーネルでのリソースリークが発生する○カーネルリソースを枯渇させるサンプルコードも有り。(もちろんAndroidでは動かないのでLinuxで動作させる必要あります。)
Android独自のIPC
●System V IPCの代わりは以下の通りと思われる。○メッセージキューはBinderに。○共有メモリはAshmemに。○セマフォはPOSIX IPCのセマフォに。
Binderの歴史
●元々はNext Generation BeOSに採用される予定だったOpenBinder。(http://www.angryredplanet.com/~hackbod/openbinder/)
● UNIXのCORBA、WindowsのCOMのように分散コンポーネント環境を提供するフレームワーク。
● BeOSポシャっちゃったので残念ながら動くものとしては採用されず。
● OpenBinderをメンテしていたHackbornさんは今はAndroidのプラットフォームエンジニア。よくGroupに投稿しています。
●余談ですがBeの創業者Gasseeも、Danger、Androidの創業者Andy Rubinも元Apple。Appleすごいですね。
Binderの仕組み
Kernel
Binder Driver(/dev/binder)
ServiceManager libBinder
ReceiverApplication
SenderApplication
Binderの仕組み
● Binder Driver(/dev/binder)にアクセスしているのはフレームワーク中、ServiceManager(ソースの/framework/base/cmds/servicemanager/service_manager.c)とlibBinder(/framework/base/libs/binder/ProcessState.cpp)のみ。○この中の仕組みはまだ追えていません。
●ユーザーアプリケーションは直接Binderドライバを操作することはない。
Binderの仕組み
● Binderを受信するアプリはServiceManagerに自身をサービスとして登録する。(図の水色矢印)
●受信するアプリはBBinderを継承し、onTransact()で受信処理を実装。
● Binderを送信するアプリはServiceManagerから送信先サービスを取得し(図の緑矢印)、取得したIBinderサービスに対しtransact()でメッセージを送信(図の赤矢印)。
実際に使ってみる
Binderを受信するNativeデーモン(BinderReceiver)と、送信するNativeアプリ(BinderSender)を作ってみます。
実際に使ってみる 受信側
● main.cpp 受信側起動処理 SurfaceFlingerやAudioFlingerなどが参考になります。
#define LOG_TAG "RECEIVER"#include <binder/IPCThreadState.h>#include <binder/ProcessState.h>#include <binder/IServiceManager.h>#include <utils/Log.h>#include "receiver.h"
int main(int argc, char** argv) {LOGD("Reciever start.");sp<ProcessState> proc(ProcessState::self());sp<IServiceManager> sm = defaultServiceManager();LOGD("ServiceManager: %p", sm.get());Receiver::instantiate();ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();
}
実際に使ってみる 受信側
● receiver.h BBinderを継承した受信処理クラスの定義
#ifndef RECEIVER_H_#define RECEIVER_H_
#include <utils/RefBase.h>#include <binder/IInterface.h>#include <binder/Parcel.h>
using namespace android;
class Receiver:public BBinder {public:
static void instantiate();Receiver();virtual ~Receiver();virtual status_t onTransact(
uint32_t, const Parcel&, Parcel*, uint32_t);};
#endif /* RECEIVER_H_ */
実際に使ってみる 受信側
● receiver.cpp Receiverクラスの実装。受けた数値を5倍して返す。
#define LOG_TAG "RECEIVER"#include <binder/IServiceManager.h>#include <binder/IPCThreadState.h>#include <utils/Log.h>#include "receiver.h"
using namespace android;
void Receiver::instantiate() { defaultServiceManager()->addService( String16("Receiver"), new Receiver());}
Receiver::Receiver() {LOGD("Receiver created.\n");
}
Receiver::~Receiver() {LOGD("Receiver destroyed.\n");
}
実際に使ってみる 受信側
● receiver.cpp 続き BBinder::onTransactの実装部分
status_t Receiver::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {LOGD("Message received code=%d.\n", code);pid_t pid;int num;switch(code) {case 0:
pid = data.readInt32();LOGD("Sender pid=%d", pid);num = data.readInt32();LOGD("Number Data=%d\n",num);
reply->writeInt32(num*5);
break;default:
//do nothing.break;
}return NO_ERROR;
}
実際に使ってみる 送信側
● receiver.cpp 送信処理。自プロセスIDと数値の12を送信する。
#define LOG_TAG "SENDER"
#include <binder/IServiceManager.h>#include <binder/IPCThreadState.h>#include <binder/IInterface.h>#include <binder/Parcel.h>#include <utils/RefBase.h>#include <utils/Log.h>
using namespace android;
int main() {sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder = sm->getService(String16("Receiver"));LOGD("Sender getService %p\n",sm.get());
if (binder == NULL) { LOGE("Receiver Service not found.\n"); return -1;}
実際に使ってみる 送信側
● receiver.cpp 続き。実際の送信処理部分。
Parcel data, reply;pid_t pid = getpid();LOGD("pid=%d", pid);data.writeInt32(pid);
int num = 12;LOGD("num=%d", num);data.writeInt32(num);
//非同期メッセージはFLAG_ONEWAYを4番目に追加binder->transact(0, data, &reply);
LOGD("reply num=%d", reply.readInt32());
return NO_ERROR;}
実際に使ってみる 受信側makefile
● receiver/Android.mk
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \receiver.cpp \main.cpp
LOCAL_SHARED_LIBRARIES:= \libcutils \libbinder
LOCAL_MODULE:= BinderReceiver
include $(BUILD_EXECUTABLE)
実際に使ってみる 送信側makefile
● sender/Android.mk
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \sender.cpp
LOCAL_SHARED_LIBRARIES:= \libcutils \libbinder
LOCAL_MODULE:= BinderSender
include $(BUILD_EXECUTABLE)
実際に使ってみる ビルドスクリプト
● build/build.sh
#!/bin/bashANDROID_ROOT=~/android/myfroyosource $ANDROID_ROOT/build/envsetup.shcd ~/android/myfroyo/external/binder_samplemm
●全体のAndroid.mk
include $(all-subdir-makefiles)
実行結果のログ
D/RECEIVER( 286): Reciever start.D/RECEIVER( 286): ServiceManager: 0xb678D/RECEIVER( 286): Receiver created.D/SENDER ( 288): Sender getService 0xa678D/SENDER ( 288): pid=288D/SENDER ( 288): num=12D/RECEIVER( 286): Message received code=0.D/RECEIVER( 286): Sender pid=288D/RECEIVER( 286): Number Data=12D/SENDER ( 288): reply num=60
Ashmemって?
●プロセスの間で複数のプロセスからアクセスできる共有メモリのAndroid版。
● ashmemドライバ(/dev/ashmem)を介して使用する。● 作成したashmemのファイルディスクリプタをmmap()して使うことができる。
● Androidでは以下で使われている。○システムプロパティの格納領域(@shigepon7 さん情報)○ andrpoid.os.MemoryFileクラスの内部○ グラフィックメモリのバッファ領域○ Skia(2Dグラフィック&フォントエンジン)のバッファ領域
Ashmemの関数
● libcutils.soに含まれる。●定義は/system/core/include/cutils/ashmem.h
● ashmem_create_region○ 新規に共有メモリ領域を作成し、そのfdを返す。ラベルをつけることが可能。
● ashmem_set_prot_region○メモリ領域のアクセス権限などのオプションを設定する。
● ashmem_pin_region○ pinを立てる。pinを立てている領域は使用中と見倣される。
● ashmem_unpin_region○ pinを外す。pinが外れた領域は未使用としてカーネルがメモリ領域を回収する。
● ashmem_get_size_region○メモリ領域のサイズを取得。
実際にAshmemを使ってみる
● initやbionicのシステムプロパティでashmemを使っている箇所を参考にサンプルを書いてみましたが、残念ながらエラー。
●共有メモリにアクセスするためのfdに、共有メモリ作成したのとは別のプロセスからアクセスしましたが、EBADF(ファイルディスクリプタ不正)が発生...
●以前Donutで同じようなコードを書いたときはEACCES(パーミッションエラー)が発生...
●もう少し試してうまくいったら資料を更新します。
次回予告
● Ashmemがうまくいったら、Binder、Ashmem、ソケット通信のベンチマーク(送信データサイズごとに)を計りたいと思います。
● libbinder(/frameworks/base/libs/binder)内のクラス構成も調べたいなと。