デバッガベンダーが語る「Android」とは?Androidセミナーレポート

「AndroidはLinuxとは違う」――。組み込み開発分野におけるデバッグ環境を提供する京都マイクロコンピュータが開発者向けにセミナーを開催し、Androidの開発を行う上でのヒントとなる情報を披露した。

» 2011年09月16日 12時27分 公開
[小山安博,@IT MONOist]

「AndroidはLinuxとは違う」――。組み込み開発分野におけるデバッグ環境を提供する京都マイクロコンピュータが開発者向けにセミナーを開催(2011年8月)し、Androidの開発を行う上でのヒントとなる情報を披露した。AndroidはLinuxをベースとしたソフトウェアプラットフォームだが、実際、Linuxとは大きく異なる特徴を持つ。そのため、LinuxやAndroidの採用を検討する場合、その違いを正しく把握・理解することが重要となる。

 セミナーを開催した京都マイクロコンピュータは、25年以上にわたってJTAG-ICE、ROMインサーキットデバッガ、コンパイラなどの開発環境と、CPUボード類などの製品を提供してきたベンダーだ。今回、同社が新たな取り組みとして同セミナーを実施したのは、開発者に直接アプローチすることで、さまざまなフィードバックをもらい、「さらに商品開発の体制を整えたい。開発者のサポーターになりたい」(京都マイクロコンピュータ 代表取締役社長 山本彰一氏)との思いからだという。

 本稿では、「実際、評価ボードにAndroidを載せたとき、どんなことに気が付いたか」ということから話が進められた、同社 技術部 小林哲之氏の講演「Android is NOT just ‘Java on Linux’」の内容を基にお届けする。

Android is NOT just ‘Java on Linux’

 そもそもAndroidはLinuxをベースにしているが、「Linuxカーネルしか使っていない」と小林氏。AndroidアプリはJavaで作られており、Java SEと似ているが、これも同等ではない。AndroidはJavaを実行環境のDalvik VM(Virtual Machine:仮想マシン)上で実行するが、これ自体も「オラクル(のJava)とは随分違っており、コードをそのまま実行するのではなく、“Dalvik Executable(DEX)”と呼ばれる独自形式のコードに変換し、Dalvik VM上で動作させている」(同)。

 Dalvik VMは、Javaのバイトコードから変換したDEX形式のコードを実行するVMで、Java VMの“スタックベース”と比べ、16ビット単位の“レジスタベース”になっているのが特徴だ。実行速度を向上させるために使われるJIT(Just in Time)は、Androidでも2.2から搭載されるようになり、高速化が図られている。また、Androidではガベージコレクション(Garbage Collection:GC)機能も搭載されているが、これは実行中にアプリ動作が停止する瞬間があって不評だったという。これに対して、Android 2.3からは「コンカレントGC」と呼ばれる技術が投入され、この停止時間が短縮化されているそうだ。

 Androidのシステム構成は、一番下層にLinuxカーネルがあり、その上に各種ライブラリとAndroidランタイムが存在する。小林氏は「Android以前のJava搭載携帯電話では、それまで使っていたOSの上に、後からJava VMを載せたため、メールソフトやブラウザはJavaとは関係なく、そこにさらにJavaが混在していた」と説明。そのため、システムの中身は非常に複雑だったという。これに対して、Androidではシステムの根幹からJavaを動かす構成になっているため、「Javaは“一級市民”という扱いになった」と小林氏は表現する。

