システムインタフェース

第 7 章 メモリ管理

この章では、アプリケーション開発者の観点から SunOS 5 仮想記憶を説明し、アプリケーション開発者が利用できる、固定記憶のシステムにはない仮想記憶の機能を示します。また、これらの機能を使用し、制御するために SunOS 5 で提供されているインタフェースを説明します。

仮想記憶の概要

(仮想記憶ではない) 固定記憶のシステムでは、プロセスの実アドレス空間は、システム主記憶のある部分を占有し、それに限定されます。

SunOS 5 の仮想記憶では、プロセスの実アドレス空間は、ディスク記憶装置のスワップパーティションにあるファイルを占有します (このファイルをバッキングストアと言います)。主記憶のページは、プロセスアドレス空間のアクティブな (または最近アクティブだった) 部分をバッファリングし、CPU が実行するコードとプログラムが処理するデータを提供します。

現在メモリにないアドレスが CPU によってアクセスされ、「ページフォルト」が発生すると、アドレス空間のページが読み込まれます。参照されているアドレスセグメントがメモリに読み込まれてページフォルトが解決されるまで実行は継続できないので、ページが読み込まれるまでプロセスは休眠します。

アプリケーション開発者にとって 2 つのメモリシステムの最も明瞭な違いは、仮想記憶によって、アプリケーションはより大きいアドレス空間を占有できることです。また、仮想記憶を使用すると、ファイル入出力が簡単で効率的になり、プロセス間のメモリ共用が非常に効率的になります。

アドレス空間とマッピング

バッキングストアファイル (プロセスのアドレス空間) は、スワップ空間だけに存在するため、UNIX 名前付きファイル空間に含まれていません。(このため、他のプロセスはバッキングストアファイルにアクセスできません。) しかし、1 つ以上の名前付きファイルの全部または一部をバッキングストアに論理的に挿入できるようにしたり、その結果を単一のアドレス空間として扱えるようにしたりするために簡単に拡張できます。このことを「マッピング」と言います。

マッピングを使用すると、読み取り可能または書き込み可能なファイルの任意の部分を論理的にプロセスのアドレス空間に含めることができます。プロセスのアドレス空間の他の部分と同様に、ページフォルトによって強制されるまで、ファイルのどのページも実際にはメモリに読み込まれません。その内容が修正された場合だけ、メモリのページはファイルに書き込まれます。そのため、ファイルの読み取りと書き込みは、完全に自動的かつ効率的に行われます。

複数のプロセスを 1 つの名前付きファイルにマッピングできます。こうすることで、効率的にプロセス間のメモリを共用できます。他のファイルの全部または一部をプロセス間で共用することもできます。

マッピングできない名前付きファイルシステムのオブジェクトもあります。たとえば、端末やネットワークデバイスファイルなどの記憶領域として扱うことができないデバイスなどです。

プロセスのアドレス空間は、アドレス空間にマッピングされるファイルの全部 (またはファイルの一部) によって定義されます。各マッピングは、プロセスが実行されるシステムのページ境界に合うように大きさと境界割り当てが調整されます。プロセスに関連付けられているメモリはありません。

プロセスのページは、一度に 1 つのオブジェクトだけにマッピングされますが、オブジェクトのアドレスは、多くのプロセスのマッピングの対象になることがあります。「ページ」という概念は、マッピングされるオブジェクトの属性ではありません。オブジェクトのマッピングは、プロセスがオブジェクトの内容を読み取るまたは書き込むための潜在的な機能だけを提供します。

マッピングによって、プロセスはオブジェクトの内容を直接アドレス指定できます。アプリケーションは、読み取りと書き込みによって間接的ではなく、使用する記憶領域の資源に直接アクセスできると便利な場合があります。たとえば、効率化 (不要なデータコピーの省略) と単純化 (読み取り、バッファの変更、書き込みではなく 1 つの操作で更新) などの利点があります。オブジェクトにアクセスし、アクセス中その識別情報を保つ機能は、このアクセス方式に固有で、この機能により共通のコードとデータを共用できます。

ファイルシステムの名前空間は、NFS を介して他のシステムから接続されているディレクトリツリーを含むので、ネットワークに接続されたファイルをプロセスのアドレス空間にマッピングすることもできます。

一貫性

複数のプロセスが 1 つのファイルを同時にマッピングするときに、メモリまたはファイルに含まれているデータを共用するかに関係なく、データ要素に同時にアクセスすると問題が発生することがあります。そのようなプロセスは、SunOS 5 で提供される同期メカニズムのいずれかによって協調できます。SunOS 5 で最も効率的な同期メカニズムは非常に軽量なので、スレッドライブラリに組み込まれています。その使用方法については、mutex(3T)condition(3T)rwlock(3T)、および semaphore(3T) の各マニュアルページを参照してください。

メモリ管理インタフェース

仮想記憶機能は、いくつかの関数の組み合わせによって使用し、制御します。この節は、これらの呼び出しを要約して説明し、それらの使用例を示します。

マッピングの作成と使用

mmap(2) は、プロセスのアドレス空間への名前付きファイルシステムオブジェクト (またはその一部) のマッピングを確立します。これは、基本的なメモリ管理機能で、非常に簡単です。まず、ファイルを open(2) し、次に適当なアクセスオプションと共用オプションで mmap(2) し、後は必要な作業を行います。

mmap によって確立されるマッピングは、指定されたアドレス範囲の以前のマッピングを置き換えます。

