メモリ管理ユニット
メモリ管理ユニット (メモリかんりユニット、memory management unit、MMU) は、コンピュータのハードウェア部品のひとつであり、CPUの要求するメモリアクセスを処理する機構の事である。
主な機能としては、仮想アドレスを物理アドレスに変換する機能(すなわち仮想記憶管理)、メモリ保護機能、キャッシュ制御機能、バス調停機能、そして単純な8ビットシステムなどに見られるバンク切り替え機能などがある。なお、メモリ保護機能を持ち、仮想記憶機能を持たない物を特にメモリ保護ユニットと呼ぶことがある。これは、小さく信頼性の要求があり、かつリアルタイム性の要求されるプロセッサに伴って使われることがある。
最近のMMUでは、一般的にメモリ空間(アドレス空間)はページに分割される。ページのサイズは 2Nであり、たいていの場合数Kバイトである。CPUが出力するアドレス下位Nビット(ページ内オフセット)は仮想→物理の変換においては変化しない。CPUが出力するアドレス上位ビットは仮想ページ番号である。ページテーブルは配列構造になっていて、仮想ページ番号がそのインデックスになっている。各ページテーブルエントリ (PTE) は仮想ページ番号(そのPTEのインデックス)に対応した物理ページ番号を格納している。この物理ページ番号とページ内オフセットを組み合わせて完全な物理アドレスを作り出すのである。
PTEには、以下のような情報が含まれている。
最後にアクセスされたのはいつか(ページ置換アルゴリズムで使用)(必ずしもきちんとした時刻が記されているわけではない)
特権レベルごとのアクセス権(特権モードでの読み / 書き / 実行、ユーザモードでの読み / 書き/ 実行の可 / 不可を設定)
ある仮想ページに対して物理メモリ (RAM) が対応していないこともある。その場合、MMUは「ページフォールト」をCPUに通知する。オペレーティングシステム (OS) はその通知を受けて、空いている物理ページを探し、PTEの内容をその物理ページを示すよう書き換える。ページフォールト処理から元のプログラムに復帰するとフォールトを発生させた命令を再度実行して、今度はPTEが物理ページを指しているので正常に処理が続行される。
もし、空きメモリがない場合、使用中のページから(何らかの置換アルゴリズムを使って)物理ページを選択し、その内容をディスクに書き込んで退避し、そのページを使う(必要ならばディスクから内容を復帰させる)。これをページング方式と呼ぶ。ページテーブル自体もメモリ上に存在するので、PTEが存在しないという場合もあり、その場合もOSとMMUが協力して新たにページテーブルを割り当てる。
「ページフォールト」は時としてソフトウェアのバグによって発生することもある。MMUのメモリ保護機能を使えば、OSはアクセス権を与えない設定をすることで不正なプログラムからメモリを守ることができる。一般にOSはプログラム(プロセス)ごとに別の仮想アドレス空間を用意する。
MMUはメモリのフラグメンテーションに対しても有効である。メモリブロックのアロケートと解放が繰り返されると、フリーなメモリは十分あっても、大きくかつ連続的なブロックをアロケートできなくなってしまう。仮想記憶では、こまぎれな物理ページを集めて連続な仮想空間をつくることができる。
マイクロプロセッサの初期の設計では、MMUは独立した集積回路となっていた。たとえば、Macintosh IIのMC68020 CPUでは MC68851 MMU を使い、Z8000ファミリでは Z8010 が使われた。その後のCPUであるMC68030やZ280ではマイクロプロセッサ内にMMUを内蔵している。
この項目では最近のページング方式を使うMMUについて解説したが、MMUにはセグメント方式や他の方式を使用しているものもある。そしてそれは現在のマイクロプロセッサにも残っている。x86系統のマイクロプロセッサはページングだけでなくセグメント方式も提供している。
最近のシステムでは、メモリを 4 KiB から 64 KiB のページに分割することが多く、また最大で 2 MiB から 1 GiB の巨大ページも利用可能である。ページのアドレス変換情報はTLBにキャッシュされる。古い設計の RISC では、TLB に変換情報がないときに OS に対して例外が発生するようになっているものもあった。多くのシステムでは、ハードウェアベースのページツリー探索機能がある。MMU を無効化可能であることが多く、ページフォールト時に MMU を無効化することもある。
ARMアーキテクチャ のプロセッサは、ARM の 仮想メモリシステムアーキテクチャに基づいて MMU が実装されている。現在のアーキテクチャは 4 KiB と 16 KiB のページと、1 MiB のセクション、16 MiB の スーパーセクションを規定している。古いシステムでは 1 KiB のタイニーページもあった。4 KiB と 64 KiB の場合は2段階、1 MiB もしくは 16MiB のセクションの時は1段階のページテーブルを使用する。
TLB の更新はページウォークの際に自動的に更新される。
PTE は読み書き権限、キャッシュ対象かどうか、NXビット、non-secure ビットなどの情報を持つ
。
DEC Alpha プロセッサは、メモリを 8192バイトのページに分割する。TLBミスが起きると、ファームウェア(PALコードと呼ばれる)が三層の木構造のページテーブルを探索する。64ビットの仮想アドレスは上位ビットから、21ビットが未使用、10ビットがルートレベルのインデックス、10ビットが中間レベルのインデックス、10ビットが最下層レベルのインデックス、13ビットが物理アドレスにそのまま対応する。リード/ライト/実行のパーミッション設定がサポートされている。
PowerPCは数種類の互換性の無いMMUの設計をもったアーキテクチャが存在する。
PowerPC G1/G2/G3/G4 は、ページは通常 4 KiB である。TLBミスが起きると、標準の PowerPC MMU は2つの参照を同時に行う。1つは、データとコード毎に4個または8個ある Block Address Translation (BAT) レジスタが指すアドレス範囲かどうかの参照である。BATレジスタは最大 256 MiB の連続なメモリをマップするもので、OS がカーネル自身をマップするのに使われることが多い。BAT参照が成功すると、もう一方の参照は中止され、無視される。
もう1つの参照は、全プロセッサでサポートされているわけではないが、逆引きページテーブルを参照する。まず、仮想アドレスの上位4ビットで 16本あるセグメントレジスタを1つ選択する。セグメントレジスタにある24ビットの値を仮想アドレスの上位4ビットと置換して 52ビットのアドレスを生成する。セグメントレジスタを使うことで複数のプロセスが1つのハッシュテーブルを利用できるようになっている。52ビットのアドレスはハッシュされ、チップ外のテーブルのインデックスとして使用される。ここで、8個のページテーブルエントリが一致するかどうかチェックされる。ハッシュ衝突によって一致が見つからない場合、プロセッサはハッシュ関数を若干変えて再試行する。それでも一致を見つけられない場合、CPUは(MMUを無効化した上で)OSに例外(トラップ)を通知し、処理を依頼する。OS はハッシュテーブルから1つのエントリを捨てて空きを作り、新たなエントリをそこに入れる。新たなエントリは、通常の木構造のページテーブルを参照することで得る方式もあるし、低速になるがメモリ効率のよい方法としてもっと根本的なメモリ管理のデータ構造を辿って得る場合もある。NXビット制御のサポートはセグメントレジスタで行われているため、256 MiB 単位となる。
この方式の問題は、ハッシュ関数を使うため、キャッシュ上の局所性に乏しい点である。木構造のページテーブルでは、仮想アドレス上近いエントリは近い場所に配置される。PowerPC での OS はこの問題の影響を小さくするため、なるべくハッシュテーブルの大きさを小さくする必要がある。
また、プロセスのページテーブルエントリを削除するのも時間がかかる。これに対処するために OS はなるべくセグメントレジスタの再利用を遅らせようとする。あるいは、メモリの浪費を承知の上で、プロセス単位にハッシュテーブルを持つことも考えられる。G1 ではページテーブルエントリの探索は行わないが、ハッシュ値の生成まではハードウェアが行い、OSがその先を行う。従って、OS が TLB の書き換えを行う。G2、G3 と初期の G4 では、ハードウェアでハッシュテーブルを探索する。最近のチップでは OS がどちらの方式にするかを選択できる。ハードウェアでハッシュテーブル探索しない場合、OS は一般的な木構造のページテーブルを使うことができる。
VAXのページは512バイトと非常に小さい。OS は複数ページを1つのページであるかのように扱うことができる。Linux は 8 ページをグループ化して 4 KiB のページとして扱っている。VAX ではメモリは4つの固定目的の領域に分割され、それぞれ 1 GiBのサイズである。各領域の用途は、アプリケーション用ページメモリ、カーネル用ページメモリ、カーネル用非ページメモリ、未使用の4つである。ページテーブルは大きな配列である。通常、アドレス空間の両端から使うと、このページテーブルは非常に無駄が多いことになるが、アプリケーションのページテーブルはカーネルのページメモリを使って作成される。従って、実質的には2レベルの木構造になっており、ページテーブルにメモリを無駄使いしないようにできる。VAX MMU にはアクセスビットがない(ページ置換アルゴリズムで必要とされる)。このため、OS で何らかのエミュレートが必要となる。典型的な方法としては、OS が定期的にページをアンマップし、ページフォールトの発生をアクセスビットの代わりとする。
x86アーキテクチャは従来互換を保ちつつ発展してきた。そのため、MMUには非常に複雑で様々な操作モードが存在する。Intel 80386およびその後継CPUでの典型的な操作を以下で解説する。
CPU はメモリを 4 KiB ページに分割する。Intel 8086 や Intel 80286 MMU からの伝統とも言うべきセグメントレジスタは、最近の OS ではなるべく無視するのが一般的である。しかし、重要な例外がある。アプリケーションのスレッド局所記憶とカーネルのCPU固有データへのアクセスには、明示的に FSレジスタと GSレジスタが使われる。全てのメモリアクセスにはセグメントレジスタが使われ、どのレジスタが使われるかは実行中のコードに依存する。セグメントレジスタはテーブルのインデックスのように働き、仮想アドレスに加算すべきオフセットを提供する。上述のFSおよびGSを使う場合以外は、OS はそのオフセットがゼロになることを保証できる。オフセットが加算されると、アドレスは32ビット以内に収まるようにマスクされる。その結果を使って木構造のページテーブルを参照する。その場合のアドレスの分割は上位から、10ビットがルートレベルのインデックス、10ビットが最下層レベルのインデックス、12ビットがそのまま物理アドレスの下位12ビットとなる。
Pentium では MMU が若干変更され、下層のページテーブルを省くことで 2 MiB か 4 MiB の巨大ページを利用可能となった。Pentium Proでは、物理アドレス拡張 (PAE) 機能によって物理アドレスが 36ビットとなり、キャッシュ属性を指定可能になっている。
NXビットサポートは当初セグメント単位だったため、非常に扱いにくかった。最近では PAEモードでページ単位のNXビットをサポートしている。PaXでは、セグメント単位のNXビットでページ単位の処理をエミュレートしており、性能低下もあまりない。
AMD64およびIntel 64は x86 の 64ビット拡張である。この場合も上述のFSとGS以外のセグメントレジスタはオフセットがゼロに設定され、無視される。ページテーブルは4階層になる。上位ビットから、16ビットは未使用で、9ビットずつが各層のインデックス、残り12ビットがそのまま物理アドレスの下位12ビットとなる。未使用の16ビットは下位48ビットを符号拡張したものとなる。また、ページ単位のNXビットがサポートされている。
System/370とその後継のアーキテクチャでは、アクセスビットとダーティビットをページテーブル以外に格納するという珍しい方式になっている。それらのビットは仮想アドレスではなく物理アドレスに対応付けられていて、特殊な命令でアクセスできる。これにより仮想化が容易になっている。また、通常ならOSがページテーブルにあるそれらのビットを物理メモリに対応した別のデータ構造にコピーするなどの処理が必要だが、System/370 の方式ではそれが不要となる。
PA-RISCでは、ハッシュ・ページ・テーブルと呼ばれるより複雑な構造でアドレス変換を行っている。この機構はIA-64にも受け継がれているため、IA-64は一般的なページング方式とPAライクなハッシュ方式とIA-32モードでのセグメント方式の3つの方式をサポートしている。
MIPSアーキテクチャではMMUはあるもののページテーブルは規定されておらず、オペレーティングシステムが直接トランスレーション・ルックアサイド・バッファを書き換えるようになっている。