2010年12月09日
Androidのinit
Androidで一番最初に起動するユーザープロセスであるinitについて調べたのでそのメモ。
(注:この記事はAndroid 2.2のソースコードに関するもの。)
/init
initはルートディレクトリの直下に置かれている。すなわち、/init
通常のLinuxではinitは /sbin/init
Linuxカーネルはデフォルトの設定では最初のプロセスを
- /sbin/init
- /etc/init
- /bin/init
- /bin/sh
の順に探しにいく。(kernelのinit/main.c init_post参照)
このため、Androidを起動するときにはカーネルのブートパラメータに "init=/init" を追加して明示的にinitのパスを指定しなければならない。それを忘れると "No init found. Try passing init= option to kernel." というメッセージを見ることになる。
Androidのinitは他のLinuxのものとは全く異なる。init.rcの書式も独自。
Androidのinitはスタティックリンクされている。そのためinitが動作するのにダイナミックリンカの助けはいらない。
(ちなみにLinixの/sbin/initはダイナミックリンクされているので、何かのトラブルでダイナミックリンクの機構が動かなくなるとシステム全体が起動できなくなる。*BSDではinitはスタティックリンクだそうだ。)
initの構成
initのソースは system/core/initのディレクトリにある。
initは大きく2つの部分に分かれる。ブート処理部とデーモン処理部。
(2011.12.13追記:Android2.3以降はブート処理部分は実行する関数をキューに積んで実行するようになり、複雑化した。)
int main(int argc, char **argv) { /* ブート処理 */ for (;;) { /* デーモン処理 */ } }
ブート処理は起動時に一度だけ実行される。
デーモン処理はシステムが動作している限り繰り返し実行される。
ブート処理
/dev, /proc, /sys をマウントした後に/init.rc のスクリプトを読み込んで実行する。
このあたりは横浜Androidプラットフォーム部の第3回、第4回の勉強会に詳しい資料がある。
デーモン処理
device_fd, property_set_fd, signal_recv_fd, keychord_fdの4つのファイルディスクリプタをpollシステムコールで監視している。
device_fd
カーネルからのデバイスの状態の変化の通知を受けて、/dev以下のデバイスファイルの生成と削除を行う。
通常のLinuxではudevdが行っていること。
生成するデバイスファイルのmode, uid, gidはソースdevices.cの中のstruct perms_ devperms[]か、init.rcでのdeviceコマンドで指定できる。
(2011.12.13追記。Android2.3以降では、この処理はueventdに移された。)
property_set_fd
システムプロパティの書き込み処理を行う。
システムプロパティはinitが原本を管理していて、この原本だけが書き込み可能になっている。
initから起動された各プロセスはシステムプロパティのデータをashmemの機構を使ってread onlyで共有している。そのためシステムプロパティの読み取りは共有メモリをアクセスし、書き込みはソケットによるプロセス間通信でinitに処理を依頼することになる。
(Androidのシステムプロパティに関しての詳細はまた別の機会で。)
(2011.12.13追記。システムプロパティに関しては以下のページのスライドの後半を参照。)
Inter-process communication of Android
signal_recv_fd
子プロセスが終了したときにsignalが発生するように設定されている。
子プロセスが終了したときにwaitシステムコールで、その後始末をするのは伝統的なUnixでのinitの役割。
終了したプロセスがserviceとして登録されているものである場合には、登録されている情報に従って再起動等を行う。そうでない場合には "untracked pid %d exited."とコンソールに出す。
CRITICALの属性のserviceが一定期間内に一定回数以上クラッシュした場合にはシステム全体をリブートするようになっている。
keychord_fd
あらかじめ登録した複数のキーの入力の組み合わせを監視し、それに対応するserviceを起動する。
ただし、この機能が使えるのはシステムプロパティが"ro.debuggable" == "1" || "init.svc.adbd" == "running" の時だけ。
デフォルトのinit.rcではこの機能を使うserviceは無い。デバッグ時にメニューキーと戻るキーを同時に押したら何かの情報をSDカードに書き出すといったことに使えそう。
(2011.12.13追記。Nexus One, Nexus Sではこの機能を使っているようだ。)
お知らせ
2010年12月10日のCELFテクニカルジャンボリーでこの話をします。
KZM-A9-DualボードにPARTNER-Jetをつないで気になるところをデバッガで追いかけるところも紹介する予定です。
関連するページ
CELFテクニカルジャンボリーでAndroidのinitの話
Androidでinitの代わりに/system/bin/shを動かしてみた