vm.c int loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)

トップページ
jupiteroak.hatenablog.com


vm.c
https://github.com/mit-pdos/xv6-public/blob/master/vm.c#L197

int
loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
{
  uint i, pa, n;
  pte_t *pte;

  if((uint) addr % PGSIZE != 0)
    panic("loaduvm: addr must be page aligned");
  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
      panic("loaduvm: address should exist");
    pa = PTE_ADDR(*pte);
    if(sz - i < PGSIZE)
      n = sz - i;
    else
      n = PGSIZE;
    if(readi(ip, P2V(pa), offset+i, n) != n)
      return -1;
  }
  return 0;
}

loaduvm関数は、ページディレクトリpgdirが指定する仮想アドレスのマッピング設定に基づいて、iノードip内におけるoffの位置からszバイトのデータを読み込み、読み込んだデータを先頭アドレスaddrのメモリ領域にロードします。

引数 pde_t *pgdir
仮想アドレスaddrのマッピング設定に関わっているページディレクトリの先頭アドレスです。

引数 char *addr
ロード先のとなるメモリ領域の先頭アドレスです。

引数 struct inode *ip
読み込み対象となるiノードの先頭アドレスです。

引数 uint offset
読み込み対象となるiノードにおいて、読み込みを開始する位置となるバイトオフセットの値です。

引数 uint sz
読み込まれるデータのサイズです。

戻り値 0 または -1
読み込んだデータのロードに成功した場合は、0が戻り値となります。
読み込んだデータのロードに失敗した場合は、-1が戻り値となります。


処理の内容

仮想アドレスaddrが4KB境界であることを確認する

if((uint) addr % PGSIZE != 0)
    panic("loaduvm: addr must be page aligned");

addr % PGSIZE != 0 が真となる場合→仮想アドレスaddrが4KB境界(0x1000の倍数値)ではない場合は、panic関数を呼び出してメッセージを出力します。

iノードからszバイトのデータをロードする

  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
      panic("loaduvm: address should exist");
    pa = PTE_ADDR(*pte);
    if(sz - i < PGSIZE)
      n = sz - i;
    else
      n = PGSIZE;
    if(readi(ip, P2V(pa), offset+i, n) != n)
      return -1;
  }

for文を使って、iノードipから、先頭アドレスaddrのメモリ領域に、szバイトのデータをロードします。
各ループで最大4KBずつデータを読み込み、最終的に総計szバイトのデータを読み込みます。


初期設定式 i = 0; について
iの値は、今までのループで読み込んだデータ量の総計です。


再設定式 i += PGSIZE について
一回のループで4KBのデータを読み込むので、再設定式ではiの値を PGSIZE分インクリメントします。


継続条件式 i < sz; について
今までのループで読み込んだデータ量の総計iが、szを越えた時点でループを脱出します。

仮想アドレスaddr+iに対応しているPTE(ページテーブルエントリ)を取得する

if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
      panic("loaduvm: address should exist");

walkpgdir関数を呼び出し、ページディレクトリpgdirを使って、仮想アドレスaddr+iに対応しているpte(ページテーブルエントリの先頭アドレス)を取得します。仮想アドレスva+iに対応しているpte*(ページテーブルエントリ)が既に設定されている前提でwalkpgdir関数を呼び出すので、第三引数に0を指定しています。
(pte = walkpgdir(pgdir, addr+i, 0)) == 0 が真となる場合→walkpgdir関数の戻り値が0となる場合→仮想アドレスvaに対応しているpte*(ページテーブルエントリ)が存在しない場合は、panic関数を呼び出してメッセージを出力します。

PTE(ページテーブルエントリ)が参照している物理アドレスを取得する

pa = PTE_ADDR(*pte);

PTE_ADDマクロを使って、pte*(ページテーブルエントリ)から、そのpte*(ページテーブルエントリ)が参照しているページフレームの先頭アドレス(物理アドレス)を取得します。

今回のループで読み込むデータ量nを定める

    if(sz - i < PGSIZE)
      n = sz - i;
    else
      n = PGSIZE;

今回のループで読み込むデータ量nを定めます。
szは、読み込む必要があるデータ量の総計です。
iは今までのループで読み込んだデータ量の総計です。
よって、sz - iは、読み込む必要がある残りのデータ量とまります。
sz - i < PGSIZE が真となる場合→読み込む必要がある残りのデータ量sz - iがPGSIZE(ページ単位)より小さい場合→、今回のループで読み込むデータ量n を 読み込む必要がある残りのデータ量sz - iにします(今回のループが最後のループになります)。
sz - i < PGSIZE が偽となる場合→読み込む必要がある残りのデータ量sz - iがPGSIZE(ページ単位)以上の場合は、今回のループで読み込むデータ量nをPGSIZE(ページ単位)にします。

iノードから最大で4KBのデータを読み込む

    if(readi(ip, P2V(pa), offset+i, n) != n)
      return -1;

readi関数を呼び出して、iノードip内におけるoffの位置からnバイトのデータを読み込み、読み込んだデータをアドレスP2V(pa)となるメモリ領域に保存します。
readi(ip, P2V(pa), offset+i, n) != n が真となる場合→readi関数によって読み込まれたデータ量がnと異なる場合→readi関数が引数nで指定した分のデータ量を読み込めなかった場合は、-1を戻り値として処理を終了します。

0を戻り値としてリターンする

  return 0;