連載
» 2011年01月13日 00時00分 UPDATE

作りながら理解するファイルシステムの仕組み(10):Androidをデバッグしメモリダンプからデータを復旧する (1/3)

Android組み込みボードを使って、デバッグしながらファイルデータ管理の内容を探る! 併せて、“実践知”も紹介する

[森 崇 株式会社 永和システムマネジメント,@IT MONOist]

 今回は、「Linuxカーネルのファイルデータ管理」について解説していきます。

 ただ、単なる教科書的な説明だけではつまらないので、Android実験用に購入した組み込みボード(Android移植済み)を使い、実際のファイルデータ管理の内容をデバッグしながら見ていきたいと思います。使用するデバッグ手法としては、Linuxでおなじみの「kgdb」(注1)を利用することにします。

 そして、ここで説明するLinuxファイルデータ管理の理解をより“実践知”として生かせるように、「Linuxカーネルメモリからファイルデータを復旧する方法」を紹介します。

 具体的には、デバッグ中に「メモリダンプ」を採取し、そのダンプ情報からあるファイルのデータを復旧していきます。このやり方を習得しておくと、ログファイルなどのファイルデータをメモリダンプから参照できるようになるので、裏ワザとして覚えておくと大変役に立ちます(注2)!

※注1:kgdbは、Linuxカーネルをデバッグするための機能です。


※注2:筆者の経験では、システムパニック時に、あるデーモンプロセスのログ情報をどうしても見る必要があり、この方法でログデータを復旧して問題の原因を解明できたことがあります。


Android組み込みボード

 今回使用するAndroid組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」を図1に示します(注3)。

Android組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」 図1 Android組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」
Androidのバージョンは「2.1(Eclair)」です。ただし、デモ用なので、問題にぶつかったら自分で直していく必要があります(そこがだいご味でもあります)
※注3:4.3インチのタッチパネル、Samsung S3C6410XH-66、ARM1176JZF-Sコア(667MHz)、128MBytes Mobile DDR RAM、10/100Base-T Ethernet(DM9000)、USB 1.1ホスト×1、USB 2.0デバイス×1、RS-232×4、SDカードスロット×1。


組み込みボードのデバッグ方法

 この組み込みボード(ターゲットボード)のデバッグ方法ですが、選択肢として以下の2つがあります。

  1. JTAG(注4)を使ったデバッグ
  2. kgdbを使ったデバッグ
※注4:CPUのデバッグ専用のJTAGとJTAGエミュレータを接続することでデバッグする。


 今回は以下に示す理由により、kgdbを選択することにしました。

(1)導入費用が少なくて済む(クロスLANケーブル 500円程度)。
(2)ARM用のgdbは、Android開発環境のものを使える。
(3)kgdbが、gdbと通信を行っている間は割り込みが禁止されるので、割り込みタイミングが変わる可能性がある。しかし、ファイルシステムレベルのデバッグであれば割り込みのタイミングに依存することは少ないはず。
(4)うまくいかないときは、Linuxカーネルソースを見れば対処できる(それが楽しかったりもする)(注5)。

※注5:実際に、kgdbを導入しようとしてカーネルのコンフィグレーションを変更してみましたが、予想どおりこれだけでは動作せず、いろいろと調べながら動作させました。この辺のハマリどころと、解決方法はまた別の機会に紹介します。


 今回、ターゲットボードとホストPCは、図2のようにケーブル接続を行いデバッグします。

組み込みボードデバッグ時のホストPCとの配線 図2 組み込みボードデバッグ時のホストPCとの配線

 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ブロックに入っています。

tarfsのデータ構成 図3 tarfsのデータ構成

 次に、Linuxカーネルがこのファイルデータをメモリ上でどうやって管理しているのかを理解するために、以下の順番でLinuxの管理について解説します。

  1. Linuxのメモリ管理(ページフレームとpage)について
  2. ファイルデータのpageの管理について
  3. 物理オフセットを管理するbuffer_headについて

1.Linuxのメモリ管理(ページフレームとpage)について

 メモリ管理を知るうえで、まず「ページフレーム」と「page(ページ)」について解説します。

 今回使用するボードのCPUはARMであり、CPUがアクセスするメモリアドレスは「仮想メモリアドレス」になります。そして、このメモリ領域はすべて4kbytes単位で分割管理されており、各領域をページフレームと呼びます。また、各ページフレームの状態などを管理するために、Linuxでは32bytesのpage構造体を定義しています。

 このpage構造体のデータは、「mem_map」というグローバル変数が指す領域に連続配置されており、各pageがページフレームと1対1の関係になっています(図4)。

pageのメモリ配置 図4 pageのメモリ配置

 今回のターゲットボードの場合、カーネルの仮想アドレスの開始位置は3Gbytes(0xc0000000)からでしたので、ページフレームの先頭アドレスも3Gbytesとなります。なお、図4の「no」はページフレームのインデックス番号であり、これを「ページフレーム番号」と呼びます。

 Linuxのすべてのデータはページフレーム上に存在しますので、当然、ファイルのデータもこのページフレーム上に存在しています。

 ここでpageのアドレスからページフレームのアドレスが求められることに気付かれた方、とても鋭いです。page構造体のメンバには、ページフレームを指すアドレスは存在しません。これは、pageとページフレームのメモリ配置がリニアにマッピング可能であり、以下の計算式でページフレームのアドレスを求めることができるからです。

  • ページフレーム番号 = (mem_map − 対象pageのアドレス) / 32
  • ページフレームのアドレス = 0xc0000000 + (ページフレーム番号 × 4kbytes)

2.ファイルデータのpageの管理について

 前述のとおり、1つのページフレームには、最大で4kbytesまでデータを詰めることができます。今回のファイルの場合は、サイズが約13kbytesですので、ページフレームの個数としては4個必要となります(図5)。

pageによるファイルデータの管理 図5 pageによるファイルデータの管理

 page構造体には「index」というメンバがあり、ページフレームがファイルデータとして使用される場合は、ファイルオフセット順の値(論理オフセット)が設定されます。このため、例えばファイルオフセット位置が1kbyte目のデータを見たい場合は、論理オフセット「0」のpageが管理するページフレームのデータを参照すればよいわけです。

 これらのpageが複数ある場合は、それらを論理オフセット順に管理する必要があります。単純な管理でよければ、線形リストでpageをリスト管理すればよいですが、これでは大量のpageが存在する場合、性能面でデメリットになり得ます。このため、Linuxでは「基数ツリー」というデータ構造でpageを管理しています。本稿では詳細は割愛しますが、今回のファイルの場合は図6のようなデータ構造となります。

ファイルのpage管理 図6 ファイルのpage管理

 まず、起点となるのは「inode」であり、ここからpage全体を管理するデータ(adress_space構造体)を「i_mapping」というメンバで参照できるようになっています。「adress_space」構造体には、基数ツリーと呼ばれるデータ(radix_tree_node構造体)が管理されており、page_tree(radix_tree_root構造体)の「rnode」メンバ(注6)を通してアクセスできます。

※注6:なお、pageの個数が1個の場合は特殊なケースであり、radix_tree_nodeが使われることはありません。その代わり、rnodeには対象pageのアドレスが設定されます。


 そして、radix_tree_nodeの「slots」というポインタ配列の中に、pageのアドレスが論理オフセット順に格納されています(注7)。

※注7:pageが大量に存在する場合は、ポインタ配列にはradix_tree_nodeが入っている場合があります。


       1|2|3 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.