vm.c int deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)

トップページ
jupiteroak.hatenablog.com


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

int
deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
  pte_t *pte;
  uint a, pa;

  if(newsz >= oldsz)
    return oldsz;

  a = PGROUNDUP(newsz);
  for(; a  < oldsz; a += PGSIZE){
    pte = walkpgdir(pgdir, (char*)a, 0);
    if(!pte)
      a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
    else if((*pte & PTE_P) != 0){
      pa = PTE_ADDR(*pte);
      if(pa == 0)
        panic("kfree");
      char *v = P2V(pa);
      kfree(v);
      *pte = 0;
    }
  }
  return newsz;
}

deallocuvm関数は、仮想アドレスnewszから仮想アドレス未満oldszの範囲にマッピングされている全てのページフレームを解放します(全ての4KBのメモリ領域を未使用状態にします)。

引数 pde_t *pgdir
ページディレクトリの先頭アドレスです。

引数 uint oldsz
解放対象となるページフレームに関連した仮想アドレスの範囲における、終端アドレスです。

引数 uint newsz
解放対象となるページフレームに関連した仮想アドレスの範囲における、先頭アドレスです。

戻り値 int newsz または int oldsz
全てのページフレームを解放した場合は、newszが戻り値となります。
全てのページフレームを解放しなかった場合は、oldszが戻り値とまります。


処理の内容

アドレスnewszが下位側・アドレスoldszが上位側にあることを確認する

if(newsz >= oldsz)
    return oldsz;

newsz >= oldszが真となる場合→アドレスnewszが下位側・アドレスoldszが上位ではない場合は、oldszを戻り値として処理を終了します。

アドレスnewszを4KB境界に切り上げる

a = PGROUNDUP(newsz);

PGROUNDUPマクロを使って、アドレスnewszを4KB境界のアドレス値(0x1000倍のアドレス値)に切り上げます。

仮想アドレスnewszから仮想アドレスoldsz未満の範囲にマッピングされている全てのページフレームを解放する

for(; a  < oldsz; a += PGSIZE){
    pte = walkpgdir(pgdir, (char*)a, 0);
    if(!pte)
      a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
    else if((*pte & PTE_P) != 0){
      pa = PTE_ADDR(*pte);
      if(pa == 0)
        panic("kfree");
      char *v = P2V(pa);
      kfree(v);
      *pte = 0;
    }
  }

仮想アドレスnewszから仮想アドレス未満oldszの範囲において、PTE(ページテーブルエントリ)を取得し、そのPTE(ページテーブルエントリ)が参照しているページフレーム(4KBのメモリ領域)を解放していきます。

PTE(ページテーブルエントリ)を取得する

pte = walkpgdir(pgdir, (char*)a, 0);

walkpgdir関数を呼び出して、仮想アドレスaに対応しているpte(ページテーブルエントリ)を取得します。

ページテーブルが存在していなかった場合

if(!pte)
      a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;

!pteが真となる場合→pteが0の場合→仮想アドレスaに対応しているページテーブルが存在していなかった場合は、PDE(ページディレクトリエントリ)のインデックス値(仮想アドレスaのbit31-22)をインクリメントすることで参照先のページテーブルを変更してから、次のループへ進みます。
PDE(ページディレクトリエントリ)のインデックス値(仮想アドレスaのbit31-22)をインクリメントした仮想アドレスを取得するために、PGADDRマクロの第一引数にPDX(a) + 1を指定しています。
また、次のループに進んだ際の再設定式で、不必要にPGSIZE(#define PGSIZE 4096)が加算されるので、あらかじめPGSIZE(#define PGSIZE 4096)分減算しています。

ページテーブルがページフレームを参照している場合

else if((*pte & PTE_P) != 0){
      pa = PTE_ADDR(*pte);
      if(pa == 0)
        panic("kfree");
      char *v = P2V(pa);
      kfree(v);
      *pte = 0;
    }
ページフレームの先頭アドレスを取得する
pa = PTE_ADDR(*pte);
if(pa == 0)
  panic("kfree");

PTE_ADDRマクロを使って、pte(ページテーブルエントリ)からページフレームの先頭アドレスpaを取得します。ページフレームの先頭アドレスpaが0の場合は、panic関数を呼びだして、メッセージを出力します。

ページフレーム(4KBのメモリ領域)を解放する
char *v = P2V(pa);
kfree(v);

ページフレームの先頭アドレスpa物理アドレスなので、 P2Vマクロを使って、物理アドレスpaを仮想アドレスvに変換します。
その後、kfree関数を呼び出して、ページフレーム(4KBのメモリ領域)を解放します。

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

return newsz;