Androidをデバッグしメモリダンプからデータを復旧する:作りながら理解するファイルシステムの仕組み(10)(1/3 ページ)
Android組み込みボードを使って、デバッグしながらファイルデータ管理の内容を探る! 併せて、“実践知”も紹介する
今回は、「Linuxカーネルのファイルデータ管理」について解説していきます。
ただ、単なる教科書的な説明だけではつまらないので、Android実験用に購入した組み込みボード(Android移植済み)を使い、実際のファイルデータ管理の内容をデバッグしながら見ていきたいと思います。使用するデバッグ手法としては、Linuxでおなじみの「kgdb」(注1)を利用することにします。
そして、ここで説明するLinuxファイルデータ管理の理解をより“実践知”として生かせるように、「Linuxカーネルメモリからファイルデータを復旧する方法」を紹介します。
具体的には、デバッグ中に「メモリダンプ」を採取し、そのダンプ情報からあるファイルのデータを復旧していきます。このやり方を習得しておくと、ログファイルなどのファイルデータをメモリダンプから参照できるようになるので、裏ワザとして覚えておくと大変役に立ちます(注2)!
Android組み込みボード
今回使用するAndroid組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」を図1に示します(注3)。
図1 Android組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」
Androidのバージョンは「2.1(Eclair)」です。ただし、デモ用なので、問題にぶつかったら自分で直していく必要があります(そこがだいご味でもあります)
組み込みボードのデバッグ方法
この組み込みボード(ターゲットボード)のデバッグ方法ですが、選択肢として以下の2つがあります。
- JTAG(注4)を使ったデバッグ
- kgdbを使ったデバッグ
今回は以下に示す理由により、kgdbを選択することにしました。
今回、ターゲットボードとホストPCは、図2のようにケーブル接続を行いデバッグします。
kgdbでのデバッグでは、ターゲットボードとホストPCとの通信には「シリアル通信」を使うのが一般的だと思います。しかし、このボードにはシリアル通信用のポートが1つしかなく、これをkgdb用にしてしまうとコンソールが見えなくなってしまうので断念しました。一方、今回のデバッグ対象モジュール(ファイルシステム)では、LANを使った通信を行わないので、kgdbでの通信には“LANを採用”することにしました(KGDB over Ethernet)。そこで、ターゲットボードとホストPCが直接通信できるようにクロスLANケーブルで接続しているわけです(もちろん、ストレートLANケーブルをハブに接続して通信させることもできます)。
Linuxカーネルのファイルデータ管理
今回デバッグ対象とするファイルシステムおよびファイルの情報は、以下のとおりです(表1)。
使用するファイルシステム | tarfs |
---|---|
ファイルシステムのブロックサイズ | 512bytes |
デバッグ対象のファイル | Android起動スクリプトファイル(init.rc)をtarfs上にコピーして持ってきたもの |
デバッグ対象のファイルサイズ | 12823bytes |
表1 デバッグ対象とするファイルシステムおよびファイルの情報 |
また、tarfsの物理ディスク上のデータ構成は図3のとおりで、「init.rc」のデータは8〜33ブロックに入っています。
次に、Linuxカーネルがこのファイルデータをメモリ上でどうやって管理しているのかを理解するために、以下の順番でLinuxの管理について解説します。
- Linuxのメモリ管理(ページフレームとpage)について
- ファイルデータのpageの管理について
- 物理オフセットを管理するbuffer_headについて
1.Linuxのメモリ管理(ページフレームとpage)について
メモリ管理を知るうえで、まず「ページフレーム」と「page(ページ)」について解説します。
今回使用するボードのCPUはARMであり、CPUがアクセスするメモリアドレスは「仮想メモリアドレス」になります。そして、このメモリ領域はすべて4kbytes単位で分割管理されており、各領域をページフレームと呼びます。また、各ページフレームの状態などを管理するために、Linuxでは32bytesのpage構造体を定義しています。
このpage構造体のデータは、「mem_map」というグローバル変数が指す領域に連続配置されており、各pageがページフレームと1対1の関係になっています(図4)。
今回のターゲットボードの場合、カーネルの仮想アドレスの開始位置は3Gbytes(0xc0000000)からでしたので、ページフレームの先頭アドレスも3Gbytesとなります。なお、図4の「no」はページフレームのインデックス番号であり、これを「ページフレーム番号」と呼びます。
Linuxのすべてのデータはページフレーム上に存在しますので、当然、ファイルのデータもこのページフレーム上に存在しています。
ここでpageのアドレスからページフレームのアドレスが求められることに気付かれた方、とても鋭いです。page構造体のメンバには、ページフレームを指すアドレスは存在しません。これは、pageとページフレームのメモリ配置がリニアにマッピング可能であり、以下の計算式でページフレームのアドレスを求めることができるからです。
- ページフレーム番号 = (mem_map − 対象pageのアドレス) / 32
- ページフレームのアドレス = 0xc0000000 + (ページフレーム番号 × 4kbytes)
2.ファイルデータのpageの管理について
前述のとおり、1つのページフレームには、最大で4kbytesまでデータを詰めることができます。今回のファイルの場合は、サイズが約13kbytesですので、ページフレームの個数としては4個必要となります(図5)。
page構造体には「index」というメンバがあり、ページフレームがファイルデータとして使用される場合は、ファイルオフセット順の値(論理オフセット)が設定されます。このため、例えばファイルオフセット位置が1kbyte目のデータを見たい場合は、論理オフセット「0」のpageが管理するページフレームのデータを参照すればよいわけです。
これらのpageが複数ある場合は、それらを論理オフセット順に管理する必要があります。単純な管理でよければ、線形リストでpageをリスト管理すればよいですが、これでは大量のpageが存在する場合、性能面でデメリットになり得ます。このため、Linuxでは「基数ツリー」というデータ構造でpageを管理しています。本稿では詳細は割愛しますが、今回のファイルの場合は図6のようなデータ構造となります。
まず、起点となるのは「inode」であり、ここからpage全体を管理するデータ(adress_space構造体)を「i_mapping」というメンバで参照できるようになっています。「adress_space」構造体には、基数ツリーと呼ばれるデータ(radix_tree_node構造体)が管理されており、page_tree(radix_tree_root構造体)の「rnode」メンバ(注6)を通してアクセスできます。
そして、radix_tree_nodeの「slots」というポインタ配列の中に、pageのアドレスが論理オフセット順に格納されています(注7)。
Copyright © ITmedia, Inc. All Rights Reserved.