log.c static void write_log(void)

トップページ
jupiteroak.hatenablog.com


log.c
https://github.com/mit-pdos/xv6-public/blob/master/log.c#L177

static void write_log(void)
{
  int tail;

  for (tail = 0; tail < log.lh.n; tail++) {
    struct buf *to = bread(log.dev, log.start+tail+1); // log block
    struct buf *from = bread(log.dev, log.lh.block[tail]); // cache block
    memmove(to->data, from->data, BSIZE);
    bwrite(to);  // write the log
    brelse(from);
    brelse(to);
  }
}

write_log関数は、トランザクション内で管理されているブロックをハードディスク上のログスペースに保存します。


処理の内容

トランザクション内で管理されているブロックをハードディスク上のログスペースに保存する

for (tail = 0; tail < log.lh.n; tail++) {
    ...

log構造体内にあるblock配列の各要素には、トランザクション内で管理されているブロック(セクタ)のセクタ番号が格納されています。
このセクタ番号を使って、トランザクション内で管理されているブロック(セクタ)を、ハードディスク上にあるログスペースに保存していきます(ハードディスク上にあるログスペースのヘッダーブロックの次にあるブロック(セクタ)から書き出しを行っていきます)。

ハードディスク上にあるログスペースのブロックに対応するバッファを取得する

struct buf *to = bread(log.dev, log.start+tail+1);

bread関数を呼び出して、ハードディスク上にあるログスペースのブロック(セクタ)に対応するバッファを取得します。
log.startは、ログスペースのヘッダーブロックを指定するセクタ番号です。よって、1回目のループでは、log.start+tail+1は、ヘッダーブロックの次にあるブロック(セクタ)を指定するセクタ番号とまります。

トランザクション内で管理されているブロックに対応するバッファを取得する

struct buf *from = bread(log.dev, log.lh.block[tail]);

bread関数を呼び出して、トランザクション内で管理されているブロック(セクタ)に対応するバッファを取得します。
block[tail]には、トランザクション内で管理されているブロック(セクタ)のセクタ番号が格納されています。

トランザクション内で管理されているブロックからハードディスク上にあるログスペースのブロックへデータを保存する

memmove(to->data, from->data, BSIZE);

memmove関数を呼び出して、トランザクション内で管理されているブロック(セクタ)に対応しているバッファから、ハードディスク上にあるログスペースのブロック(セクタ)に対応しているバッファへ、データをコピーします。

ハードディスク上にあるログスペースのブロックへ書き出す

bwrite(to);

bwite関数を呼び出して、バッファtoに格納されているデータを、バッファtoに対応しているハードディスク上のセクタ(ハードディスク上にあるログスペースのブロック)へ書き出します。

バッファに関連しているスリープロックを解放する

brelse(from);
brelse(to);

brelse関数を呼び出して、バッファfromに関連しているスリープロックを解放し、他のプロセスがこのバッファを利用できるようにします。
brelse関数を呼び出して、バッファtoに関連しているスリープロックを解放し、他のプロセスがこのバッファを利用できるようにします。

トランザクションの実質的な完了

トランザクションの実質的な完了(実質的なコミット)は、write_log関数とwrite_head関数によってなされます。
write_log関数はトランザクション内で管理されているブロックをハードディスク上のログスペースに保存します。
トランザクション内で管理されているブロックがハードディスク上のログスペースに保存されていれば、OSがクラッシュした後の再起動時に呼び出されるinstall_trans関数によって、ハードディスク上のログスペースからトランザクション内で管理されているブロックを復旧(リカバリー)させることができます。
しかし、install_trans関数はlogheader構造体(ヘッダーブロックを表現したデータ構造)のnメンバの値が0の時は、復旧(リカバリー)を行いません。
そのため、write_head関数を呼び出して、logheader構造体の値もハードディスク上のログスペースに保存しなければなりません。
以上のような理由で、トランザクションの実質的な完了(実質的なコミット)にはwrite_log関数とwrite_head関数の2つの処理が必要となります。