25
Binderのはじめの一歩と Androidのプロセス間通信(IPC) Android IPCのとりあえず1回目 2010/10/31 @l_b__ 横浜Androidプラットフォーム部 3回勉強会

Binderのはじめの一歩とAndroid

  • Upload
    lb

  • View
    239

  • Download
    1

Embed Size (px)

Citation preview

Page 1: Binderのはじめの一歩とAndroid

Binderのはじめの一歩とAndroidのプロセス間通信(IPC)

Android IPCのとりあえず1回目

2010/10/31@l_b__

横浜Androidプラットフォーム部第3回勉強会

Page 2: Binderのはじめの一歩とAndroid

目次

●Androidのプロセス間通信●Binderって?●何故Binder?●Android独自のIPC●Binderの歴史●Binderの仕組み●実際に使ってみる●Ashmemって?●Ashmemの関数●実際にAshmemを使ってみる●次回予告

Page 3: Binderのはじめの一歩とAndroid

Androidのプロセス間通信

● AndroidのNative側でプロセス間通信(IPC,InterProcess Communication)を行うには以下の方法がある。1. Binder2. Ashmem(Anonymous Shared Memory)3. UNIXドメインソケット4. TCP/UDPソケット5.名前付きパイプ

  ※ただ、Androidソース内に名前付きパイプを使っている箇所は無い。

Page 4: Binderのはじめの一歩とAndroid

Binderって?

●Android独自のプロセス間通信の一つ。●POSIXメッセージの置き換え。●プロセス間で小さいデータ(数百〜数千byte程度)を高速にやり取りするために使用。

●何故POSIXのIPCがあるのにBinderという機構が導入されたのか。

Page 5: Binderのはじめの一歩とAndroid

何故Binder

●AndroidはSystemV(UNIXの1種)のIPCをサポートしていない!

●NDKの/docs/system/libc/SYSV-IPC.TXTにサポートされない理由の記述あり。○カーネルでのリソースリークが発生する○カーネルリソースを枯渇させるサンプルコードも有り。(もちろんAndroidでは動かないのでLinuxで動作させる必要あります。)

Page 6: Binderのはじめの一歩とAndroid

Android独自のIPC

●System V IPCの代わりは以下の通りと思われる。○メッセージキューはBinderに。○共有メモリはAshmemに。○セマフォはPOSIX IPCのセマフォに。

Page 7: Binderのはじめの一歩とAndroid

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すごいですね。

Page 8: Binderのはじめの一歩とAndroid

Binderの仕組み

Kernel

Binder Driver(/dev/binder)

ServiceManager libBinder

ReceiverApplication

SenderApplication

Page 9: Binderのはじめの一歩とAndroid

Binderの仕組み

● Binder Driver(/dev/binder)にアクセスしているのはフレームワーク中、ServiceManager(ソースの/framework/base/cmds/servicemanager/service_manager.c)とlibBinder(/framework/base/libs/binder/ProcessState.cpp)のみ。○この中の仕組みはまだ追えていません。

●ユーザーアプリケーションは直接Binderドライバを操作することはない。

Page 10: Binderのはじめの一歩とAndroid

Binderの仕組み

● Binderを受信するアプリはServiceManagerに自身をサービスとして登録する。(図の水色矢印)

●受信するアプリはBBinderを継承し、onTransact()で受信処理を実装。

● Binderを送信するアプリはServiceManagerから送信先サービスを取得し(図の緑矢印)、取得したIBinderサービスに対しtransact()でメッセージを送信(図の赤矢印)。

Page 11: Binderのはじめの一歩とAndroid

実際に使ってみる

Binderを受信するNativeデーモン(BinderReceiver)と、送信するNativeアプリ(BinderSender)を作ってみます。

Page 12: Binderのはじめの一歩とAndroid

実際に使ってみる 受信側

● 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();

}

Page 13: Binderのはじめの一歩とAndroid

実際に使ってみる 受信側

● 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_ */

Page 14: Binderのはじめの一歩とAndroid

実際に使ってみる 受信側

● 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");

}

Page 15: Binderのはじめの一歩とAndroid

実際に使ってみる 受信側

● 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;

}

Page 16: Binderのはじめの一歩とAndroid

実際に使ってみる 送信側

● 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;}

Page 17: Binderのはじめの一歩とAndroid

実際に使ってみる 送信側

● 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;}

Page 18: Binderのはじめの一歩とAndroid

実際に使ってみる 受信側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)

Page 19: Binderのはじめの一歩とAndroid

実際に使ってみる 送信側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)

Page 20: Binderのはじめの一歩とAndroid

実際に使ってみる ビルドスクリプト

● build/build.sh

#!/bin/bashANDROID_ROOT=~/android/myfroyosource $ANDROID_ROOT/build/envsetup.shcd ~/android/myfroyo/external/binder_samplemm

●全体のAndroid.mk

include $(all-subdir-makefiles)

Page 21: Binderのはじめの一歩とAndroid

実行結果のログ

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

Page 22: Binderのはじめの一歩とAndroid

Ashmemって?

●プロセスの間で複数のプロセスからアクセスできる共有メモリのAndroid版。

● ashmemドライバ(/dev/ashmem)を介して使用する。●  作成したashmemのファイルディスクリプタをmmap()して使うことができる。

●  Androidでは以下で使われている。○システムプロパティの格納領域(@shigepon7 さん情報)○  andrpoid.os.MemoryFileクラスの内部○  グラフィックメモリのバッファ領域○  Skia(2Dグラフィック&フォントエンジン)のバッファ領域

Page 23: Binderのはじめの一歩とAndroid

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○メモリ領域のサイズを取得。

Page 24: Binderのはじめの一歩とAndroid

実際にAshmemを使ってみる

● initやbionicのシステムプロパティでashmemを使っている箇所を参考にサンプルを書いてみましたが、残念ながらエラー。

●共有メモリにアクセスするためのfdに、共有メモリ作成したのとは別のプロセスからアクセスしましたが、EBADF(ファイルディスクリプタ不正)が発生...

●以前Donutで同じようなコードを書いたときはEACCES(パーミッションエラー)が発生...

●もう少し試してうまくいったら資料を更新します。

Page 25: Binderのはじめの一歩とAndroid

次回予告

● Ashmemがうまくいったら、Binder、Ashmem、ソケット通信のベンチマーク(送信データサイズごとに)を計りたいと思います。

● libbinder(/frameworks/base/libs/binder)内のクラス構成も調べたいなと。