仮想記憶

Question: ページング方式が導入されたのはいつ


仮想記憶（かそうきおく、English: Virtual Memory、バーチャルメモリ）とは、コンピュータ分野におけるメモリ管理の仮想化技法の一種であり、オペレーティングシステムなどが物理的なメモリを、アプリケーション・ソフトウェア（プロセスなど）に対して、専用の連続した主記憶装置に見えるように提供する。
この技術により、物理的な主記憶装置に加えてハードディスク装置等の補助記憶装置を併用すれば、物理的な主記憶装置よりも大きな仮想メモリを提供する事ができる。またアプリケーション・プログラム側は、物理メモリ上のアドレスを意識しなくて良いため、マルチタスクの実現が容易である。このため現代のオペレーティングシステムの多くが仮想記憶をサポートしている。
仮想的に与えられたアドレスを<b data-parsoid='{"dsr":[478,490,3,3]}'>仮想アドレス (virtual address) または<b data-parsoid='{"dsr":[512,524,3,3]}'>論理アドレス (logical address)、実記憶上で有効なアドレスを<b data-parsoid='{"dsr":[556,568,3,3]}'>物理アドレス (physical address) または<b data-parsoid='{"dsr":[591,602,3,3]}'>実アドレス (real address) という。仮想アドレスの範囲を<b data-parsoid='{"dsr":[632,650,3,3]}'>仮想アドレス空間、物理アドレスの範囲を<b data-parsoid='{"dsr":[661,675,3,3]}'>物理アドレス空間</b>という。
概要
仮想記憶の実装（仮想記憶方式）には、大きく分けてセグメント方式とページング方式の二種類がある。ちなみに68000システムでは、68451（セグメント方式）と、68851（ページング方式）のメモリ管理ユニット (Memory Management Unit、MMU) が準備されていた。
一般にページング方式の方がよく使われている。これにより、メモリスワッピング（あるいは<b data-parsoid='{"dsr":[910,926,3,3]}'>匿名メモリページング）と仮想記憶が結びつけられる。メモリスワッピングとは、一次記憶装置内のメモリページを二次記憶装置(の<b data-parsoid='{"dsr":[990,1004,3,3]}'>スワップファイル</b>あるいは<b data-parsoid='{"dsr":[1008,1029,3,3]}'>スワップパーティションと呼ばれる場所)に書き出して、より高速な一次記憶装置を他のプロセスが使えるように解放することである。
「仮想記憶」という用語は「メモリスワッピング」と混同されることが多い。これはWindows系のオペレーティングシステム (OS) がメモリスワッピングの可 / 不可を設定する項目を "virtual memory" と称していることも一因と考えられる。実際、Windowsはその "virtual memory" を不可としても、ページング方式と仮想記憶を使用している[1][2][3]。Windows XP日本語版では、"システムのプロパティ"→"詳細設定"→"パフォーマンスオプション"→"仮想メモリ"のことである。
Unix系システムでもスワップファイルやスワップパーティションなしで仮想記憶を使用することが可能である。従って主記憶装置以上の大きな記憶領域を仮想的に使用できるようにすることは仮想記憶の主な目的ではあるが、本質ではないとも言える。本質は、不連続な物理メモリ領域を連続な仮想メモリ領域にマッピングすることであり、複数のプロセスがそれぞれ固有の連続な仮想メモリ領域を割り当てられる点である。これによって他のプロセスのことを気にせずに動作できる環境が提供されている。そういった意味で、仮想機械がゲストOSに対して提供していることと、仮想記憶がユーザープロセスに提供していることは等価である。仮想記憶は物理メモリのフラグメンテーションを隠蔽することでアプリケーションのプログラミングを容易にする。すなわちカーネルに記憶階層の管理を委任することで、プログラムが明示的なオーバーレイの制御を行う必要性を排除している。
技術的には、仮想記憶を使うことにより、ソフトウェアが動作するメモリアドレス空間のサイズとアドレス範囲は当該コンピュータの物理メモリ領域には必ずしも縛られなくなる。仮想記憶を適切に実装するには、CPU（あるいはそれに付随するデバイス）がOSに対して仮想メモリを物理メモリにマップする（対応付ける）手段を提供し、主記憶（物理メモリ）に対応していない仮想アドレスにアクセスしたことを検出する手段を提供して必要なデータをスワップインできるようにしなければならない。CPUの支援なしで仮想記憶を提供することも可能だが、その場合は上述の機能を提供するCPUをエミュレートするだけであって、本質的には同じことである。アドビシステムズの一部のアプリケーションのように、アプリケーションプログラムが自前で仮想記憶機構を持つ例もある。
ソフトウエアによって仮想記憶を実現する事は、本来ハードウエアで実現できる機能をあえてソフトウエアで行おうとしているので一見非効率に思える場合がある。しかし、この見方は場合によっては誤りである。なぜならページ方式やセグメント方式では仮想アドレス空間に対する広がりを持たせているので、例えば画像などを扱う場合には単にアドレス空間方向への広がり＝すなわち水平方向への広がりしか持たない。だが画像に対する操作は、特定の2次元空間＝面的な広がりを持っている。ここにソフトウエアで仮想記憶を実施するに当たって、面的な広がりをメモリ参照の局在性としてみなすと、単なる仮想記憶よりも性能の向上が期待できる。
背景
まず、以下の議論の前提とする、簡略化した典型的な記憶装置の階層構造のモデルを述べる。
CPU内のレジスタ。CPUのクロック速度で動作するため、最も高速である。レジスタの使用は一般に呼出規約の制限下にあり、コンパイラによる管理に任せられる。大きな容量はないため（32ビットあるいは64ビット、現代的なマイクロプロセッサでは16本〜普通は100本前後）次の計算が使えるように明け渡す必要があり、データを長期間保持させることは通常はできない[注 1]。内部的にはレジスタ・リネーミングを行っていることもある。
CPU内かCPUに隣接したキャッシュメモリ。通常はある種のSRAMを使用。レジスタに次いで高速であるが、容量は128kバイト〜数メガバイト程度に留まる。近年では1次キャッシュから3次キャッシュまでもがオンダイという場合もあり、ここで示した容量などに特に拘る意味は無い。
主記憶装置（通常はDRAMを使用）は、プログラミング上CPUが直接リード/ライトできる。キャッシュメモリに比べると速度は落ちるものの、より大きな容量（数百メガバイト〜数ギガバイト）を装着できる。
補助記憶装置としての磁気ディスク装置は、機械的な動作を伴うため、主記憶装置と比べても桁違いに低速だが、数十ギガバイト～数十テラバイト以上と容量が大きい。
主記憶装置とキャッシュメモリのどちらを使用するかは、一般にハードウェアに任せられているため、プログラマからはどちらも同じ物理メモリとしてしか見えない（しかし性能への影響は大きいので、高性能計算など、キャッシュヒット率を考慮してコードを書くこともある）。ハードウェアの動作としては、まずキャッシュメモリをアクセスし、さらに必要ならキャッシュメモリと主記憶とのやり取りを行う。
多くのアプリケーションは、情報（実行ファイルの命令列やデータ）がなるべく物理メモリ上に格納された状態でアクセスできることを要求する。これは特に見かけ上並列に複数のプロセス/アプリケーションを同時実行するオペレーティングシステムでは重要となる。全実行中プログラムが必要とする物理メモリ量が実装されている物理メモリ量より大きい場合、当然の結果として情報の一部をディスクに退避し、必要に応じてその内容を物理メモリに戻して使用することになる。しかし、これを実現する手法は様々である。
ひとつの方法として、アプリケーション自身が物理メモリ上に置くべき情報の範囲を決定し、補助記憶装置との情報のやりとりも制御することが考えられる。プログラマはプログラム（およびデータ）のどの部分が現時点で必要か不必要かを判断し、それらの領域の物理メモリへのロード（あるいはアンロード）を必要に応じて行わなければならないだろう。この手法の欠点は、各アプリケーションのプログラマがそのような設計/実装/デバッグに時間を費やす必要がある点であり、アプリケーションそのものに集中できなくなってプログラミング効率が低下する。また、あるプログラマがある時点で物理メモリ上に置くべきデータなどを決定しても、それが例えば、どうしても全物理メモリが必要となった場合など、他のプログラマの決定と衝突してしまう危険がある。仮想記憶</b>が普及する以前のオーバーレイ方式がほぼこれに相当する。
別の方法として、データの参照をポインタではなく何らかのハンドルで行う方式である。OSはそのようなハンドルと対応するデータを物理メモリにロードしたり、逆に補助記憶装置に移したりする。この方式の欠点は、アプリケーションのコードが非常に複雑になる点、アプリケーションがうまく振舞わなければならない点（必要なデータを物理メモリにロックする機能が必要になるだろう）、標準ライブラリが大きなメモリを確保しておいてアプリケーションのメモリ確保要求に応えるという機能を使えなくなる点などが上げられる。この方式の既存の例としてはMicrosoft Windows 3.1が有名である。
現代の解決策は仮想記憶方式の使用である。特別なハードウェアとOSの組合せにより、主記憶容量が大きくなったように見せ、各プログラムが自由気ままに空間を広げて使用することを可能にする。動作中の他のソフトウェアからは、仮想記憶機構の内部の動きは見えない。一般に仮想的な主記憶容量はほとんどどんな大きさにもできる。ただし、アドレスそのもののサイズによる制限がある。32ビットシステムでは、仮想アドレス空間サイズは全体で 232バイト、つまり4ギガバイトである。64ビットの場合、アドレスは64ビットや48ビットといったさらに大きなものになっている。多くのオペレーティングシステムは仮想アドレス空間全体をアプリケーションに使わせることはなく、一部をカーネル空間にすることでカーネルからユーザ空間に容易にアクセスできるようにしている。ただし、これは絶対必要な機能ではなく、OSによっては仮想空間全体をユーザー空間としている。
仮想記憶によってアプリケーションプログラマの仕事はずっと単純になる。アプリケーションが必要とするメモリ容量を気にする必要はなく、必要なサイズの主記憶が使えるように見え、仮想アドレス空間全体の好きな場所にデータを配置することができる。プログラマは主記憶と補助記憶の間でデータをやりとりするのを気にしなくてもよい。もちろん、プログラマが大量のデータを扱う際の性能を考慮しなければならない場合、アクセスするデータがなるべく近いアドレスに配置されるよう注意して、ある時点で必要なメモリ量を減らし不要なスワッピングを回避しなければならない。
仮想記憶はコンピュータ・アーキテクチャの重要な部分であり、その実装にはハードウェア的サポートがほぼ[注 2]必須である。メモリ管理ユニット (MMU) と呼ばれる部分であり、性能の都合もありCPUに組込まれることも多い。1960年代までのメインフレームの大部分<!--と1980年代中ごろのパーソナルコンピュータ（[[DOS (OS)|DOS]]）<ref>{{Cite web |url= http://support.microsoft.com/kb/32905 |title=Windows のバージョン履歴 |accessdate=2008-12-03 |publisher=Microsoft |date=Last Review: July 19, 2005 }}</ref>--><!-- ← 出典付きの記述なのでコメントとして残すが、MS-DOS環境に保護が無いのはCPUおよびマシンがそうだったからで、DOSの責ではない -->は、基本的には仮想記憶をサポートしていなかった。1960年代のメインフレームで例外といえるものを以下に挙げる。
Atlas Supervisor（Atlas）
MCP（バロース B5000）
MTS、TSS/360、CP/CMS（IBM System/360 Model 67）
Multics（GE 645）
TSOS（RCA Spectra 70/46）
1980年代のパーソナルコンピュータで仮想記憶をサポートした例として Apple Lisa がある。
歴史
仮想記憶技術が開発される以前、1940年代から1950年代のプログラマは2レベルの記憶装置（主記憶あるいはRAMと、磁気ディスク装置あるいは磁気ドラムメモリといった二次記憶）を直接管理する必要があり、大規模プログラムではオーバーレイなどの技法が使われていた。従って仮想記憶は、主記憶を拡張するためだけでなく、そのような拡張をプログラマが可能な限り容易に扱えるように導入された[4]。マルチプログラミングやマルチタスクを実装した初期のシステムは、メモリを複数のプログラムに分割するのに仮想記憶を使っていない。例えば初期のPDP-10はレジスタを使ってマルチタスクを実現していた。
ページング方式はマンチェスター大学のAtlas上で開発された。1万6千ワードの磁気コアメモリの一次記憶と9万6千ワードの磁気ドラムメモリによる二次記憶を制御するものである。最初のAtlasは1962年に稼働開始したが、ページングのプロトタイプは1959年に開発されている[4](p2)[5][6]。なお、ドイツの初期の情報工学者 Fritz-Rudolf Güntsch (後に Telefunken TR 440 というメインフレームを開発)は 1957年の博士論文 Logischer Entwurf eines digitalen Rechengerätes mit mehreren asynchron laufenden Trommeln und automatischem Schnellspeicherbetrieb(複数非同期ドラム装置と自動高速メモリモードを持つデジタル計算機の論理概念)で仮想記憶のコンセプトを発明していたと言われている。
1961年、バロースはセグメント方式で仮想記憶をサポートした世界初の商用コンピュータ B5000 をリリースした[7]。
1965年にMITが開発したMultics以降、仮想記憶は本格的に採用され始めた。
コンピュータ史上の多くの技術と同様、仮想記憶にも様々な曲折があった。安定した技術と見なされるまで、仮想記憶の様々な問題点を解決しようとするモデルや理論が開発され実験がなされた。仮想アドレスを物理アドレスに変換するハードウェア機構の開発も必然的だったが、初期の実装ではそれによってメモリアクセスが若干遅くなった[4]。システム全体を対象とするアルゴリズム（仮想記憶）は従来のアプリケーション単位のアルゴリズム（オーバーレイ）よりも非効率ではないかという懸念もあった。1969年、商用コンピュータでの仮想記憶に関する論争は事実上終結した[4]。David Sayre 率いるIBMの研究チームが仮想記憶システムが手動制御システムよりも優位にあることを示したのである。
1970年、IBMはSystem/370シリーズのOSであるDOS/VS、OS/VS1、OS/VS2（後のMVS）で仮想記憶をサポートした。OS/VS1はシングルタスクの仮想記憶で、マルチタスクには従来通りユーザーによるマルチプログラミングが必要であったが、OS/VS2はマルチタスクの仮想記憶（複数の仮想アドレススペース）をオペレーティングシステムの機能としてサポートした。以後の各社メインフレームでは仮想記憶が一般的となる。
ミニコンピュータで初めて仮想記憶を導入したのは、ノルウェーのNORD-1である（1969年）。1976年、DECのミニコンピュータ VAXシリーズのOSであるVMSで仮想記憶をサポートした。
しかし、1980年代の初期のパーソナルコンピュータでは仮想記憶は採用されていない。これは当時のマイクロプロセッサの性能や機能の問題もあるし、個人用のコンピュータに仮想記憶が必要になると見なされていなかったという面もある。当時の主流はバンク切り換えによるメモリ増設だった。x86アーキテクチャで仮想記憶が導入されたのは、Intel 80286 によるプロテクトモードが最初だが、セグメント単位のスワッピングはセグメントが大きくなると性能が悪くなるという問題があった。Intel 80386 では既存のセグメント方式の下層にページング方式を実装し、ページフォールトによるページングが可能となった。しかしセグメントディスクリプタのロードは時間のかかる処理だったため、OS設計者はセグメントを使わずページングだけを使うようになっていった。仮想記憶が導入されたのは、OS/2（1987年）、Microsoft Windows 3.0 （1990年）、MacintoshのSystem 7（1991年）、Linuxカーネル 0.11+VM（1991年）などが最初である。
ページング方式
仮想記憶は、必須ではないものの通常ページング方式を使って実装される。ページングでは仮想アドレスを表すビット列の下位ビット列部分はそのまま物理アドレスの下位ビット列部分として使われる。上位ビット列部分はアドレス変換テーブル（群）のキーとして使用され、それによって実際の物理アドレスの上位ビット部分を得る。
このため、サイズが2の冪乗の仮想アドレス空間の連続したアドレス範囲が、対応する連続な物理アドレス範囲に変換される。そのような範囲で参照されるメモリを<b data-parsoid='{"dsr":[10626,10635,3,3]}'>ページ</b>と呼ぶ。ページサイズは512バイトから8192バイトが一般的であり、特に4096バイトが最もよく使われるが、特殊用途として4Mバイトやそれ以上のサイズのページも使われることがある。
オペレーティングシステムは、ページテーブルと呼ばれるデータ構造にアドレス変換テーブル、つまり仮想ページ番号と物理ページ番号のマッピング情報を格納する。
あるページが使用不可とされている場合（物理メモリに対応しておらず、スワップ領域に内容がある場合など）、CPUがそのページ内のメモリ位置を参照しようとしたとき、ハードウェアの機構がオペレーティングシステムに、一般にページフォールトと呼ばれる例外を通知する。これにより実行コンテキストはオペレーティングシステム内の例外処理ルーチンにジャンプする。そのページがスワップ領域にあるなら、そのルーチンは「ページスワップ」と呼ばれる処理を実行して必要なページの内容を物理メモリに読み込む。
ページスワップ操作には一連の段階がある。まず、メモリ上のページを選択する。例えば、最近アクセスされておらず、なるべくならスワップ領域を含む何らかのディスクから読み込まれたままで変更されていないページを選択する（詳細はページ置換アルゴリズムを参照）。そのページが変更されている場合、その内容をスワップ領域に書き出す。次に必要とされている、例外発生時に参照しようとしていた仮想アドレスに対応するページの情報を読み込む。ページの読み込みが完了したら、その物理メモリの内容更新に応じて仮想アドレスから物理アドレスへの変換テーブルを更新する。ページスワップが完了すると、例外処理を完了し、元のプログラムの実行が例外発生箇所から再開されて何事もなかったかのように処理が続行される。
仮想ページに何も割り当てられていないために使用不可となっている可能性もある。そのような場合、未使用、あるいはスワップアウトして未使用にした物理ページを割り当て、OSによってはその内容をゼロクリアする。ページテーブルはそれに対応して更新され、上述の場合と同様にプログラムが再開される。
セグメント方式
バロース B5000[8] などのシステムは、ページング方式ではなくセグメント方式を使い、仮想アドレス空間を可変長のセグメントに分割する。その場合、仮想アドレスはセグメント番号とセグメント内オフセットから成る。Intel 80286の保護モードにもそのようなセグメント方式があったが、ほとんど使われなかった。セグメントとページは、セグメントをページに分割するという形で同時に使用できる。そのようなメモリ構成のシステムとして、MulticsやSystem/38がある。その場合の基本はページングであり、セグメントはメモリ保護に使われる[9][10][11]。
Intel 80386 とその後のIA-32プロセッサでは、セグメントをページ化された32ビットのリニアなアドレス空間に置く。セグメントによる管理と、ページ単位による管理の、2段階のシステムとなっている。しかし、複数のセグメントを活用しているOSは少なく（ただし、研究レベルまで含めればそんなに少なくない）、単純にベースアドレスを全てゼロとして、範囲のみ指定した最小限必要なだけのセグメントと、ページングのみを使っていることが多い。

単一レベル記憶

これは、mmapやWin32のMapViewOfFileのような機構とは異なる。mmap等ではファイルは任意の位置にマッピングされる可能性があるため、ファイル間のポインタは使えないためである。Multicsでは、ファイル（複セグメントファイルの場合はセグメント）はセグメント機構を通してアドレス空間にマッピングされる。そのためファイルは常にセグメント境界にマッピングされる。ファイルのリンク部分にはポインタが並んでおり、そのポインタをレジスタにロードしたり間接参照したりするとトラップが発生する。未解決のポインタにはセグメント名を示す値とセグメント内オフセットがある。トラップハンドラは対応するセグメントをアドレス空間にマッピングし、ポインタのセグメント識別子部分をセグメント番号に書き換えるので、2度目以降はそのポインタにアクセスしてもトラップが発生しなくなる[12]。この方式ではリンケージエディタが不要であり[4]、同じファイルを複数のプロセスが異なる位置にマッピングしても問題なく機能する[13]。
詳細

仮想アドレスから物理アドレスへの変換はメモリ管理ユニット (MMU) というハードウェア装置によって実装されている。これはCPUに内蔵されたモジュールの場合もあるし、外付けでCPUに密結合された別のチップの場合もある。これを<b data-parsoid='{"dsr":[15240,15256,3,3]}'>動的アドレス変換機構 (DAT: Dynamic Address Translation) と呼ぶ。
OSは、プログラムの仮想アドレス空間のどの部分を物理メモリに保持するかを決定する。OSは、MMUが使用する仮想アドレスから物理アドレスへの変換テーブルも管理する。さらに仮想メモリ例外が発生したら、OSはそれを解決するために物理メモリ領域を確保し、必要なら元の内容をディスクに追い出した上で新たに必要とされている情報をディスクから持ってきて、変換テーブルを更新し、例外発生したソフトウェアの実行を再開する。
多くのコンピュータでは、この変換テーブルは物理メモリに格納されている。従って仮想メモリを参照すると、本来の参照以外に変換テーブルの参照が（変換テーブルの構成によっては複数回）発生する。このアドレス変換による性能低下を低減するため、ほとんどのMMUはよく使われる仮想ページに高速にアクセスできるよう、最近使われた仮想アドレスとそれに対応する物理アドレスを保持しておくテーブルを持っている。これをトランスレーション・ルックアサイド・バッファ（TLB）と呼ぶ。参照アドレスがTLB内に格納された変換テーブルでカバーされていれば、余分な変換テーブルの参照をせずに、高速に変換を行うことができる。ただし、TLBは高価な装置のためテーブルの大きさが限られており、目標の仮想アドレスが見当たらない場合は物理メモリ上の変換テーブルを参照してアドレス変換が行われる。
プロセッサによっては、この一連の処理がハードウェア内で行われる。MMUは物理メモリ上の変換テーブルから必要な変換内容を持ってくるので、ソフトウェア側は余分な処理を必要としない。別の種類のプロセッサでは、オペレーティングシステムの介在が必須である。TLBに必要な変換内容がない場合、例外が発生し、オペレーティングシステムがTLB内の1つのエントリを必要な変換テーブルの内容と置き換え、当初のメモリ参照を行った命令から実行が再開され、再度TLBを参照して変換を行う。
仮想記憶をサポートするハードウェアの多くはメモリ保護もサポートしている。MMUはメモリ参照の種類(リード、ライト、実行)やメモリ参照時のCPUモードによって扱いを変える機能を持っていることもある。これによってオペレーティングシステムは自身のコードとデータ（例えばアドレス変換テーブルなど）を問題のあるアプリケーションプログラムの不正なメモリアクセスから保護したり、アプリケーションを相互に保護したり、アプリケーション自身の不正動作（例えば自身のコード部分に書き込もうとするなどの動作）から保護したりする。
仮想アドレス空間管理

各プロセスの仮想アドレス空間には、そのプロセスが使用するコードやデータが配置される。ページング方式であれ、セグメント方式であれ、仮想アドレス空間内で使用している範囲の管理と制御が仮想記憶機構として必須である。例えば、実行ファイルの内容を仮想メモリ上に配置する領域、スタックを配置する領域などがある。このような領域を<b data-parsoid='{"dsr":[16640,16651,3,3]}'>セグメント</b>と呼ぶ。セグメント方式のセグメントと似ているが、純粋に仮想的なオブジェクトである。実行ファイルを配置する領域は必ずしも連続ではない。プログラムのコード部分とデータ部分を分離して配置するのが一般的で、前者を<b data-parsoid='{"dsr":[16753,16768,3,3]}'>テキストセグメント</b>もしくは<b data-parsoid='{"dsr":[16772,16786,3,3]}'>コードセグメント、後者を<b data-parsoid='{"dsr":[16790,16804,3,3]}'>データセグメント</b>と呼ぶ。Unix系システムや Windows では、一般的にデータセグメントの一部としてBSSセクションとヒープ領域を含む。BSSセクションにはプロセス起動時に0に初期化される静的変数を配置する。初期値が0の静的変数を別扱いしているのは、読み書きが発生するまで0で初期化するのを後回しに出来るようにするための高速化のテクニックである。Unix系システムではヒープ領域はデータセグメントの末尾に配置され、brk() 関数などでデータセグメントのサイズを変えることでヒープ領域のサイズを変えられるようにする。各セグメントはマッピングしているオブジェクトが何であるか、その領域へのアクセス権などを属性情報として保持する。
テキストセグメントはファイルシステム上の実行ファイルの一部と完全に対応しており、書き換えられることもない。従って、マッピングしているオブジェクトは実行ファイルであり、アクセス属性は「リードオンリー」となる。データセグメントやスタックは一時的な存在であるため何かをマッピングしているわけではない。そこでこれらは匿名ファイル（Anonymous File）をマッピングしているものとして管理される。匿名ファイルをマッピングしているセグメントに対応するページを匿名ページと呼び、これがスワッピングの際にスワップ領域に書き出される。データセグメントは当初は実行ファイルの一部と対応しているが、書き込み可能な属性が設定されている。ページング方式の場合、データセグメント内の内容が更新されたページはページ単位で匿名ページへと属性変更される。
exec() システムコールなどで新たにプロセスの仮想アドレス空間を設定した当初は、基本的にこのような仮想アドレス空間を管理するデータ構造がカーネル内に作成されるだけで、実際の実行ファイルの内容はロードされない。Unix系システムでは、exec() システムコールからユーザ空間に制御が戻された瞬間にページフォールトが発生し、そこで初めてページ単位に実行ファイルの内容がロードされる。ただし、性能向上目的で事前にマッピングを作成する場合もある。
各プロセスの仮想アドレス空間のアドレス範囲は同じでありオーバーラップしているのが一般的である。これを<b data-parsoid='{"dsr":[17825,17837,3,3]}'>多重仮想記憶</b>と呼ぶ。MMUは現に実行中のプロセスの仮想空間のみを認識する。コンテキストスイッチでプロセスを切り替える際、MMUに対して仮想アドレス空間の切り替えも指示する必要があるが、その方式はアーキテクチャによって様々である。
同じプログラムを実行するプロセスが複数存在する場合、多重仮想記憶ではそれぞれが同じ仮想アドレスに実行ファイルをマッピングしていながら、それぞれ独立した仮想空間を使用する。このため、実行ファイルを配置する仮想アドレスはどのプロセスでも同じにすることができ、実行ファイル自体に配置すべきアドレスを格納しておくようになっているのが一般的である。また、それぞれのプロセスが実行ファイルのテキストセグメントをマッピングするのに使う物理メモリは共有することができる。他にも mmap() でファイルをマッピングする場合や共有メモリ機能でプロセス間の通信を行う場合、マッピングされる物理メモリが共有される。
なお、アーキテクチャによっては、多重仮想記憶がオーバーラップしていると捉えず、全仮想空間がフラットに並んだ巨大な仮想空間を想定することもある。この場合、仮想空間識別番号が巨大な仮想空間のアドレスの一部と考えられる。もっとも、これは単にモデル化の手法が違うだけで実装に大きな違いがあるわけではない。実際、各ユーザープロセスが自分の仮想空間識別番号以外の仮想空間にアクセスすることはできない。
実装例
Ferranti Atlas
1962年に世界で初めてページングをサポートしたコンピュータは[14][15][16]、フェランティ、マンチェスター大学、Plesseyが共同開発した。このマシンには連想メモリがあり、1エントリに512ワード長のページが対応している。スーパーバイザ[17]は非同値割り込みを処理し[注 3]、磁気コアメモリと磁気ドラムメモリ間のページ転送を管理する。特筆すべきは、世界初の仮想記憶システムであるにもかかわらず、難しさなどから後のシステムでも実現例のあまり多くない、プログラムに単一レベル記憶を提供している[18]ことである。
Windows 3.x と Windows 9x
Windows では、1990年のWindows 3.0から仮想記憶をサポートしている。マイクロソフトはWindows 1.0とWindows 2.0での失敗を受け、OSへのリソース要求を削減するために仮想記憶を導入した。
マイクロソフトはスワップファイルを「仮想メモリ」と呼んでおり、混乱の元となっている。この概念に馴染みのない初心者はこの定義に疑問を持たず、仮想記憶（仮想メモリ）といえばWindowsのスワップファイルの大きさの調整のことだと思っている。実際には全てのプロセスは固定の変更不可能な仮想記憶空間を持っている（32ビットの場合、一般に2GB）。ユーザーは単にページング専用のディスク容量を変更することしかできない。
Windows 3.xには隠しファイルとして386SPART.PARまたはWIN386.SWPがあり、それらがスワップファイルとして使われる。通常、ルートディレクトリにあるが、WINDOWSなど他のディレクトリに置くこともある。そのサイズはコントロールパネルで設定される「仮想メモリ」サイズで決定される。ユーザーがこのファイルを削除したり移動させたりすると、次回Windowsを起動したときにブルースクリーンが表示され、エラーメッセージが表示される（英語では "The permanent swap file is corrupt"）。
Windows 95、Windows 98 / 98 SE、Windows Me でも同様の仕組みになっている。スワップファイルの大きさはデフォルトでは物理メモリ量の1.5倍であり、最大で物理メモリの3倍まで拡張できる。
Windows NT系
NT系のWindows（Windows 2000、Windows XP、Windows Vista、Windows 7、Windows 8 / 8.1、Windows 10 ）などは、pagefile.sysというファイルを使用する。このファイルのデフォルトの位置はWindowsをインストールしたパーティションのルートディレクトリである。Windowsは任意のドライブの空き領域をページファイルとして使用できる。XPまでのWindowsではシステムクラッシュ時にカーネルまたは全メモリをダンプする設定にしている場合、ブートパーティション（Windowsがインストールされたドライブ）にこのファイルを置く必要があった（ダンプ先として使用するため）。リブートすると、システムがダンプを通常のファイルに移す[19][20]。
ページングファイルのサイズには初期サイズと最大サイズがあり、ページングファイルが不足するとページングファイルは最大サイズまで拡張される。拡張されたページングファイルは再起動するまで小さくならない。
ページングファイルがあると、積極的なクリーニングにより物理メモリで足りる場合であってもページファイルへの書き出しが行われる。積極的なクリーニングは仮想記憶を実装するページ置換アルゴリズムの一つで、CPUの余剰時間を使ってページ内容をディスクに書き出しクリーンな状態にしておき、ページが必要になった時に短時間でページを再利用する手法である。勿論クリーンな状態のページ内容そのものが必要になった時には、ページファイル上のページ内容を無視するだけで良いので、オーバーヘッドは発生しない。
Windows XP以降ではページファイルを使用しないオプションが選択できる。もちろんこれは物理メモリの容量が十分に足りている場合（かつ、ページファイル必須アプリケーション運用を必要としない場合）にのみ選択すべきオプションである。ページファイルを使用しない場合、ほとんど使用されない常駐ソフトなどのデータをスワップアウトすることができなくなるので、キャッシュとして使える物理メモリの空き領域が減ってパフォーマンスが低下することがある。
フラグメンテーション
ページングファイルのサイズには初期サイズと最大サイズがあり、ページングファイルが不足するとページングファイルは最大サイズまで拡張される。徐々に拡張された場合、フラグメンテーションが起き、性能に悪影響を与えることがある[21]。これに対する助言としては、ページファイルのサイズを固定することでOSがそのサイズを変更できないようにするという対策がある。ただし、ページファイルが拡張されるのは全部を使い切ったときで、デフォルト設定では物理メモリの150%の量になっている[22]。したがって、ページファイルに対応した仮想記憶の要求が物理メモリの250%を越えないと、ページファイルは拡張されない。
ページファイルの拡張によるフラグメンテーションは一時的なものである。拡張された領域が使われなくなれば（遅くとも次回の再起動時には）、追加で確保されたディスク領域は解放され、ページファイルは本来の状態に戻る。
ページファイルの大きさを固定にすると、物理メモリとページファイルを合わせた以上のメモリを要求するアプリケーションがある場合に問題となる。その場合、メモリ確保要求が失敗し、アプリケーションやシステムプロセスが異常終了する可能性がある。ページファイルを拡張可能にすべきだという根拠として、ページファイルが先頭から順にシーケンシャルにアクセスされることはなく、ページファイルが連続領域になっていること性能上の利点はほとんどないという見方もある。いずれにしても、メモリを大量に使うアプリケーションを使用するならページファイルは大きい方がよく、ページファイルを大きくしてもディスク容量がそれに割かれる以外のペナルティはない。
現代的な仕様のシステムでは余分にディスク領域を使用しても問題はない。例えば、メモリが3GBのシステムで6GBの固定スワップファイルを使用するとしても、HDDが750GBなら問題はないし、メモリが6GB、スワップファイルが16GB、HDDが2TBなら、これも問題はない。どちらの場合もスワップファイルとして使用する領域はHDDの1%に満たない。
ページファイルは任意のドライブに作成する事ができる。これはUNIX同様スワップ専用のパーティション割り当てが行えるのに等しい。またページファイルはストライピングが行われるので複数のハードディスクドライブに小分けにしてページファイルを作成すると、ページング速度が向上する。
物理メモリ以上のメモリを常に使うような使い方をする場合、ページファイルのデフラグメンテーションをすることで性能が改善する可能性もある。しかし、根本的には物理メモリを追加する方が性能改善に役立つ。
Mac OS
Mac OSはSystem 7から「仮想メモリ」として実装される。当時はコントロールパネルでメモリサイズ（メインメモリのサイズを加算した値）を指定し機能を入にすることで使用できるようになる。すると起動ディスクに隠しファイルとしてスワップファイルが作成される。スワップファイルは指定したメモリサイズの大きさとなり、これ以上は増えない。この頃の仮想メモリは使用しているかどうかでプログラムの動作が不安定になることがあった。そのため、プログラムのパッケージや説明書には仮想メモリの設定を確認させる記述が見られる。
UNIXとUnix系システム
UNIXおよびUnix系OSでは、ハードディスクのパーティションを丸ごとスワップに使用することが多く、そのようなパーティションをスワップパーティションと呼ぶ。ドライブを1個丸ごとスワップパーティションとすることもある。そのようなドライブをスワップドライブなどと呼ぶ。スワップパーティションしかサポートしないシステムもあるが、ファイルへのスワッピングもサポートするシステムもある。フラグメンテーション問題を回避して性能を維持するためにもパーティションの使用が推奨されている。また、スワップパーティションを使うと、スワップ領域をディスク内の最も高速アクセス可能な場所に配置できる。最近のハードディスクでは、先頭の方がよいとされている。
Linux
Linuxのユーザプロセスから見れば、大局的にはメモリはCPUキャッシュ (L1, L2, …)、メインメモリ、ファイルの順に階層化されており、上位（CPUにより近い）メモリは下位メモリのキャッシュに過ぎない。実行可能なファイルや共有ライブラリのテキストセグメントやmmapで明示的にマップされる名前付きファイルに対して、スワップエリアはスタックやヒープを保持する名無しファイルである（データセグメントやBSSセグメントは読み出し専用の実行可能ファイルや共有ライブラリファイルからプロセス固有の読み書き可能な無名ファイルに展開・コピーされると考えられる）。もちろん、実際にはカーネルは性能維持のためにスワップエリアの使用とアクセスを最小限にするような最適化の努力を払う（メインメモリに余裕があればスワップエリアには書き出さず「キャッシュ（メインメモリ）」だけにしか情報が保持されない）。
2.6のLinuxカーネルではスワップファイルはスワップパーティションと同程度の性能である[23][24]。カーネルはスワップファイルの存在するディスク上の位置を把握しており、バッファキャッシュやファイルシステムのオーバヘッドを回避して直接ディスクにアクセスする[24]。レッドハットはスワップパーティションの使用を推奨している[25]。スワップパーティションの場合、ディスク上の位置を決めることができるので、スループットが最も高い場所に置くことができる。一方スワップファイルは管理の柔軟性という点でスワップパーティションに優っている。例えば、スワップファイルは任意のドライブ上に置くことができ、どんな大きさにもでき、必要に応じた追加や変更が容易であるだけでなく、ネットワークを介して外部ホスト上のリモートファイルを使うことも可能である。一方、スワップパーティションは一度位置と大きさを決めたら、ドライブ全体のパーティショニングをやり直さないと変更できない。
Linuxは事実上無制限な個数のスワップデバイスをサポートし、それぞれに優先度を設定できる。オペレーティングシステムが物理メモリをスワップアウトする場合、最高優先度のデバイスの空き領域から使っていく。同じ優先度に複数のデバイスがある場合、それらは RAID 0と同様の使い方をされる。これによって並列的に複数のデバイスにアクセスするので性能が向上する。従って優先度の設定には注意が必要である。例えば、同じドライブ上の複数のスワップ領域を同じ優先度にするのは得策ではない。また、高速なデバイスを高優先度に設定するのが性能的に有利である。
Linuxシステムでスワップを追加するには、その前にスワップ領域を作成しなければならない。パーティションならば一般のパーティション作成ツールが使用できる。通常ファイルの場合、ddコマンドと /dev/zeroを使って内容がゼロのファイルを作ることができる。作成したスワップ領域はmkswap filename/deviceでフォーマットし、swaponおよびswapoffコマンドでON/OFFを制御する。
Windowsとは違い、物理メモリに入りきらない場合のみ、スワップが利用される。これは積極的なクリーニングが実装されていないためで、ページングが開始された時システムは著しい速度低下を起こす（スラッシング）。しかし、これは物理メモリが飽和状態を続けている場合さほど深刻では無い。メモリが飽和すればあまり利用されないページは自ずとハードディスクに追い出され、物理メモリには有用なページが残される様になる。
macOS
macOSではUnix系をベースとしたことで仮想メモリは常に使用するようになっており、複数のスワップファイルを使用できる。デフォルト（かつアップルの推奨）ではルートパーティションに配置されるが、他のパーティションやデバイスに置くこともできる[26]。
コンピュータの起動時から64MBのスワップファイルが1つ作成されている。場所は/private/var/vm/以下で、swapfile<i data-parsoid='{"dsr":[28241,28246,2,2]}'>nという名前がつけられている（nは0からの数字）。容量が不足するとスワップファイルは自動的に追加される。swapfile1までは64MB、以降のスワップファイルのサイズは128MB、256MB...と8の倍数で増えるのが基本だが、メモリの最大容量・ハードディスクの空き容量の1/4・1GBのいずれか小さい方を選択し容量が決定する[27]。ひとつのスワップファイルが大きくなるのではなく複数のファイルが作成される。すなわちスワップファイルが4つなら64+64+128+256で合計512MBとなる。スワップファイルの場所はコマンドライン操作などで他のデバイスに変更できる。
スワップファイルを削除するアプリケーションも存在し、これを用いなくとも削除できる（ただし、削除後は再起動するのが望ましい）。また、一旦ログアウトしてからログインしなおすと再起動することなく削除できる。
macOSでは多重仮想記憶がサポートされ、仮想空間はプロセス毎に資源が分離されている。この違いが、Classic Mac OSとmacOSでの仮想記憶に対する信頼性の違いとなって現れている。
脚注

注釈

出典


参考文献
John L. Hennessy, David A. Patterson, Computer Architecture, A Quantitative Approach (ISBN 1-55860-724-2)
関連項目
仮想機械
メモリアドレス
アドレス空間
CPU設計
単一レベル記憶
ページング方式
ページテーブル
ワーキングセット
メモリ管理ユニット
キャッシュアルゴリズム
ページ置換アルゴリズム
セグメント方式
System/38
メモリ管理
メモリ保護
動的メモリ確保
プロテクトモード - x86 の仮想記憶が可能な動作モード
外部リンク
 by Michael T. Alexander in Advanced Topics in Systems Programming, University of Michigan Engineering Summer Conference 1970 (revised May 1971) - 仮想記憶とページングを含むリソース割り当て技法とスケジューリングについて、CP-67、TSS/360、MTS、Multics というメインフレーム用OSを比較している。
.
, mailing list discussion.