log.c static void write_head(void)

トップページ
jupiteroak.hatenablog.com


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

static void write_head(void)
{
  struct buf *buf = bread(log.dev, log.start);
  struct logheader *hb = (struct logheader *) (buf->data);
  int i;
  hb->n = log.lh.n;
  for (i = 0; i < log.lh.n; i++) {
    hb->block[i] = log.lh.block[i];
  }
  bwrite(buf);
  brelse(buf);
}

write_head関数は、メインメモリ上で管理されているlogheader構造体に保存されているデータを、ハードディスク上にあるログスペースのヘッダーブロック(ハードディスク上にあるログスペースの先頭セクタ)に書き出します。


処理の内容

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

struct buf *buf = bread(log.dev, log.start);

bread関数を呼び出して、ハードディスク上にあるログスペースのヘッダーブロックに対応するバッファを取得します。第一引数にはヘッダーブロックが所属するハードディスク(マスタードライブ)を指定し、第ニ引数にはヘッダーブロックのセクタ番号を指定します。

ログスペースのヘッダーブロックに対応するバッファに格納されているデータをlogheader構造体で表現する

struct logheader *hb = (struct logheader *) (buf->data);

buf構造体のメンバである配列data(uchar data[BSIZE])にハードディスクから読み込んで取得したログスペースのヘッダーブロックのデータ512バイトが保存されています。 この配列data(uchar data[BSIZE])の先頭アドレスをlogheader構造体へのポインタlhに代入することで、dataに格納されているデータ(ハードディスクから読み込んで取得したログスペースのヘッダーブロックのデータ)をlogheader構造体として扱うことができます。logheader構造体はログスペースのヘッダーブロックを表現したデータ構造です。 

メインメモリ上で管理されているlogheader構造体でバッファに格納されているlogheader構造体を更新する

メインメモリ上で管理されているlogheader構造体でバッファに格納されているlogheader構造体を更新します。
ログスペースのヘッダーブロックへの書き出しは、メインメモリで管理されているlogheader構造体log.lh(log構造体内にあるlogheader構造体)の値を、一度、ログスペースのヘッダーブロックに対応しているバッファに書き込むことで成されます(バッファに書き込まれた後に、バッファ内のデータをハードディスク上のセクタに書き出します)。
以下の操作によって、メインメモリ上で管理されているlogheader構造体log.lhの値が、ハードディスク上にあるログスペースのヘッダーブロックに最新の値として書き出されます。

バッファに格納されているlogheader構造体のnメンバを更新する

hb->n = log.lh.n;

メインメモリ上で管理されているlogheader構造体のnメンバの値で、バッファに格納されているlogheader構造体のnメンバの値を、更新します。
logheader構造体のnメンバの値は、トランザクション内で管理されているブロックの数を示しています。

バッファに格納されているlogheader構造体のblock配列メンバの各要素を更新する

for (i = 0; i < log.lh.n; i++) {
    hb->block[i] = log.lh.block[i];
  }

メインメモリ上で管理されているlogheader構造体が持つblock配列の各要素で、バッファに格納されているlogheader構造体のメンバであるblock配列の各要素を、更新します。block配列の各要素には、トランザクション内で管理されている各ブロック(セクタ)のセクタ番号がそれぞれ保存されています。

バッファに格納されているlogheader構造体のデータをハードディスクへ書き出す

bwrite(buf);

bwrite関数を呼び出して、バッファに格納されているlogheader構造体のデータを、ハードディスク上にあるログスペースのヘッダーブロックへ書き出します。

バッファのスリープロックを解放する

brelse(buf);

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

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

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