ide.c void iderw(struct buf *b)

トップページ
jupiteroak.hatenablog.com


ide.c
https://github.com/mit-pdos/xv6-public/blob/master/ide.c#L137

void iderw(struct buf *b)
{
  struct buf **pp;

  if(!holdingsleep(&b->lock))
    panic("iderw: buf not locked");
  if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
    panic("iderw: nothing to do");
  if(b->dev != 0 && !havedisk1)
    panic("iderw: ide disk 1 not present");

  acquire(&idelock);  //DOC:acquire-lock

  // Append b to idequeue.
  b->qnext = 0;
  for(pp=&idequeue; *pp; pp=&(*pp)->qnext)  //DOC:insert-queue
    ;
  *pp = b;

  // Start disk if necessary.
  if(idequeue == b)
    idestart(b);

  // Wait for request to finish.
  while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
    sleep(b, &idelock);
  }


  release(&idelock);
}

iderw関数は、ハードディスクの読み込み処理・書き出し処理で使用するバッファbをIDEキューの最後尾に追加し、IDEキューの先頭にあるバッファを使ったハードディスクの読み込み処理・書き出し処理を開始させます。

引数 struct buf *b
ハードディスクの読み込み処理・書き出し処理で使用するバッファを指定するアドレスです。


処理の内容

現在実行中のプロセスがスリープロックを保持してることを確認する

if(!holdingsleep(&b->lock))
    panic("iderw: buf not locked");

holdingsleep関数を呼び出して、現在実行中のプロセスがスリープロックを保持しているかを確認します。
!holdingsleep(&b->lock)が真となる場合→holdingsleep関数の戻り値が0となる場合→スリープロックを保持していない場合は、panic関数を呼び出してメッセージを出力します。

バッファbがハードディスクから読み込んだデータを保存しているだけの状態である場合はメッセージを出力する

if((b->flags & (B_VALID|B_DIRTY)) == B_VALID)
    panic("iderw: nothing to do");