Androidのシステム構成 画像1 Androidのシステム構成

 Dalvik VMは、Androidランタイムの中心にあり、ほとんどのDaemonもJavaで記述され、Androidアプリの起動から入力待ち、終了などといったライフサイクルはJava APIで規定されている。

 さらに、AndroidではNDK(Native Development Kit)が用意されており、JNI(Java Native Interface)を利用してC/C++やOpenGL ESといったネイティブコードを実行できるようになっている。「Javaでアプリを書くとき、ゲームなどで性能を上げるためにC++のアプリをリンクさせるような開発ツール」(同)であり、ネイティブライブラリを呼び出すことで実現しているが、アプリのライフサイクル自体は変更がない。NDKでは、新たにネイティブアクティビティが用意され、C/C++の既存のプログラムが移植しやすくなったことで、「Javaの部分を書かなくてもアプリが作れるようになった」(同)点がポイントだ。


 Androidのディレクトリツリーは、通常のLinuxとは異なっている。一般的なLinuxはHDDにファイルシステムが存在する前提で、全てのディレクトリでリード/ライトが可能になっている。しかし、Androidはフラッシュメモリを利用し、リードオンリーでマウントされているルートシステムが存在し、書き換えができないようになっている。つまり「組み込みシステムを意識した構造になっているといえる」(同)わけだ。そのため、例えばシステムアップデートをする場合は、丸ごと置き換わることになる。また、システム実行中は書き換えができないので、バグやマルウェアでもシステムの変更はできない。

Androidの典型的なディレクトリツリー 画像2 Androidの典型的なディレクトリツリー。/rootの/system以下のディレクトリはリードオンリーになっている。通常のLinuxシステムでは、こういったディレクトリも書き換え可能

 次に、Androidのブートシーケンスを見てみると、カーネルからinitプロセスが起動し、そこからDaemonやサービスマネジャー、Zygoteなどが起動している。ZygoteからはDalvik VMなどが起動される。

Androidのブートシーケンス 画像3 Androidのブートシーケンス。initからDaemonやサービス、Zygote経由でDalvik VMが起動する

 ここで登場するinitは、「通常のLinuxと名前は一緒だが、内容は違う」(同)。カーネルから起動しているが、「ルートディレクトリ直下は検索パスに入っていない」(同)ため、initの位置を指定しないとプログラムが動作しなくなってしまう。Linuxとは異なり、スタティックリンクを設定する必要があり、「FreeBSD系統だと大事なプロセスはスタティックリンクされている」(同)のと同等といえる。

 標準のライブラリであるBionicは、「BSD系のものを寄せ集めた」(同)libcやlibmなどが用意されている。WebKitのようにC++で書かれたている部分もあるが、libcは「小さなシステム向けで、C++の型情報などはサポートしていない」(同)という。また、例外は使われないようになっており、こうしたアプリを移植する際はNDKを使うことで解決する仕組みが用意されている。C++のライブラリをスタティックリンクするようにしており、アプリのサイズは大きくなってしまうものの、例外処理が利用できるようになるそうだ。

 prelinkでは、あらかじめAndroidのライブラリに振られたアドレスに対してリンクが設定され、アドレス解決を簡素化している。小さな組み込みシステム向けに作られたシステムで、全ライブラリに固定したアドレスが割り振られ、3Gバイトのユーザー空間に全ライブラリを並べることが可能。普通、UNIXのシステムではライブラリを固定せず、ライブラリが変わるとサイズが変わってしまい、それを解決する必要がある。Androidはシステムの書き換えができないため、簡単にダイナミックリンクが実現できるのがメリットだ。

 Androidでは、独立した各JavaのプロセスでDalvik VMが起動しているため、Javaアプリの数だけDalvik VMが動作している。この場合に一つずつDalvik VMを起動していると時間がかかるので、「ひな型が用意されている」(同)。それが“接合子”を意味するZygoteで、システムフォークで丸ごとコピーを作ることによって、実メモリ空間上は共有しながら、仮想メモリを使って高速化を図っている。メモリへの書き込みがあったところだけをコピーし、書き込みが行われない部分は共有したままにする「Linuxの仮想メモリを使ったうまい方法」(同)を用いている。

 Zygoteでは、あらかじめ1800個ほどのクラスをロードしておき、それをプロセスの元にすることで、「フォークした瞬間に子プロセスが動く」(同)状態になる。ダイナミックリンクライブラリもメモリにロードされた状態で、フォークした瞬間に子プロセスが動作する形だ。プロセスの生成では通常、forkとexecを使うが、execでは「まっさらになってしまう」(同)ために使われず、「普通は前のプロセスの残骸を有効活用する」(同)という考え方に基づいているとのこと。

