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(ページ単位)にします。
0を戻り値としてリターンする
return 0;