JFSを30日間試してみた

 JFS(Journaled File System)は1999年にIBMがオープンソース化し2002年にLinuxカーネルに統合された、あまり知られていないファイルシステムだ。JFSはもともとはAIXを搭載したUnixサーバ上の標準ファイルシステムとしてIBMによって開発され、その後OS/2にも移植された。そのような立派な血統にも関わらずJFSはあまり知られておらず、またext2/ext3やReiserFSなどの他のLinux用のファイルシステムのように広くは使用されていない。今回JFSについてより詳しく知るために、ルートファイルシステムとしてJFSをインストールしてみた。その結果、JFSは他の有名なファイルシステムの代替物として遜色のないファイルシステムだということが分かった。

 JFSを試してみるため、ノートPC上にSlackware 12をインストールして、インストール時にファイルシステムとしてJFSを選択した。パーティション分割に関して特別なことは何もせず、すべてを含む一つのJFSファイルシステムを作成した。インストールは順調に終わり、システムはGRUBから問題なくブートした。JFSはすべてのディストリビューションでインストールオプションとして提供されているわけではなく、また、デフォルトのカーネルがJFSを含めずにコンパイルされているディストリビューションもあるだろう。FedoraとSUSEでは、JFSを使用することができるようになっているものの、どちらもデフォルトのファイルシステムはext3になっている。JFSを試してみたい人には、Slackware、Debian、Ubuntuとそれらの派生ディストリビューションが良い選択肢だろう。

 JFSを初めて使ってみて最初に気付いた点は、二流のファイルシステムには付き物のlost+foundディレクトリがないということだ。

 JFSは完全に64ビットのファイルシステムだ。デフォルトのブロックサイズは4KBで、ファイルシステムのサイズとして最大4ペタバイト(ただしブロックサイズが4KBより小さい場合は上限も小さくなる)をサポートしている。サポートしているファイルシステムの最小サイズは16MBだ。JFSのトランザクションログのサイズは、デフォルトでは集合体のサイズの0.4%(メガバイト単位に切り上げ)で、最大32MBとなっている。ディスクのレイアウトの興味深い点として、fsckの作業領域があった。これはファイルシステム内に割り当てられている小さな領域で、ブート時に大規模なファイルシステムを追跡するだけの十分なRAMがない場合に、ブロックの割り当てを記録しておくためのものだ。

 JFSはiノードに対して動的にディスク領域を割り当て、必要がなくなればその領域を解放する。そのため小さなファイルが数多くある場合にも、iノードの数が不足することはない。すでにカーネル内に統合済のファイルシステムのうち、この機能を持つファイルシステムは、私が知る限りはJFSだけだ。また性能と効率を上げるために、小さなディレクトリの内容はディレクトリのiノードの中に保存されている。自分自身(.)と親ディレクトリ(..)以外に8個までのエントリがiノード内にインラインで保存される。それよりも大きなディレクトリは、高速に検索できるようにするため、名前をキーとしたB+ツリーを使用している。内部的にはJFSは、ファイルにブロックを割り当てるのにエクステントを使用しているので、ファイルのサイズが大きくなっても領域を効率良く使用することができる。なおエクステントはXFSでも使用されており、またext4の主な新機能の一つでもある。

 JFSは、スパースファイルとデンスファイルの両方をポートしている。スパースファイルでは、途中のファイルブロックにデータを書き込むことなしに、データをファイル内の任意の位置に書き込むことができる。JFSは、使用されている可能性のある最大ブロック数をファイルのサイズとして報告するが、割り当ては実際に使用するブロックに対してのみ行う。スパースファイルは、大規模な論理領域を必要とするがその領域を部分的にしか利用しないようなアプリケーションに向いている。一方デンスファイルでは、データが書き込まれていても書き込まれていなくても、ファイルのサイズの分だけブロックが割り当てられる。

 JFSでは通常のパーミッションに加えて、不変属性(i)や追加専用属性(a)などの基本的な拡張属性をサポートしている。lsattrプログラムとchattrプログラムで試してみたところ、問題なく設定/確認することができた。なおLinuxでのJFSのACL(Access Control List)についてはあまり情報を見つけることができなかった。

ロギング

 JFSの主な設計目標は、大規模なファイルシステムのために、古いUnixのファイルシステムにあるような長時間のfsck(ファイルシステムチェック)をなくして高速なクラッシュ復旧を提供することだった。このことはext3やReiserFSなどのファイルシステムにおいても同様にメインの目標だった。しかしext3とは違ってJFSではジャーナリングは後からの付け足しではなく、設計当初から設計の中に組み込まれていた。JFSではアプリケーションの性能を高めるために、ファイルシステムの最初の作成時に指定することでトランザクションログファイルを外部ボリューム上に作成することができる。

 JFSはファイルシステム構造の一貫性を維持するためにメタデータの変更を伴う操作をログに記録するが、ファイルの中身はログに記録されないためファイルの中身の整合性は必ずしも維持されない。そのためクラッシュが起こった場合データは古い状態になる可能性があるが、ファイルシステムの一貫性は保たれていて使用可能な状態であるはずだ。

 以下に、JFSがログに記録するファイルシステム操作を示す。

  • ファイル作成(create)
  • リンク(link)
  • ディレクトリ作成(mkdir)
  • ノード作成(mknod)
  • リンク削除(unlink)
  • 名前変更(rename)
  • ディレクトリ削除(rmdir)
  • シンボリックリンク(symlink)
  • 通常ファイルのトランケート

