最終回では、前回使用したAndroid搭載ボードにオリジナルのファイルシステム「tarfs」を組み込む方法を紹介する!
昨今、「Android」をはじめとするスマートフォンなどの組み込み機器では、当たり前のようにファイルシステムが存在します。
連載第1回「ないと困るファイルシステムのありがたみ」でも説明したとおり、多様化するユーザーニーズに応えるために、組み込み機器で取り扱うデータ量は年々増加傾向にあり、ファイルシステムを採用するメリットが高くなってきています。
最終回となる今回は、Android(前回の組み込みボード「マルチ・メディア ARM11ボード Idea6410+LCD4.3」)にオリジナルのファイルシステムである「tarファイルシステム(tarfs)」を組み込む方法を紹介します。そして、最後に動作確認(デモ)として、組み込みボード上で動画ファイル(注1)の入ったtarファイルをマウントし、その動画ファイルを再生してみます。
ご存じのとおり、AndroidのベースとなるカーネルはLinuxです。従って、オリジナルのファイルシステムの組み込み方はLinuxでのやり方と同様です。
Linuxでは、ファイルシステムの処理を「仮想ファイルシステム」という構造で共通化しておりますので、オリジナルのファイルシステムを追加するのはそれほど難しくはありません。また、Linuxが用意している汎用関数を積極的に利用することで、コード量を削減できるだけなく、その後のカーネルベースの版数アップにも追随しやすくなります。
図1は、Linuxのファイルシステムスタック構造です。Linuxにオリジナルファイルシステムを組み込むためには、
の2つの作業が必要となります。
tarfsの実装に当たり、共通処理として「論物変換処理」と「ディスクI/O処理」があります。これらはファイルシステムにとって重要な処理ではありますが、内容が多少複雑であり、その理解だけで道に迷う可能性もありますので、本ページ下部に補足情報(補足1)(補足2)として掲載しました(ご興味のある方はそちらも併せてご覧ください)。
図1で示したように、LinuxのVFS層が提供している関数インターフェイスの種類には以下のものがあります。これらのインターフェイスを実装すれば、ファイルシステムとして機能してくれます。
ファイルシステムの登録対象関数は多数ありますが、特殊なファイルシステムでなければ、連載第9回「動画・写真などのデータを仮想化する仕組みとは?」で説明したとおり、Linuxがデフォルトで用意している関数を使用することができます。
今回作成するtafrsは、一般的な読み込み専用のファイルシステムと同じ機能を提供するものであり、特殊な機能はありません。このため、大半の関数はLinuxの汎用関数を利用することができ、オリジナル関数の登録は非常に少なくて済みます。
それでは、実際の登録状況を順番に見ていきましょう。
1.inodeオペレーション
inodeオペレーションでは、通常ファイルの場合、ファイルのサイズやパーミッション操作などを登録します。ディレクトリの場合は、ファイルの作成・削除・検索などを登録します。シンボリックリンクの場合は、リンク先パス名の操作関数を登録します。
以下の表1では、inode関数テーブルのエントリと、tarfsで実際に登録した関数を示しています。しかし、通常ファイルについては、tarfsが読み込み専用ファイルシステムという理由から1つも登録する必要がありませんでした。ディレクトリについては、ファイル検索用の関数(tarfs_lookup)1つだけです(注2)。シンボリックリンクは、リンク先パス操作で3つの関数を登録しましたが、そのうち1つの関数(generic_readlink)は、Linuxで用意されているものを利用しています。
2.fileのオペレーション
fileオペレーションでは、通常ファイルの場合はファイルの読み書き操作、ディレクトリの場合はディレクトリエントリ操作を登録する必要があります。なお、シンボリックリンクファイルについては、こういった操作は行いませんので登録は不要です。表2に示すとおり、通常ファイルについては、Linuxで用意されているものを登録するだけで済みました。ディレクトリについては、オリジナルのものは「tarfs_readdir」だけです。
3.address_space
address_spaceオペレーションでは、pageを使ったファイルデータの読み書き操作を登録する必要があります。ディレクトリ、シンボリックリンクファイルについては、こういった操作は行いませんので登録は不要です。表3に示すとおり、tarfsが読み込み専用のファイルシステムであることから、「tarfs_readpage」だけの登録で終わりです。
4.super_blockのオペレーション
super_blockオペレーションでは、ファイルシステム操作処理を登録します(表4)。
5.file_system_type のオペレーション
file_system_typeオペレーションでは、ファイルシステムのマウントとアンマウント処理関数を登録します(表5)。
ここまでくれば、後はtarfsのモジュールをファイルシステムとして登録すればいいだけです! 具体的には、Linuxが用意している登録関数「register_filesystem()」の引数にtarfsのfile_system_typeオブジェクト(tar_fs_type)を渡すだけです(注4)。以下に、実際に作成したコードを示します(図2)。
ちなみに、tarfsのモジュールを削除する場合は、「unregister_filesystem()」の引数にtarfsのfile_system_typeを渡します(図3)(注5)。
最後に、これらの関数をモジュールとして登録・解除が行われるように、以下の“おまじない”をします。
「module_init()」の引数に指定する関数は、モジュールロード時に実行される初期化関数(init_tarfs)です。一方、「module_exit()」の引数に指定する関数は、モジュールアンロード時に実行される終了関数(exit_tarfs)となります。なお、tarfsはGPLライセンスとしていますので、MODULE_LISENSE()マクロの引数には“GPL”を指定しています。
ファイルシステムの重要な役割の1つとして、ユーザーのデータ配置状況をメタデータとして管理するという役割があります(注6)。そして、ファイルシステムは「ユーザーが意識するファイルのオフセット(論理オフセット)」と「実際にデータが配置されているオフセット(物理オフセット)」を管理する必要があります。
ユーザーのファイルシステムに対する要求は「論理オフセット」という形できますから、ファイルシステムとしてはそれを「物理オフセット」に変換して、ディスク上のどこに対象となるデータがあるのかを探す必要があるわけです(注7)。このような変換を「論物変換処理」と呼びます。
ここで、この変換処理にバグがあると、まったく関係のないデータを読み込んでしまいデータが正しく見えないという問題が起きてしまったり、メモリ破壊を引き起こし、システム停止が発生してしまったりしますので、慎重に設計・実装を進めなければなりません。
tarfsには、論物変換対象となる論理データが3つ(以下)ありますが、バグの作り込みが低くなるように、いくつか工夫を施しています。
上記3つの論理データを管理しているtarfsのメタデータは表6に示すとおり、すべて「ディスクinode」としています。
論理データ | 論物変換管理データ |
---|---|
ファイルオフセット | 対象ファイルの「ディスクinode」(注8) |
ディレクトリオフセット | 対象ファイルの「ディスクinode」 |
inode番号 | ディスクinode管理用特殊「ディスクinode」 |
表6 tarfsの論物変換対象データ |
つまり、論物変換の対象(ファイルオフセット、ディレクトリオフセット、inode番号)はそれぞれ違いますが、tarfsではディスクinodeという共通データが管理していますので、論物変換用の処理は統一することができ、実装量を少なくできるわけです。加えて、論物変換処理自体にLinuxカーネル依存はなく、ロジック検証はユーザー空間で手軽に行うことできるので、バグが入り込む余地はグッと低くなります。
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でもこの関数を使用することにしました。
Copyright © ITmedia, Inc. All Rights Reserved.