b->flagsをB_VALID(#define B_VALID 0x2)とB_DIRTY(#define B_DIRTY 0x4)の論理和でマスク処理をすることにより、バッファbのflagsメンバbit1とbit2を取り出しています。
(b->flags & (B_VALID|B_DIRTY)) == B_VALIDが真となる場合→バッファbのflagsメンバbit1だけに1が設定されている場合(b->flagsにB_VALIDのみが含まれる場合)→バッファbがハードディスクから読み込んだデータを保存しているだけの状態である場合は、panic関数を呼び出してメッセージを出力します。

スレイブドライブを使用する場合に問題がないか確認する

 if(b->dev != 0 && !havedisk1)
    panic("iderw: ide disk 1 not present");

b->dev != 0 && !havedisk1 が真となる場合→b->devが1、かつ、havedisk1が0の場合→バッファbに対応しているセクタがスレイブドライブ上にあるにも関わらず、ハードディスクドライブにスレイブドライブがない場合は、panic関数を呼び出してメッセージを出力します。

クリティカルセクションの入口を定める

acquire(&idelock);

ハードディスクの読み込み処理・書き出し処理を排他制御するために、acquire関数を呼び出してロックを取得し、クリティカルセクションの入口とします。

バッファbをIDEキューの最後尾に追加する

バッファbのqnextメンバを初期化する

  b->qnext = 0;

バッファbのqnextメンバ(buf構造体へのポインタ)を0で初期化します。
qnextメンバはIDEキューを作成するときに使用されるbuf構造体へのポインタです。
IDEキューは、ディスクからの読み込み処理やディスクへの書き出し処理がなされるのを保留してあるバッファから成るリスト(buf構造体の単方向リスト)です。

IDEキュー内にあるバッファを先頭から順番に走査する

  for(pp=&idequeue; *pp; pp=&(*pp)->qnext) 
    ;
  *pp = b;

buf構造体へのポインタへのポインタpp と 間接参照演算子* を用いて、IDEキュー内にあるバッファ(buf構造体)を先頭から順番に走査します。
idequeueはbuf構造体へのポインタで、IDEキューの先頭バッファのアドレスが格納されています。
&idequeue(buf構造体へのポインタを指定するアドレス)をbuf構造体へのポインタへのポインタppに格納することで、*ppは先頭バッファのアドレスが格納されたポインタ(buf構造体へのポインタ)になり、(*pp)->qnextとして、2番目のバッファのアドレスが格納されたポインタ(buf構造体へのポインタ)を得ることができます。
さらに、&(*pp)->qnext(buf構造体へのポインタを指定するアドレス)をbuf構造体へのポインタへのポインタppに格納することで、*ppは2番目のバッファのアドレスが格納されたポインタ(buf構造体へのポインタ)になり、(*pp)->qnextとして、3番目のバッファのアドレスが格納されたポインタ(buf構造体へのポインタ)を得ることができます。
またさらに、&(*pp)->qnext(buf構造体へのポインタを指定するアドレス)をbuf構造体へのポインタへのポインタppに格納することで、*ppは3番目のバッファのアドレスが格納されたポインタ(buf構造体へのポインタ)になり、(*pp)->qnextとして、4番目のバッファのアドレスが格納されたポインタ(buf構造体へのポインタ)を得ることができます。
以上のような操作を繰り返して、最終的に、IDEキュー内にある最後尾バッファが持つqnextメンバを指定するアドレス&(*pp)->qnext(buf構造体へのポインタを指定するアドレス)を取得します。
for文が終了するとき→継続条件式が偽となるときは、IDEキュー内にある最後尾バッファが持つqnextメンバの値*pp((buf構造体へのポインタ)が0になります。このとき、buf構造体へのポインタへのポインタpp(buf構造体へのポインタを指定するアドレスを格納する変数)には、IDEキューの最後尾にあるバッファが持つqnextメンバを指定するアドレス&(*pp)->qnext(buf構造体へのポインタを指定するアドレス)が格納されているので、引数で渡されたバッファbのアドレスを*ppに格納することで、バッファbをIDEキューの最後尾に追加します。

IDEキューの先頭にあるバッファを使ったハードディスクの読み込み処理・書き出し処理を開始させる

  if(idequeue == b)
    idestart(b);

idequeue == b が真となる場合→IDEキューの先頭バッファが引数で渡されたバッファbとなる場合は、idestart関数を呼び出してハードディスクの読み込み処理・書き出し処理を開始させます。

ハードディスクの読み込み処理・書き出し処理が完了するまでプロセスを休止状態にする

  while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){
    sleep(b, &idelock);

b->flagsをB_VALID(#define B_VALID 0x2)とB_DIRTY(#define B_DIRTY 0x4)の論理和でマスク処理をすることにより、バッファbのflagsメンバbit1とbit2を取り出しています。
(b->flags & (B_VALID|B_DIRTY)) != B_VALIDが真となる場合→バッファbのflagsメンバのbit1が0、または、bit2が1に設定されている場合→バッファbにデータが保存されていない、または、バッファbに保存されているデータをハードディスクへ書き出す必要がある状態の場合は、sleep関数を呼び出して、プログラムの制御を別のプロセスに移します(CPU資源を別のプロセスに譲ります)。idestart関数内で行われるハードディスクの読み込み処理・書き出し処理は完了するのに時間がかかるので、この時点でハードディスク上のセクタからバッファbへの読み込み、または、バッファbからハードディスク上のセクタへの書き出しが完了していない場合は、sleep関数を呼び出します。

(b->flags & (B_VALID|B_DIRTY))の値

bit2=1(Dirty) bit2=0
bit1=1(Valid) 0b 0000 0110 0b 0000 0010
bit1=0 0b 0000 0100 0b 0000 0000

(b->flags & (B_VALID|B_DIRTY)) != B_VALIDの値

bit2=1(Dirty) bit2=0
bit1=1(Valid) 真(1) 偽(0)
bit1=0 真(1) 真(1)

クリティカルセクションの出口を定める

release(&idelock);

release関数を呼び出して取得したロックを解放し、クリティカルセクションの出口とします。