人生は勉強ブログ

https://github.com/dooooooooinggggg

Linux(64bit)のページング機構

この記事はsfc-rg Advent Calendar 2018 4日目の記事です.

概要

Linuxのページング機構について勉強する必要があったが,書籍やインターネットにある情報は,32bit版のものが多く,64bit版の情報を探すのに苦労した.

今後自分のような人が出てきた際に,困らないように,32bit版と64bit版の違いなどについて書く.

この記事で扱うLinuxは,x86およびx86-64アーキテクチャにおけるLinuxとする.

セグメント機構とページング機構

x86アーキテクチャおよびx86-64アーキテクチャには,その上に乗るOSのアドレス変換機構をサポートする機能がある.

それが,セグメント機構と,ページング機構である.

論理アドレス --[セグメント機構]--> リニアアドレス --[ページング機構]--> 物理アドレス

それぞれの役割は上の通り.つまり,CPUには二段階の変換機構がハードウェアレベルで用意されているということ.

セグメント機構は,無効にすることはできないが,ページング回路は,有効無効を切り替えることができる.OS次第.

上に出てきている,リニアアドレスというのが,いわゆる仮想アドレスのことであり,Linuxにおいては,これがプロセス空間にて使用される.

セグメント機構

セグメント機構に関しては,この記事では扱わない.以下の記事で解説した.

blog.ishikawa.tech

ページング機構

上述の通り,有効無効を切り替えることができる.デフォルトではオフになっており,この場合は,仮想アドレスと物理アドレスが等価となる.

これからは,ページング回路が有効な場合について書く.

ページング回路は,アドレス空間を固定長(4KB or 4MB)のブロックに分割し.ブロック単位でアドレス変換を行い,これらのブロックのことをページフレームと言う.

アドレス変換に必要なページフレームは,メモリ上に置かれ,その物理アドレスをCR3レジスタに格納する.つまり,プロセスごとにCR3レジスタの値が存在するということ.

このCR3レジスタの値は,プロセスのアドレス空間を保持する上でとても大事な役割を果たすため,ユーザープロセスからの書き換えはできない.

また,このページフレームをうまく設定することで,同じ物理アドレスを複数のアドレスから参照することもできる.

このページング回路により,プロセスからはあたかもメモリ全体を占有しているように見える. しかし,プロセスごとに,メモリ空間全体を与えていては,メモリがいくらあっても足りなくなってしまう. なので,この仮想アドレスに対応する物理アドレスはその先に存在しない,というときに,そういうフラグをエントリのなかに立てる.こうすることで,メモリ空間をいい感じに扱うことができる.

ページングのアドレス変換は,ページフレーム単位で行われる. 仮に,一段階で全てのページフレームを扱おうとすると,32bitの場合,4GB / 4KB = 1048576個必要になる.しかし,一つのプロセスが,4GBのメモリ空間を使うことはない. そうなると,ほとんどが未使用の穴だらけのテーブルが完成してしまう.こうすると,無駄が大きい. そのため,32bitでは,2段階,64bitでは,4段階の変換テーブルが用意されている.こうすることで,プログラムの大きさが小さい場合に,2番目以降の変換テーブルを設定する必要がなくなり,無駄が削減できる.

各テーブルとエントリ

基本的な役割は32bit,64bitで同じだが,各エントリのbit数,エントリ数などが微妙に異なる.

共有

各エントリの0bitには,Pフラグが割り当てられている.このフラグが1となっているとき,そのエントリがさすページフレーム,物理アドレスは存在しないということとなる

各エントリにある値は,便宜上物理アドレスとしているが,実際の物理アドレスは,エントリに12個0を付け足したものとなる.こうすることで,4KB境界に合わせられる.64bitの場合は,28bitでベースアドレスと指定しているので,現在のCPUでは,40bitまでのアドレス空間しか使うことができない.

32bit

32bitにおいては,変換テーブルが二つ存在する.

1段階目の変換テーブルは,ページディレクトリと呼ばれる.ページディレクトリの大きさは,4KBで,4Byteのページディレクトリエントリが1024個ある.

このサイトに書いてあるように,ページディレクトリエントリのインデックスは,仮想アドレス空間の31bit - 22bitに格納されている.10bitであり,エントリ数の1024と等しい.

4Byte = 32bitのうち,31bit - 12bitにページテーブルの物理アドレスが格納されている.仮想アドレスで指定されてインデックスを見て,そのインデックスのエントリから二段階目の物理アドレスを読み,次の階層に進む.

2段階目の変換テーブルは,ページテーブルと呼ばれる.ここにもページディレクトリと同様,4Byteのエントリが1024個あり,テーブルのサイズは4KBである.先ほどと同様,仮想アドレスで指定されているインデックス(仮想アドレスの21bit - 12bit)を見て,該当エントリに飛ぶ.そのエントリの31bit - 12bitにページフレームのベースアドレスが格納されている.ここに,仮想アドレスのオフセット部分(11bit - 0bit)を足すことで,仮想アドレスが指す物理アドレスを割り出すことができる.

64bit

64bitにおける変換機構は32bitのものと少しだけエントリの数とサイズが違う.

64bitでは,ページディレクトリ(Page Directory),ページテーブル(Page Table)の上に,ページマップレベル4(Page Map Level 4)という階層と,ページディレクトリポインタ(Page Directory Pointer)という階層が追加されている.

順番でいうと,32bitが

  1. ページディレクト
  2. ページテーブル

であったが,64bitでは,

  1. ページマップレベル4
  2. ページディレクトリポインタ
  3. ページディレクト
  4. ページテーブル

となっている.

テーブルのサイズは4KBで固定であるが,64bitでは,扱うべきアドレス幅が大きくなっている. 各エントリは,32bitのときは4Byteであったが,64bitでは,8Byteとなっている.これに伴い,各エントリ数は,512個となっている.(8Byte * 512 = 4KB)

ページサイズや変換の階層が異なったりと,他にも色々なモードがあるが,とりあえず基本的なもののみ解説した.

参考文献

詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版

(アスキー書籍)

参考にしたサイト