連載
» 2011年03月10日 14時28分 UPDATE

作りながら理解するファイルシステムの仕組み(11):tarファイルシステムをAndroidに組み込む!! (1/2)

最終回では、前回使用したAndroid搭載ボードにオリジナルのファイルシステム「tarfs」を組み込む方法を紹介する!

[森 崇、宮下 尚邦、上田 眞司、野村 昌平(株式会社 永和システムマネジメント),@IT MONOist]

 昨今、「Android」をはじめとするスマートフォンなどの組み込み機器では、当たり前のようにファイルシステムが存在します。

 連載第1回「ないと困るファイルシステムのありがたみ」でも説明したとおり、多様化するユーザーニーズに応えるために、組み込み機器で取り扱うデータ量は年々増加傾向にあり、ファイルシステムを採用するメリットが高くなってきています。

 最終回となる今回は、Android(前回の組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」)にオリジナルのファイルシステムである「tarファイルシステム(tarfs)」を組み込む方法を紹介します。そして、最後に動作確認(デモ)として、組み込みボード上で動画ファイル(注1)の入ったtarファイルをマウントし、その動画ファイルを再生してみます。

※注1:今回は、筆者が過去にYouTubeに投稿したAndroid移植の動画ファイル(以下2つ)をtarアーカイブにします。


Androidにファイルシステムを組み込むには?

 ご存じのとおり、AndroidのベースとなるカーネルはLinuxです。従って、オリジナルのファイルシステムの組み込み方はLinuxでのやり方と同様です。

 Linuxでは、ファイルシステムの処理を「仮想ファイルシステム」という構造で共通化しておりますので、オリジナルのファイルシステムを追加するのはそれほど難しくはありません。また、Linuxが用意している汎用関数を積極的に利用することで、コード量を削減できるだけなく、その後のカーネルベースの版数アップにも追随しやすくなります。

 図1は、Linuxのファイルシステムスタック構造です。Linuxにオリジナルファイルシステムを組み込むためには、

  1. LinuxのVFS層が提供している関数インターフェイス(図1の緑の処理部分)の実装
  2. 出来上がったモジュールをファイルシステムとして登録

の2つの作業が必要となります。

ファイルシステムスタック構造 図1 ファイルシステムスタック構造

 tarfsの実装に当たり、共通処理として「論物変換処理」と「ディスクI/O処理」があります。これらはファイルシステムにとって重要な処理ではありますが、内容が多少複雑であり、その理解だけで道に迷う可能性もありますので、本ページ下部に補足情報(補足1)(補足2)として掲載しました(ご興味のある方はそちらも併せてご覧ください)。

関数インターフェイスの登録

 図1で示したように、LinuxのVFS層が提供している関数インターフェイスの種類には以下のものがあります。これらのインターフェイスを実装すれば、ファイルシステムとして機能してくれます。

  1. inodeオペレーション
  2. fileオペレーション
  3. address_spaceオペレーション
  4. super_blockオペレーション
  5. file_system_typeオペレーション

 ファイルシステムの登録対象関数は多数ありますが、特殊なファイルシステムでなければ、連載第9回「動画・写真などのデータを仮想化する仕組みとは?」で説明したとおり、Linuxがデフォルトで用意している関数を使用することができます。

 今回作成するtafrsは、一般的な読み込み専用のファイルシステムと同じ機能を提供するものであり、特殊な機能はありません。このため、大半の関数はLinuxの汎用関数を利用することができ、オリジナル関数の登録は非常に少なくて済みます。

 それでは、実際の登録状況を順番に見ていきましょう。

1.inodeオペレーション
 inodeオペレーションでは、通常ファイルの場合、ファイルのサイズやパーミッション操作などを登録します。ディレクトリの場合は、ファイルの作成・削除・検索などを登録します。シンボリックリンクの場合は、リンク先パス名の操作関数を登録します。

 以下の表1では、inode関数テーブルのエントリと、tarfsで実際に登録した関数を示しています。しかし、通常ファイルについては、tarfsが読み込み専用ファイルシステムという理由から1つも登録する必要がありませんでした。ディレクトリについては、ファイル検索用の関数(tarfs_lookup)1つだけです(注2)。シンボリックリンクは、リンク先パス操作で3つの関数を登録しましたが、そのうち1つの関数(generic_readlink)は、Linuxで用意されているものを利用しています。

inode 関数テーブル 表1 inode 関数テーブル(注3)
※注2:ファイル検索の内容については、連載第8回「ファイル名を管理するキャッシュdentry」をご覧ください。


※注3:Linux汎用関数を利用している場合は、黒の太字、独自実装は赤の太字で表記している(以降の表も同様)。


2.fileのオペレーション
 fileオペレーションでは、通常ファイルの場合はファイルの読み書き操作、ディレクトリの場合はディレクトリエントリ操作を登録する必要があります。なお、シンボリックリンクファイルについては、こういった操作は行いませんので登録は不要です。表2に示すとおり、通常ファイルについては、Linuxで用意されているものを登録するだけで済みました。ディレクトリについては、オリジナルのものは「tarfs_readdir」だけです。

file関数テーブル 表2 file関数テーブル

3.address_space
 address_spaceオペレーションでは、pageを使ったファイルデータの読み書き操作を登録する必要があります。ディレクトリ、シンボリックリンクファイルについては、こういった操作は行いませんので登録は不要です。表3に示すとおり、tarfsが読み込み専用のファイルシステムであることから、「tarfs_readpage」だけの登録で終わりです。

