• メモリ空間をページと呼ばれる単位に分割
    • 1つのページは4KiB
  • ソフトウェアが指定する論理アドレス(logical address)を、物理メモリアドレス(物理アドレス)にマッピングする機能をページングと呼ぶ
    • 正確には、論理アドレス→(セグメンテーション)→リニアアドレス→(ページング)→物理アドレス
      • 64bitモードではセグメンテーションによるアドレス変換が行われない
  • 論理アドレスを物理アドレスに変換するための変換表をページテーブルという
    • これはページエントリの配列のようなものになっている
      • ページエントリは8byteで、[62:52] および [11:9] はOSが自由に使える領域、63bitと [8:0] がフラグ、それ以外のビット [51:12] でアドレスを示す
        • ページエントリの値 & 0x000_ff'ffff'ffff_000 が変換後の物理アドレスになるようにする
  • ページテーブルを複数の層に分けることで、メモリ消費を抑えたりすることが出来る?
    • この場合、「ページテーブル」は最下層のことを指す
  • x86_64ではページテーブルが4層ある
    • 呼び方は上から順に
      • ページマップレベル4テーブル(PML4 Table)
      • ページディレクトリポインタテーブル(PDP Table)
      • ページディレクトリ
      • ページテーブル
    • Ice Lake以降のCPUは5層テーブルも可能らしい……
  • 各テーブルに512個のエントリが存在する
    • 512個 * 8byte = 4096だから、1つのページにちょうど1つのページテーブルが収まる
  • 仮想アドレス(64bitモードでは当然64bit幅)は6つの区間に分けられており、上位から順に Sign Extention (16bit) , Level[4-1] Index (9bit * 4), Offset (12bit) となっている
    • Sign Extentionの全bitはLevel 4 Index区間の最上位と等しくなければならない
      • 異なっていたらCPU例外

ページエントリのフラグ

bitnamedesc
0presentページがメモリ内に存在する
1writable書込み可能なページ
2user accessibleユーザー権限でアクセス可能なページ
3write through cachingメモリに直接書き込みを行う
4disable cacheページをキャッシュしない
5accessedこのページが使用中である時、CPUが1に設定する
6dirtyこのページに書き込みが行われた時、CPUが1に設定する
7huge page / null後述
8globalアドレス空間変更時、キャッシュ上のこのページが初期化されない
63no executeこのページでのプログラム実行を禁止する
  • huge page / nullは、そのページエントリがどのレベルのページテーブルに属するかによって挙動が変わる
    • Level 1 / 4では0である必要がある
    • Level 2で1であった場合、2MiBのページを作成する
    • Level 3で1であった場合、1GiBのページを作成する
  • global:CR4レジスタのPGEが1である必要がある
  • no execute:EFERレジスタのNXEが1である必要がある

動作例

以下のアセンブリを実行する際におけるCPUのきもちを考える

mov rax, 1
mov rbx. 0x80'8060'4005
mov [rbx], rax
  • まずraxに1を代入
  • 次にrbxに0x0080’8060’4005を代入する
    • 64bit表現:0000’0000’0000’0000_0’0000’0001_0’0000’0010_0’0000’0011_0’0000’0100_0000’0000’0101
    • この例では、Level 4 Indexは1・Level 3 Indexは2・Level 2 Indexは3・Level 1 Indexは4・Offsetは5となっている
  • 次のmovで、raxの値をrbxが指す場所に代入する
    • 今回は *0x0080'8060'4005 = 1という意味
  • CPUは0x0c02’0034に直接代入するのではなく、このアドレスを仮想メモリアドレスと解釈して、物理アドレスに変換してから代入を行う
    • まずCR3(CPUのレジスタ)の値を見る 例えばここが0x1234だった場合、物理アドレス 0x1234にLevel 4のページテーブルが置かれているという意味になる
    • Level 4 Indexは1だから、0x1234[1] にLevel 3のページテーブルがある
    • こういうふうにどんどん辿っていって、0x1234[1][2][3][4] がページエントリとなる
  • 例えばこの 0x1234[1][2][3][4] が 0x000_00’5678’0000_000 だった場合、仮想アドレス 0x0080’8060’4000 == 物理アドレス 0x5678_0000 と変換できるということになる
  • あとはこれにOffsetを加算(+5)すれば良いだけ
  • 仮想アドレス 0x0080’8060’4005 == 物理アドレス 0x5678_0005ということがわかりました