Zygoteはコピーによって子プロセスを起動する 画像4 Zygoteはコピーによって子プロセスを起動する

 また、LinuxはもともとマルチユーザーのOSで、複数の人が1つのコンピュータを共同で使う前提であり、他人のファイルなどが見えては問題があるので、それを区別する仕組みとして「UID」「GID」といったものが用意されている。この仕組みを流用したのがAndroidだ。

 Androidではアプリを任意にダウンロードして利用できるが、アプリ間で相互に読み書きできてしまっては問題がある。それをOSが区別するためにUIDが使われている。「Androidは全てのアプリが個別のUIDを持っていて、その権限でファイルが作成され、他のアプリからそのファイルは読み書きできない」(同)。ちなみに、ZygoteはUIDが0(root)で動作する。

 Androidで使われるJavaのライブラリは、従来の携帯電話で利用されていたJava MEと比べるとJava SEに近いが、それでも同等ではないという。特に、画面表示周りでAWT(Abstract Window Toolkit)やSwingがなく、Android特有のグラフィックライブラリを使っている。他にRMI(Remote Method Invocation)も搭載されておらず、独自の手法を使っている。

 小林氏は、AndroidがLinuxカーネルを採用した利用の1つとして、「既に多くのデバイスドライバをサポートしているからではないか」と推察。AndroidはApacheライセンスがベースになっているものが多く、BSD系列のカーネルの方が「ライセンス的には統一できそう」(同)にもかかわらず、Linuxカーネルの方がデバイスドライバのサポートが多いので採用したのではないかと予想する。その中でAndroid特有のカーネルドライバとしては「binder」「ashmem」「wakelock」「logger」などがある。

Androidは他のどれ(OS)とも似ていない

 さて、Androidの全てのソースコードは、Googleの特定のサービスを除いて全てダウンロード提供されており、ビルドは容易だ。「高速なPCならフルビルドは20分ぐらい」(同)だという。「ほぼ全てのソースが手に入って、簡単なスクリプトでビルドでき、破綻せずに管理されているのがすごい」と小林氏は話す。

 小林氏は、Androidを「Linuxだがカーネルしか使っておらず、実際は随分と違う」と指摘。Android特有のデバイスドライバの拡張や、小さなシステムとして動作するようなチューニングがされているなど「他のどれ(OS)とも似ていない」(同)という。

 続いて小林氏は、「なぜAndroidなのか?」と問い掛ける。Androidではシステムとアプリが「きれいに分かれている」という特徴があり、サードパーティーによるアプリのダウンロード、コミュニティーの存在、そして簡単にアプリがアップグレード・バグフィックスできる点に加え、HTML5にも対応した高機能なブラウザコアを備える点などがメリットとして挙げられる。

 別のプラットフォームの開発者がAndroidアプリを開発する場合、「これまでの資産のどの部分を生かして、どの部分を捨て去るかという選択が必要になる」(同)と話す。その検討には、Androidでアプリを実行するときのオーバーヘッド、ストレージやメモリの使用量、OS自体のバージョンアップが早いことによるその追従といった観点に加え、「Androidそのものにはなるべく手を触れずにアプリを作る視点も考える必要がある」という。

 アプリを移植する場合、従来のバイナリがそのまま動作する場合がある。これはARMのEABIを使っているためで、例えばARM上で動作するUbuntuなどのように同じABI(Application Binary Interface)であれば、全てのファイルをコピーすれば動作してしまう。小林氏の経験によると、Rubyが再コンパイルせずにそのまま動作したそうだ。ただし、これは非常に簡単な手法だが、Androidのシステム本体とは別にライブラリを導入するため、無駄になってしまう。また、全ての場合で必ず動作するとは限らない。

 リビルドする場合は、Androidのソースをテンプレートとして用い、いらないファイルを省いた上で「Android.mk」を修正してビルドすればいい。メモリやストレージの利用量は最小限に抑えられるメリットはあるが、Cのライブラリが簡略化されていたり、マルチバイトライブラリのサポートが最小限だったり、「IPv6のサポートがよくない」(同)といった問題があるそうだ。

Copyright © ITmedia, Inc. All Rights Reserved.