ユーティリティ

 JFSには、ファイルシステム管理用ユーティリティが一式用意されている。これらのユーティリティを使用するためにはルートユーザになる必要がある。

ユーティリティ 説明
jfs_debugfs シェルベースのJFSファイルシステムエディタ。ACL、uid/gid、モード、時刻などを変更することができる。ファイルの中身を変更することもできるが、その場合は16進数で入力しなければならない――ファイルの編集に使うにはあまり効率良くはないだろう。
jfs_fsck JFSのトランザクションログを再生して、JFSファイルシステムのチェック/修復を行なう。マウントされていない状態のファイルシステムか、読み取りのみ可のファイルシステムに対してのみ実行されるべき操作。ブート時に自動的に実行される。
jfs_fscklog JFS fsckサービスのログをファイルに書き出す。「jfs_fscklog -e /dev/hda6」を実行すれば、バイナリのログをfscklog.newという名前のファイルに出力することができる。「jfs_fscklog -d fscklog.new」を実行してファイルを閲覧することができる。
jfs_logdump ログファイル内の各トランザクションのデータを示すプレーンテキストのファイルとして、ジャーナルログを出力する。
jfs_mkfs パーティションをJFS形式でフォーマットする。「-j journal_device」というオプションを使用すれば、外部ジャーナルを作成することができる(1.0.18以降)。
jfs_tune JFSの調整可能なパラメータを調整する。性能を向上させることができそうなオプションは私の場合は見つからなかった。「-l」オプションを使用すればスーパーブロック情報を見ることができる。

 スーパーブロック情報がどのように表示されるのかを以下に示す。

root@slackt41:~# jfs_tune -l /dev/hda6
jfs_tune version 1.1.11, 05-Jun-2006

JFS filesystem superblock:

JFS magic number:       'JFS1'
JFS version:            1
JFS state:              mounted
JFS flags:              JFS_LINUX  JFS_COMMIT  JFS_GROUPCOMMIT  JFS_INLINELOG
Aggregate block size:   4096 bytes
Aggregate size:         12239720 blocks
Physical block size:    512 bytes
Allocation group size:  16384 aggregate blocks
Log device number:      0x306
Filesystem creation:    Wed Jul 11 01:52:42 2007
Volume label:           ''

復旧テスト

 ホワイトペーパやマニュアルページだけでは分からない厳しい現実がサーバ室にはあるものだ。そこでJFSの復旧能力を試すために、負荷をかけた状態で(強制電源オフにより)システムをクラッシュさせてみた。なお妥当な結果を得るために、クラッシュはそれぞれ2回ずつ行なった。

クラッシュ時の負荷 復旧
ファイルを1個開いたテキストエディタを実行中のコンソール(Xは起動せず) ジャーナルログの再生に約2秒。エディタ上で保存しておかなかった変更は失われたが、ファイルは損なわれていなかった。
Xウィンドウシステム上のKDE、それぞれファイルを開いた状態のGIMP、Nvu、xterm上のテキストエディタ ジャーナルログの再生に約2秒。どのファイルも完全な状態だったが、保存していなかった変更は失われた。
Xウィンドウシステム上のKDE、それぞれファイルを開いた状態のGIMP、Nvu、xterm上のテキストエディタ、MySQLのテーブル(ISAM)にレコードを挿入するシェルスクリプト。スクリプトは自作のもので、無限ループ。レコードがディスクにフラッシュされ始めるまで2、3分間実行した。 ジャーナルログの再生に約3秒。開いておいたどのファイルも完全な状態だった。2000~3000個のレコードが挿入された状態のデータベースも損なわれていなかった。ただしテーブルファイルのタイムスタンプが1分前のものになっていた。

 上記のすべてのケースで、以下のようなブートメッセージが表示された。

**Phase 0 - Replay Journal Log
-|----- (spinner appeared for a couple of seconds, then went away)
Filesystem is clean

 クラッシュを何回も行なったが、ファイルシステムの崩壊は一回も起こらなかった。またログの再生にかかった最も長い時間は約3秒だった。

まとめ

 上記の即興の復旧テストは、負荷の高いサーバのシミュレーションとしては不十分ではあるが、JFSは問題なくテストに合格し、復旧も高速だった。tarやrsyncなどのファイルアクセスの激しいアプリケーションも試してみたが、どのアプリケーションもまったく問題なかった。またTruecryptなどのより下層を取り扱うプログラムも試したが、期待通りに動作した。

 30日間あれこれやってみた結果、私はJFSを高く信頼するに至り、安心して自分のデータを任せている。JFSはこれまで他の選択肢ほど上手に宣伝されてこなかったかもしれないが、数多くの高品質なLinux用ファイルシステムの中においても堅実な選択肢の一つだと言えるだろう。

Linux.com 原文