本日は技術記事です。
Androidの電源を入れた時の処理についてまとめてみました。
この内容は『Androidを支える技術II』の6章にも詳しく書かれていますので、
解説の順番もそちらに準拠します。
より詳しい解説を読みたい方は書籍も参考にしてください。
(2024/12/07 14:18:13時点 Amazon調べ-詳細)
Linux全般の起動では最終的にinitというプロセス(pid=1)が起動しますが、
それまでの流れとしては以下のようになります。
- ブートローダーの実行
- カーネルのロードと実行
- カーネルの一部初期化(init 起動のための)
- init の起動
- カーネルの残りの初期化・initによる初期化・ファイルシステムのマウント
ブートローダー
ブートローダーは、abootというパーティションに置かれています。
LKブートローダーという汎用のプログラムがカスタムされて使われています。
このブートローダーの特徴としては
・改ざんを避けるためメーカーの秘密鍵で署名されたイメージしか起動できない
・デバイスの特有の処理を行えるようにする DeviceTreeのサポート
・fastbootのサポート
があります。
端末のroot化では秘密鍵を読み飛ばすような処理をさせることがあります。
これをするとメーカーの保証はまず無くなります。
その後、リカバリイメージで起動するようにしたり
rootで実行させるために、suコマンドをxbinに置いたりするような手順が一般的です。
fastbootでは以下のような操作があります。
flash ファイルのパーティション書き込み
erase パーティションの中身を消す
boot 指定したカーネルやRAMを転送して起動
continue 通常起動
reboot 再起動
reboot-bootloader このfastbootモードで再起動
[oem系コマンド]
oemunlock ブートローダーの署名を確認しないようにする
oemlock ブートローダーの署名を確認するようにする
oemverified ブートローダーの署名を確認するモードか確認
oemdevice-info 端末情報
oemenable-charger-screen 充電画面を表示するように
oemdisable-charger-screen 充電画面を表示しないように
oem-select-display-panel ディスプレイの選択
initプロセス
起動時に実行される特別なinitプロセスがAndroidで行っているのは大きく以下の4つです。
1.ファイルシステムなどの初期化
2.システムサービスやデーモンを立ち上げ
3.システムサービスやデーモンの親プロセスとしてシグナルを書し、必要に応じてリスタート
4.プロパティの処理
2で、Zygote(全てのJavaプロセスの親)も立ち上がります。
4は、Windowsでいうレジストリのようなものです。
initプロセスは、root権限で起動してずっと存在しています。
プロセスは入力の監視を行ったり、
また、init.rcというファイルに従い権限を委譲しながら様々な処理を起動していきます。
特定のプロセスを場合に応じてrestartしたりします。
initのmain関数前半
1.mount
2.プロパティのデフォルト値の読み込み
3.epoll_fd 関連の初期化
4.init.rc のパースと実行
3の、epollはLinuxの関数でファイルディスクリプタを監視するものです。
Android(Linux) のタッチやキーなど入力系の処理はファイルの仕組みを利用しています。
通常ファイルやソケットを読み取るには read() で待ち受けておく必要があるのですが、readはブロッキング処理なのでそこで止まってしまいます。
また、タッチやキー操作は複数の入力があって、順番も決まっていないです。
そこで、複数のファイルの入力待ちをハイパフォーマンスで行えるのが epollです。
4の、init.rcにはトリガーとアクションを記述することができます。
ここに書いたものをActionManagerに積んでおき、無限ループ内で随時実行していくことになります。また、いくつかのアクションもハードコーディングで詰まれます。
initのmain関数後半
initのmain関数後半は無限ループとなっています。
起動時から常駐する重要なプロセスということです。
無限ループ内は以下の処理がメインとなっています。
1.先述のAcionManagerに積まれているものの実行
2.プロセスのrestart処理
※init.rcのServiceセクションで指定のもの
3.epollで待ち受けているファイルのコールバック呼び出しとプロパティの処理
プロパティについて
プロパティはWindowsでいうところのレジストリに近いもので、
単純なキーと値のペアです。
ただ数が膨大なので、initプロセスのメモリ上で管理しています。
なので、前節のinitという特権ユーザーで動いているプロセスにシグナルを投げて、
epollで受け取って書き込んでもらうという手順で更新しています。
読み取りは全てのプロセスで行う可能性があるので、
mmapにより /dev/__properties__ ファイルとして読み取り専用で公開されています。
読み書きは、/system/binの
setprop
getprop
コマンドで行えます。
Androidのコマンドラインツールからでも
adb shell getprop
とすることで一覧が表示され
例えば、
service.adb.tcp.port 5678
などと表示されます。
init.rc
最後にinit.rcについてです。
PC版のrcファイルの記述とAndroidのrcの記述は異なっています。
Androidの init.rc は、Android Init Language というスクリプト言語で書かれた設定ファイルです。
・条件である「トリガー」と、その時行うコマンドである「アクション」
・「サービス」
が書かれています。
トリガーとアクションの例
例えばトリガーとそれに続くアクションは以下のように書かれます。
on early-init
write /proc/1/oom_score_adj -1000
#中略
mkdir /mnt 0775 root system
このトリガー early-init は文字列で init の main関数前半で指定している文字列です。
このように、トリガーを文字列で分けて依存関係を解決しながら起動しているのが init.rcの特徴です。
また、後続のアクションはコマンドを書くのですが、
Androidはバックグラウンドから戻ってきた時にアプリが死んでいることがあると思います。これはメモリが少なった時にどのアプリからkillするかという仕組みがあるのですが、それの参考スコアを低くしています。
そして、mntディレクトリを生成しています。
サービスの例
例えばAndroidのインストーラーデーモンである installdというサービスは
chmodできる権限が必要がありinit.rcにより起動されます。
init.rcでは
service installs /system/bin/installd
class main
socket installed stream 600 system system
のように記述されています。(参考図書1.5節p.35より)
終りに
いかがでしたでしょうか?
駆け足で、Androidのブートとinitプロセスについて解説してまいりました。
より詳しい解説を読みたい方は書籍を参考にしてください。
(2024/12/07 14:18:13時点 Amazon調べ-詳細)
ではでは~