MAP_SHARED および MAP_PRIVATE フラグは、マッピングのタイプを指定します。いずれか 1 つを指定しなければなりません。MAP_SHARED を指定すると、書き込みがマッピングされているオブジェクトを修正します。変更を有効にするための追加操作は必要ありません。MAP_PRIVATE を指定すると、マッピングされている領域への最初の書き込みでページのコピーが作成され、すべての書き込みはそのコピーを参照します。実際に修正されたページだけがコピーされます。

マッピングのタイプは、fork(2) を実行した場合も保持されます。

mmap(2) 呼び出しに使用するファイル記述子は、マッピングが確立した後は開いたままにする必要はありません。ファイル記述子を閉じても、マッピングが munmap(2) によって取り消されるまで、または新しいマッピングで置き換えられるまで、このマッピングは有効です。

切り捨ての呼び出しによってファイルが短くされて、マッピングがファイルの終わりを越えてしまった場合は、ファイルの存在しない領域にアクセスすると、SIGBUS シグナルが発生します。

/dev/zero をマッピングすると、mmap で指定した大きさの仮想記憶のブロッキングに 0 を設定します。次のプログラムは、システムが選択したアドレスにスクラッチ記憶領域のブロッキングを作成する例を示しています。

int fd;
 	caddr_t result;

 	if ((fd = open("/dev/zero", O_RDWR)) == -1)
 		return ((caddr_t)-1);
 	result = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 	(void) close(fd);

デバイスまたはファイルは、マッピングによってアクセスした場合だけ役立つことがあります。この例としては、ビットマップ表示をサポートするフレームバッファデバイスがあります。この場合、表示管理アルゴリズムは、表示アドレスに直接ランダムアクセスできる場合に効率的に機能します。

マッピングの削除

munmap(2) は呼び出しプロセスから、指定したアドレス範囲内のページのすべてのマッピングを削除します。munmap は、マッピングされたオブジェクトには影響しません。

キャッシュ制御

SunOS 5 仮想記憶システムは、プロセッサのメモリがファイルシステムのオブジェクトからのデータをバッファリングするキャッシュシステムです。キャッシュの状態を制御または照会するためのインタフェースがあります。

mincore(2)

mincore(2) は、メモリページが指定された範囲のマッピングの対象となるアドレス空間にあるかどうか調べます。mincore がチェックした後でも、データを戻すまでの間にページの状態が変わる可能性があるため、戻された情報は古くなっていることがあります。ロッキングされたページだけがメモリに残っていることを保証されます。

mlock(3C)munlock(3C)

mlock(3C) は、指定されたアドレス範囲にあるページを物理メモリでロッキングします。(このプロセスまたは他のプロセスで) ロッキングされたページを参照しても、入出力操作を必要とするページフォルトが生じることはありません。この操作は物理資源を拘束し、通常のシステム操作を破壊する可能性があるため、mlock(3C) を使用できるのはスーパーユーザだけに制限されています。設定に依存するページ制限だけがメモリ内にロッキングされます。この制限を越えると、mlock(3C) 呼び出しは失敗します。

munlock(3C) は、物理ページのロッキングを解放します。1 つのマッピングのアドレス範囲で複数の mlock 呼び出しを行なっている場合も、1 回の munlock 呼び出しだけでロッキングが解放されます。ただし、同じページの異なったマッピングが mlock で処理されている場合は、すべてのマッピングへのロッキングが解放されるまでページのロッキングは解除されません。

ロッキングは、マッピングが mmap 操作で置き換えられるか、munmap で削除された場合にも解放されます。

MAP_PRIVATE マッピングに伴う「書き込み時コピー」イベントの際に、ロッキングはページ間を移転されるため、MAP_PRIVATE マッピングを含むアドレス範囲のロッキングは、「書き込み時コピー」のリダイレクトに合わせて透過的に保持されます (リダイレクトについては、「マッピングの作成と使用」を参照してください)。

mlockall(3C)munlockall( 3C)

mlockall(3C)munlockall(3C) は、mlock(3C)munlock(3C) に似ていますが、アドレス空間全体を操作します。mlockall(3C) はアドレス空間にあるすべてのページにロッキングを設定し、munlockall(3C)mlock(3C)mlockall(3C) のどちらで設定されたかに関係なく、アドレス範囲にあるすべてのページのロッキングを解除します。

msync(3C)

msync(3C) は、指定されたアドレス範囲にある修正されたすべてのページを、そのアドレスによってマッピングされているオブジェクトにフラッシュします。これは、ファイルに対する fsync(3C) 操作と同様です。

その他のメモリ制御機能

sysconf(3C)

sysconf(3C) は、システムに依存するメモリページの大きさを戻します。移植性を保つため、アプリケーションにページの大きさを指定する定数を組み込まないでください。同じ命令セットの実装においてもページの大きさが異なることは珍しくありません。

mprotect(2)

mprotect(2)は、指定されたアドレス範囲にあるすべてのページに指定された保護を割り当てます。割り当てられた保護は、対象となるオブジェクトに認められている権限を越えることはできません。

brk(2)sbrk(2)

brk(2)sbrk(2) は、記憶領域をプロセスのデータセグメントに追加するために呼び出されます。

プロセスは brk(2)sbrk(2) を呼び出して、この領域を操作できます。

caddr_t
 brk(caddr_t addr);

caddr_t
 sbrk(int incr); 

brk(2) は、呼び出し側によって使用されていない最小値のデータセグメント位置を addrに設定します (システムのページサイズの最小倍数に丸められます)。

代替関数である sbrk(2) は、incr バイトを呼び出し側のデータ空間に追加し、新しいデータ領域の開始位置へのポインタを戻します。