address_space関数テーブル 表3 address_space関数テーブル

4.super_blockのオペレーション
 super_blockオペレーションでは、ファイルシステム操作処理を登録します(表4)。

super_operationsの関数テーブル 表4 super_operationsの関数テーブル

5.file_system_type のオペレーション
 file_system_typeオペレーションでは、ファイルシステムのマウントとアンマウント処理関数を登録します(表5)。

file_system_typeの関数テーブル 表5 file_system_typeの関数テーブル

ファイルシステムの登録

 ここまでくれば、後はtarfsのモジュールをファイルシステムとして登録すればいいだけです! 具体的には、Linuxが用意している登録関数「register_filesystem()」の引数にtarfsのfile_system_typeオブジェクト(tar_fs_type)を渡すだけです(注4)。以下に、実際に作成したコードを示します(図2)。

ファイルシステム登録の実装例 図2 ファイルシステム登録の実装例
※注4:「init_tarfs()」は__initで宣言していますが、これはファイルシステム登録完了後にメモリから解放することを意味しています。


 ちなみに、tarfsのモジュールを削除する場合は、「unregister_filesystem()」の引数にtarfsのfile_system_typeを渡します(図3)(注5)。

ファイルシステム登録の解除例 図3 ファイルシステム登録の解除例

 最後に、これらの関数をモジュールとして登録・解除が行われるように、以下の“おまじない”をします。

ay_fsys11_cod01.gif

 「module_init()」の引数に指定する関数は、モジュールロード時に実行される初期化関数(init_tarfs)です。一方、「module_exit()」の引数に指定する関数は、モジュールアンロード時に実行される終了関数(exit_tarfs)となります。なお、tarfsはGPLライセンスとしていますので、MODULE_LISENSE()マクロの引数には“GPL”を指定しています。

※注5:「exit_tarfs()」は、__exitで宣言していますが、これはファイルシステム登録解除のときだけメモリにロードされることを意味しています。




補足1:論物変換処理の実装

 ファイルシステムの重要な役割の1つとして、ユーザーのデータ配置状況をメタデータとして管理するという役割があります(注6)。そして、ファイルシステムは「ユーザーが意識するファイルのオフセット(論理オフセット)」と「実際にデータが配置されているオフセット(物理オフセット)」を管理する必要があります。

 ユーザーのファイルシステムに対する要求は「論理オフセット」という形できますから、ファイルシステムとしてはそれを「物理オフセット」に変換して、ディスク上のどこに対象となるデータがあるのかを探す必要があるわけです(注7)。このような変換を「論物変換処理」と呼びます。

 ここで、この変換処理にバグがあると、まったく関係のないデータを読み込んでしまいデータが正しく見えないという問題が起きてしまったり、メモリ破壊を引き起こし、システム停止が発生してしまったりしますので、慎重に設計・実装を進めなければなりません。

 tarfsには、論物変換対象となる論理データが3つ(以下)ありますが、バグの作り込みが低くなるように、いくつか工夫を施しています。

  1. ファイルオフセット
  2. ディレクトリオフセット
  3. inode番号

 上記3つの論理データを管理しているtarfsのメタデータは表6に示すとおり、すべて「ディスクinode」としています。

論理データ 論物変換管理データ
ファイルオフセット 対象ファイルの「ディスクinode」(注8)
ディレクトリオフセット 対象ファイルの「ディスクinode」
inode番号 ディスクinode管理用特殊「ディスクinode」
表6 tarfsの論物変換対象データ

 つまり、論物変換の対象(ファイルオフセット、ディレクトリオフセット、inode番号)はそれぞれ違いますが、tarfsではディスクinodeという共通データが管理していますので、論物変換用の処理は統一することができ、実装量を少なくできるわけです。加えて、論物変換処理自体にLinuxカーネル依存はなく、ロジック検証はユーザー空間で手軽に行うことできるので、バグが入り込む余地はグッと低くなります。

※注6:連載第2回「素晴らしきファイルシステムのデータ管理」をご覧ください。


※注7:連載第4回「tarファイルに魔法をかけてみよう! その1」をご覧ください。


※注8:Linuxカーネルのinodeと区別するため、メタデータのinodeは「ディスクinode」と呼ぶことにします。


補足2:ディスクI/O処理

 tarfsのデータは物理ディスク上にあるので、Linuxの仕組みとして“ファイルシステムのデータをどうやって読み込むのか”を調べなければなりません。

 当然ながら、LinuxにはディスクI/Oを実行する汎用処理がいろいろと用意されていますが、使いやすそうなものを選定する必要があります。そうはいっても、やみくもに調べるのも大変なわけです……。こういうときは、何でもそうですが、お隣さんがどうやっているかを拝見するのが一番の近道です。

 そんなわけで、Linux標準のファイルシステムである「ext3」をちょっとのぞいてみると(fs/ext3/super.cの「ext3_fill_super()」)、スーパーブロックデータのディスク読み込み処理では「sb_bread()」を使っているようです。この関数の仕様は以下のとおりです。

struct buffer_head * sb_bread(struct super_block *sb, sector_t block)
sb 対象ファイルシステムのsuper_block構造体
block 読み込みオフセット位置(ファイルシステムのブロックサイズ単位)
戻り値 対象ファイルシステムが使用する物理ディスクからデータを読み込み、その結果がbuffer_head構造体として返される

 これなら使いやすそうですし、ext3やほかのファイルシステムでも使用されているので、tarfsでもこの関数を使用することにしました。



       1|2 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.