JFSのジャーナリングは、ext3およびReiserFSと同様にメタ・データを保存する。
JFSを通したディスク操作は、「ファイル生成」「リンク」「ディレクトリ作成」「データ削除」など、複数のトランザクションに分割して処理される。これらの操作は、ディスクとログの同期も含めてジャーナルログに記録される。
ジャーナルログはまずメモリに記録され、JFSのフラッシュデーモンによってディスクへ書き込まれる。JFSのディスク復旧は、ジャーナルログを読み込んでディスク操作を再生(redo)することで行われる(redo操作はjfs.fsckコマンドに含まれている)。redoは、実際に行われた操作を時系列に沿って再実行する。ジャーナルログには、実際に行われたディスクとジャーナルログの同期情報も記録されるため、障害復旧時は同期後のログデータのみを再生することで無駄な処理が発生しないようになっている。
ジャーナルログはファイルシステムの作成時(mkfs.jfs)に、一緒に作成される。デフォルトでは、ファイルシステムの総和の0.4%、最大サイズは32Mbytesとなっている。
障害が発生すると、JFSはジャーナルログを参照しながら最終コミット以降の復旧を試みる。具体的には、以下の順番で処理が行われる。
- スーパーブロックチェック
ジャーナルログを開いて最初にスーパーブロックを読み込み、バージョン情報や状態フィールド、マジックナンバーなどのチェックを行う。このとき、状態(state)が「LOGREDONE」(すでにジャーナルログの再生が終了している)になっている場合は、単にスーパーブロックの更新を行って終了する。それ以外の場合は以下の作業を続行する。
- ジャーナルログレコードの再生開始ポイントの検出
ジャーナルログの最後尾を探し出し、トランザクションの処理内容を調査する。
ディスクと同期を行う同期点(sync point)は、ジャーナルログレコードに「SYNCPT」と記録される。ジャーナルログにSYNCPTが記録されていれば、その時点でディスクと同期したことが分かる。
ジャーナリングシステムの障害回復という観点からいえば、このSYNCPTより前の情報はディスクとログの同期が保証されるため、復旧操作は必要ない。実際に復旧が必要なのは、まだディスクと同期が行われていないSYNCPT以降の操作である。故に、JFSはこのSYNCPT以降のトランザクションの操作を調査するために、ジャーナルログレコードの処理手順を再生(replay)する。
- ジャーナルログレコードの再生
ジャーナルログレコードの再生開始ポイントを検出したら、時系列に沿って処理を再生する。
- トランザクションのチェック
ジャーナルログを再生した後、各トランザクション処理が時系列的に矛盾のないことをチェックし、バッファをディスクにフラッシュする。
- 終了処理
iノードの割り当てマップおよびスーパーブロックを更新する。
JFSが行うジャーナリングのロギングは、jfs_logdumpコマンドで取得したジャーナルログのダンプメッセージで確認できる。
以下の例は、/dev/hda3をJFSのパーティションとしたときのジャーナルログの処理内容を示している。
# jfs_logdump /dev/hda3
jfs_logdump version 1.1.3、 05-Sep-2003
Device Name: /dev/hda3
JFS_LOGDUMP: The current JFS log has been dumped into ./jfslog.dmp |
jfs_logdumpによって生成されたjfslog.dmpは、以下のようになる。
JOURNAL SUPERBLOCK:
------------------------------------------------------
magic number: x 87654321
version : x 1
serial : x 9
size : t 7168 pages (4096 bytes/page)
bsize : t 4096 bytes/block
l2bsize : t 12
flag : x 10200900
state : x 1
end : x 1a1c7b0
======================================================
**WARNING** jfs_logdump and log file /dev/hda3 state is LOGREDONE
======================================================
logrec d 0 Logaddr= x 1bfff78 Nextaddr= x 1bfff54 Backchain
= x 1bfff54
LOG_COMMIT (type = d 32768) logtid = d 5781
data length = d 0
---------------------------------------------------------------------
logrec d 1 Logaddr= x 1bfff54 Nextaddr= x 1bffeac Backchain
= x 0
LOG_REDOPAGE (type = d 2048) logtid = d 5781
data length = d 132 fileset = d 1 inode = d 16 (x 10)
type = d 1 REDOPAGE:INODE
l2linesize = d 7 pxd length = d 1 phys offset = x 12035e
(d 1180510)
0x8083f20 3FF33E40 10000000 70000400 504D0100 ?.>@....p...PM..
0x8083f30 04000000 5C031200 ACA82400 00000000 ....\.....$.....
0x8083f40 4B020000 00000000 01000000 00000000 K...............
(中略)
---------------------------------------------------------------------
logrec d 2 Logaddr= x 1bffeac Nextaddr= x 1bffe88 Backchain
= x
1bffe88
LOG_COMMIT (type = d 32768) logtid = d 5780
data length = d 0
(以下略) |
ジャーナルログには、
- LOG_COMMIT
- LOG_MOUNT
- LOG_SYNCPT
- LOG_REDOPAGE
- LOG_NOREDOPAGE
- LOG_NREDOINOEXT
- LOG_UPDATEMP
という7種類の情報が記録される。特に、LOG_REDOPAGEレコードにはiノードの確保/解放など、iノードマップを更新するためのメタ・データ情報が含まれている。ジャーナルログの中で、ディスク操作を再生するための最も重要な部分といえる。
また、上記のジャーナルログでも確認できるように、LOG_COMMITとLOG_REDOPAGEは対になっている。さらに、「logtid」(赤字部分)は、1組ごとに等しいIDが割り振られていることが分かる。つまり、LOG_COMMITとLOG_REDOPAGEが1つのアトミックトランザクションとして処理されているということである。
LOG_SYNCPT(同期地点)を以下に赤字で示す。このLOG_SYNCPTは、トランザクションID(logtid = d 4603)が等しい2つの操作LOG_COMMITとLOG_REDOPAGEの間で実行されている。つまり、アトミックトランザクションの間に同期が行われている。
logrec d 2550 Logaddr= x 1b9c9a8 Nextaddr= x 1b9c984 Backchain
= x 1b9c984
LOG_COMMIT (type = d 32768) logtid = d 4603
data length = d 0
----------------------------------------------------------------------
logrec d 2551 Logaddr= x 1b9c984 Nextaddr= x 1b9c960 Backchain
= x 0
****************************************************************
LOG_SYNCPT (type = d 16384) logtid = d 0
data length = d 0
sync = x 1b82f78
****************************************************************
----------------------------------------------------------------------
logrec d 2552 Logaddr= x 1b9c960 Nextaddr= x 1b9c8b8 Backchain
= x 0
LOG_REDOPAGE (type = d 2048) logtid = d 4603
data length = d 132 fileset = d 1 inode = d 16 (x 10)
type = d 1 REDOPAGE:INODE
l2linesize = d 7 pxd length = d 1 phys offset = x 174990
(d 1526160)
0x8083f20 3FF33E40 10000000 43D70500 60180100 ?.>@....C...`...
0x8083f30 04000000 90491700 B2030000 00000000 .....I..........
0x8083f40 01000000 00000000 01000000 00000000 ................ |
LOG_SYNCPT以前のデータの整合性は保証されるため、ジャーナリング処理では「logrec d 2552」のLOG_REDOPAGEは再生せず、LOG_COMMIT以降を再生する。ここまでの様子を図5に示す。
図5 トランザクションと障害発生
時系列にコミットが行われ、それがジャーナルログに記録されているシステムにおいて、時刻Tに何らかの障害が発生した場合を考えてみよう。JFSは、記録していたジャーナルログを再生する。再生の手順としては、ジャーナルログを読み込んで、最後にSYNCPTした時点までさかのぼる。
図6 ログの再生
図6は、トランザクション2の途中でSYNCPTが取られているケースである。トランザクション1は、ログに記録したトランザクション操作とディスクに記録されている操作が同期しているため、データの整合性が保証される。しかし、トランザクション2はSYNCPTが完了していないため、操作およびデータの整合性が保証されない。トランザクション2以降も同様である。この場合、JFSはコミットされたトランザクションを再生(redo)あるいはコミットされていないトランザクションをロールバック(undo)するなどして、メタ・データの復旧を試みる。
これらの操作はfsck_jfsに実装されており、起動時なども同様の手順でジャーナリング処理が行われる。