2017年05月30日

「Androidを支える技術U」を読んで その1

 Androidを支える技術〈U〉を読みました。


 第一巻と同様に、大変感動しました(^^


 この書籍は、Android のOSの心臓部を解説したものです。ですが、もともと想定されているのが専門的な開発者レベルなので、第一巻同様、サンデープログラマでしかない私にとっては理解できないところの方が多かった(^_^;)のですが、それでもAndroid開発大好きな私としては、感動ものでした(^^)/


 以下、感動したところを中心に…というか、それしかできませんので、書いてみますm(__)m


1.大変セキュアなアプリ環境


 Androidと言うと、iOSに比べてウィルスが多いとか、個人情報が抜かれるなどと酷評されますが、この解説を読むと、Androidは大変安全でセキュアなOSということがよくわかります。ここは是非ともAndroidのセキュリティをディスっている方に読んでほしいと思いました。


 以下、著者の言葉で説明します。


 Androidには、アプリにはそのアプリを実現するために 必要な機能にだけアクセスできるようにし、 それ以外の機能にはアクセスできないようにする仕組みがあります。


 まず、他の同様なOSと比較しながら、著者は説明していきます。 Androidは、あるアプリは別のアプリを信用していません。従ってアプリ A の書いた情報はアプリ B には読むことができません。 これが同じユーザーの走らせたソフトウェア同士は大体同じ権限で動く旧来の OSと違うところです。従来の PCは別のユーザーを信用していなかったのが、 Android では別のアプリを信用しなくなったのです。


 AndroidはもともとLinuxの上に構築されたシステムです。つまり、 Linux 上では、あるユーザーが他のユーザーの作った秘密のファイルをこっそり見ることはできない訳です。


 これをAndroidはどのように実現しているのかと言うと、 Androidはこの Linux のマルチユーザーのセキュリティモデルを、全く別の目的であるアプリ同士を信用しないセキュリティモデルを構築することに使用しています。なんと、 Android の書くアプリは Linux からは別々のユーザーと見えるようになっているのです。


 具体的には、Androidのアプリがインストールされる時、アプリに固有の秘密の情報は、個々のアプリ毎に作成した/data/data/[パッケージ名]というディレクトリに保存するため他のアプリから見ることはできません。結果として、 共有の必要のないデータは dataディレクトリの下に作成すると自身のアプリでしか見ることができないということが Linux のマルチユーザーの仕組みで担保される訳です。


 そして最終的にLinux はログインするものの中に悪意のあるユーザーがいてもセキュアに運用することができています。同様に Android においても悪意のあるアプリを入れて起動してしまっても、それだけで無制限に全ての情報を持っていかれることはありませんとなります。言い換えると このようにアプリを別々のユーザーと扱うことで、長い実績と信頼のある Linux のセキュリティモデルの強固さをそのまま Android の目的に用いることができます。

 今回、これを読んで初めて、セキュリティの素人である私でも、Androidのセキュリティに、自信をもって安心することができました(^^)



posted by 【なかま】 at 13:33 | Comment(0) | android

「Androidを支える技術U」を読んでその2

 Androidの開発者ならば当然ご存知のように、そのアプリはシステムからいつkillされても良いように開発することが求められます。

 この著作は、Androidがメモリ不足時にどのように動作するのかを詳細に解説しています。

 以下、私の理解した範囲で要約してみたいと思います。

2.Androidのメモリ不足時の動作

 (1) スマホにはスワップ機能はない

 まず、スマホという環境は、WindowsなどのOSと異なり、スワップ機能がないのか基本です。それは、スマホなどの多くの機器では2次記憶は何らかの形のフラッシュメモリ(eMMCやmicroSDカード)であるためで、書き込みは遅くバッテリーも食い、しかも回数に制限があるからです。

 (2) スマホは常にメモリ不足

 そして、Android にはスワップがなく沢山のアプリがどんどん起動されるためにスマホにとっては現在でもメモリー不足はたまにしか起こらない異常事態ではなく、日常的に頻発する出来事なのです。

 (3) 不要プロセスはkillされる

 そして、AndroidはLinuxの上に構築されています。そこで、Linuxはシステムがメモリ不足で正常に動作しなくなる前に、Androidがメモリ不足になった時に、どのプロセスをkillするかを判断するのに使う仕組によって、メモリを解放するために積極的に不要なプロセスをkillします。

 この時、システムがメモリ不足に陥った時にkillされるプロセスを選ぶ時の、プロセスのkillされやすさを表すパラメータとしてoom_score_adjという指標を基準にします。

 そして、Linux がプロセスを kill するのはメモリー不足の最終段階ですが、Androidはそれよりも前の段階でメモリを解放するために積極的に不要なプロセスをkillします。

 さらに詳しく見てみると まず、プロセスを Linux カーネルがkillするよりも前の段階にカーネルがファイルシステムにキャッシュの解放を依頼するフェーズがあります。ここでLowMemoryKillerドライバーが不要なアプリのプロセスをkillします。さらに Linux カーネルがプロセスを kill する段になった時は、 Android がoom_score_adj という kill しても良いプロセスを通知するスコアを設定することで Android としてkillしても良いと思うプロセスから Linux にkillされる仕組みとなっています。

 (4) 不要Activityはkillされる

 これを開発者の視点で見ると、要はActivityがkillされるということです。その基準は、 基本的にはそのプロセスが現在画面を占有している Activity を含んでいるかContentProviderを含んでいるかあるいは SDK のServiceを含んでいるかなどの情報を管理してそれらの情報から現在重要そうなプロセスを選択しLinuxにkillを依頼する仕組みとなっています。

 このようにして、Androidは、ある程度余裕がある状態の時から頻繁に裏に行ったActivityのプロセスをkillしてメモリを開けていくことでシステムの正常稼働に必要な程度のメモリをいつも残しておくように努力しています。

 私がこの項目を読んで思うのは、この話のキモは、その時点でAndroidにとって不要なActivityは、Androidがスコアを振り、そのスコアに基づいてもともと存在しているLinuxの不要部分を削除する仕組みに依頼してkillして貰っていることだと思います。

 (5) Activityのライフサイクルへ続く

 そして、頻繁に起きるメモリ不足によってkillされた多数のActivityが、ユーザーから見て、同時に多数動いているようにみせかける仕組みが「Activityのライフサイクル」ということになります。(あってるかな?)

 この件については次回にします。


posted by 【なかま】 at 12:46 | Comment(0) | android

「Androidを支える技術U」を読んで その3

3.Androidのライフサイクルの役目

 前回書きましたように、頻繁に起きるメモリ不足によってkillされた多数のActivityが、ユーザーから見て、同時に多数動いているようにみせかける仕組みが「Activityのライフサイクル」という風に、私なりに解釈しました。

 著者の言葉で言うと、Androidが裏に行ったプロセスをkillしたり、それを再作成したりすることをそれほど意識せずに、まるでずっとアプリが立ち上がり続けているかのように開発ができるといのがライフサイクルの考え方です。

 しかし、著者も最後に書かれているように、一度でもアプリを開発した経験がある方であれば、「(実現はそう甘くありませんが)」ではあります(^_^;)何度悩んだことか(T_T)

 以下、著作は、ライフサイクルのもたらすものをユーザー視点と開発者視点から見ています。

(1) ユーザー視点から実現したいこと。

 ユーザーは無制限にアプリを起動して使っていく、そしてシステムが勝手に最近使っていないアプリを終了していき、しかしユーザーがそのアプリにまたやってくると前の状態を復旧する、これが通常のAndroidの想定された使い方です。 このように振る舞うことで ユーザーの端末のメモリリソースを最大限活用して様々なタスクの間を移動したときのレスポンスを機敏なものに保ちつつ、メモリが必要なタスクが動く時にはその分のメモリを自動的にあけようとします。

(2) アプリ開発者の視点から実現したいこと。

 いつ終了されても自動的に再開するコードを書くのは大変です。そこで、開発者が「一定の約束」に従ってコードを書けば、アプリの開発者は自分のプロセスが kill されて再開するときも、killされずにメモリ上にあったままの場合でも対応できるようにしたい。この「一定の約束」が「Activityのライフサイクル」です。

 そこで、開発者に対して具体的に求められるのは、裏に入ったActivityはkillされることがあるというよりは基本的にはkillされるものとして実装しておき、けれどキャッシュとして残っている場合にはそれを再生成する代わりに使い回していると考える方が実装者としては正しい見方となります。

 この結果として、このように裏に入ったActivityを キャッシュとして扱うことで、メモリが贅沢なマシンでは沢山の実体を残しておくことでたくさんのタスク間の移動を機敏に行える一方で、メモリが少ないマシンでも同じコードで自然とメモリを節約して複数タスクの切り替えのパフォーマンスは犠牲にしても、主要なタスクは問題なく動くというシステムを実現しているのが Android ということになります。

 いやー、改めてライフサイクルを意識してコードを書くことの重要性が再認識されます。

(3) Bundleの摩訶不思議

 最後に、ライフサイクルのひとつ、onSaveInstanceStateの中で利用される復旧情報を保存するためのBundleですが、著者は興味深い考察をされています。

 BundleはSystemServerのプロセスのメモリを使用するのでBundleにあまり大きなものを保存してはいけません。SystemServerのメモリが大きくなりすぎるとメモリを開けようとして裏のActivityをkillする頻度が上がるためタスクの切り替えがすごく遅くなったように感じますし、メモリー不足によるアプリの異常終了などが多くなってしまいます。

 しかし、

 Bundleは著者の知る限り ファイルには書き出されず、SystemServerのメモリ上に保持されたままです。個人的にはこれは信じがたいのですが、Bundleを保存してメモリを開放をしている箇所を見つけるべく相当長いことソースコードの中を探してもどうやっても該当箇所が見つけられませんでした。これではBundleにたくさんの値を入れるとSystemServerという 共有資源のメモリが圧迫されてOOMKillerが頻繁に発動するようになってしまう気がするのですが… だそうです。

 けれど、SystemServerはよほどのことがない限りkillされることはないそうですから、著者のおっしゃるように、ほどほどの大きさであれば、Bundleは安全に使えると理解し、ライフサイクルの基本にのっとって、onSaveInstanxeStateを利用していきたいと改めて思いました。

posted by 【なかま】 at 11:39 | Comment(0) | android

「Androidを支える技術U」を読んで その4

 今回で最後になります。

 第一巻、二巻を通じて、素人の私でもAndroidの凄さ、素晴らしさが少しは理解できたかと思います。

 このような素晴らしい書籍を発行された著者に改めて感謝いたしますm(__)m

4.管理者がいなくても動き続けるシステム

 私が最初に触ったAndroidの端末はサムスンのGalaxy Sでした。この端末は、よくハングして、ひとりでに再起動する不安定な端末でした。ですが、著作を読むと、この再起動は、どうしようもなくなった時に停止することなく再起動するようになっていた訳で、それはそれでよくできたシステムだったのかしれないなと、今は思います。

 最後は、そんなシステムを、著作に従って、電源投入の開始時点から見ていきます。

 まず、以前書きましたが、Androidは一般に言われているようにセキュリティが甘いシステムではありません。著作によれば、Android のブートローダーは通常、端末メーカーのみが知る秘密鍵で署名されているシステムイメージだけをロードします。署名されていないシステムイメージはロードをしません。このようにしてシステムイメージに悪意のあるファイルをこっそり追加したりするのを防いでいます。こうしてルートパーティションやRAMディスクなどは署名されたものと同じであることが保証され、後から変なウイルスなどがこっそり入り込むことができなくなっています。ハードウェアとソフトウェアが独立している PC などでは実現しづらい仕組みだそうです。

 次に、今までに見てきたように、Androidは、勝手にプロセスをkillしていきます。そのため、重要なシステムがkillされることもあるかと思いますが、それでも最近の端末はハングすることなく、ずっと電源を入れたままでも、安定して動いてくれています。

 著書を読んでみると、その背景が理解できます。いや、理解できたつもりかも(^_^;)

 再起動が自動で行われることで、万―OOMKiller(Out Of Memory Killer)などである程度重要なサービスがkillされてしまっても、自動的に再起動されてシステム全体としてはメンテナンスフリーで安定して動き続けます。

 さらに詳細な説明があります。私の頭では半分も理解できませんでしたが、雰囲気で見てください(^_^;)

 異常終了すると通信用のソケットが残ってしまって再帰動時にソケットの作成に失敗してうまく起動しなくなるというのは、出来の悪いデーモンにはありがちなことです。 Android では init が正しく後処理をしてくれるのでそういった心配は必要ありません。各サービスがどのようなソケットを作っているかという情報がすべてinit.rc に記述されているため、あるプロセスが異常終了したときもそのプロセスが持っていたソケットをinitプロセスが正確に把握しているため、正しく後処理を行います。サービスは、oneshotの指定がない限り、異常終了した場合などでも自動的に再起動されます。この機能を実現するためには、そのサービスが使用しているソケットや、そのサービスが再起動する時に一緒に再起動をしなくてはならないサービスの情報などが必要となります。 それぞれのデーモンが自分勝手にソケットなどを管理していると、そのうちの1つがうまく再起動に対応していないだけで全体としてモジュール郡の再起動ができないシステムとなってしまいます。 Android では再起動の時などに問題になりがちなソケットの管理をinitが行うことでこうした出来の悪いサービスができる可能性を減らしているわけです。

 なんか、凄いという気持ちになってくるでしょう?

 結果として、このようにして管理者のいない携帯電話というシステムを家電的に使うことができるわけです。

 …ということで、最近のAndroid端末は、昔に比べると安定してきたなぁとしみじみ思いました。


posted by 【なかま】 at 10:44 | Comment(0) | android