ここまでの説明で、pageはメモリ上でファイルデータを管理する構造だということが分かりました。
しかし、これだけだと、例えばファイルデータを変更した場合、元あった物理ディスクに書き戻すことはできません。つまり、pageが管理するデータが物理ディスク上のどこにあるのかを管理するデータ構造が別に必要となります。そのデータ構造が「buffer_head(バッファーヘッド)」構造体です。ファイルデータとしての管理構造は図7のようになります。
まず、page構造体の「private」というメンバは、buffer_head構造体をポイントしています。一方、buffer_headにはブロック型デバイスディスクリプタへのポインタ(struct block_device * b_bdev)および、物理オフセット(sector_t b_blocknr)を管理するメンバがあります。また、buffer_headのI/O用バッファは、ページフレームへのアドレスを指すメンバ(char* b_data)で管理しています。これらの情報があれば、「どのデータ(b_data)」を、「どの物理ディスク(b_bdev)」の「どこ(b_blocknr)」にI/O発行するかが一意に決まるわけです。
buffer_headは、ファイルシステムのブロックサイズ単位で管理されており、tarfsの場合は、ブロックサイズが512bytesですので、1ページ当たり8個のbuffer_headが存在することになります。そのため、ページ内のbuffer_headは、「b_this_page」というメンバを使用して、オフセット順に単方向リスト管理がされています。
以上、Linuxカーネルのファイルデータ管理について一通り見てきました。
それではこの知識を“実践知”として生かすために、早速、組み込みボードをデバッグし、メモリダンプからファイルデータを復旧してみましょう!
ステップは、以下の4つです(図8)。
まずは、ターゲットボードを起動し、その後、ホストPC上で以下のようにgdbを立ち上げます(注8)。
$ prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/arm-eabi-gdb vmlinux
次に、以下の「gdb」コマンドでターゲットボードのkgdbとの通信路を確立します。
(gdb) target remote udp:<ip address>:<port>
すると、以下のようなメッセージが出力され、Linuxカーネルはgdbからのコマンド実行待ち状態となります。
kgdb_breakpoint () at kernel/kgdb.c:1739 1739 wmb(); /* Sync point before breakpoint */
そして、「c」コマンドを実行し、処理を継続させます。
(gdb) c
これで、kgdbとの通信路確立は終了です。後はtarfsをマウントして、init.rcのデータを参照し、メモリ上にキャッシュします。
メモリダンプを採取するには、gdbの「dump」コマンドを使用します。この際、メモリの開始位置と終端位置を指定する必要があります。そこで、今回のターゲットボードのメモリ量を調べるため、/proc/meminfoを参照して、システムのメモリ量を確認してみます。
/ # cat /proc/meminfo MemTotal: 90572 kB MemFree: 8692 kB Buffers: 40 kB Cached: 33508 kB SwapCached: 0 kB :
「MemTotal」から約90Mbytesがシステムのメモリとして使われていることが分かります。よって、dumpコマンドを以下のように実行します。
(gdb) dump memory kernel.dump 0xc0000000 0xc5a00000
kernel.dump ……ダンプ出力ファイル 0xc0000000 ……カーネルメモリ領域の開始アドレス 0xc5a00000 ……終端アドレスは開始アドレス+90Mbytes分を指定 |
Copyright © ITmedia, Inc. All Rights Reserved.