メモリ管理

初期のコンピュータは1つのプログラムを実行するだけであったが、性能が向上するにつれて、複数の人のプログラムを短い時間単位で切り替えて、見かけ上、同時並行的に実行するTime Sharing System(TSS)という使用法が出てきた。このような使い方をする場合、複数のユーザのプログラムをメモリ上において実行するのであるが、ここで問題が出てくる。

まず、それぞれのユーザは、論理的には、自分がコンピュータを占有しているのであるから、プログラムは0番地から順にメモリを使うように作られている。そうすると全員のプログラムが重なってしまい、複数のユーザのプログラムをメモリ上に置いて短時間に切り替えて実行することが出来ない。また、これを解決したとしても、ユーザAのプログラムが暴走して、ユーザBやCのプログラムの格納されているメモリ領域を書き換えてしまっては困る。

このような問題を解決する手段として考案されたのが、メモリ管理機構である。

セグメント方式のメモリ管理

すべてのプログラムが0番地から始まる問題は、プログラムが意識する番地と実際のメモリの番地とをずらせることが出来れば解決できる。

図5.1 アドレス変換の考え方

図5.1のように、プロセサは0番地をアクセスするのであるが、メモリとの間にアドレス変換機構が入っており、実際にメモリに送り出すアドレスを変えてしまうのである。プログラムAを実行する場合は、そのまま0番地で良いが、プログラムB、C、Dを実行する場合は、プロセサの0番地がそれぞれのプログラムの先頭番地を指すように足し算を行う。

このとき、プロセサが認識するプログラムのアドレスを論理アドレス(あるいは仮想アドレス)と言い、メモリのアドレスを物理アドレスという。

次にどのプログラムを実行するかはOSが選択して切り替えを行うので、ハードウェアとしては、足しこむ値を保持するレジスタと、アドレスの足し算を行う機構を追加するだけで良い。ただし、詳細に言うと、OSはプログラムAであり、アドレス変換なしで実行されて、アドレスの足しこみレジスタにプログラムB、C、…のオフセット値を書き込み、それぞれのプログラムを実行させる。ただ、書き込んだ瞬間にオフセットが有効になると暴走してしまうので、ユーザプログラムに切り替える瞬間にアドレス変換が有効になるという構造にしておく必要がある。

このような制御を行うために、近代のプロセサでは、OS実行するスーパバイザ状態と、一般のプログラムを実行するユーザ状態を持ち、ユーザ状態になるとオフセットを有効にするというような構造になっている。また、OSの実行を必要とするI/Oからの割り込みなどでは自動的にスーパバイザ状態に切り替わるようになっているのであるが、横道が長くなってしまうので、これについては、別途、稿を改めて説明することにしたい。

これでアドレスをずらす問題は解決したが、これだけではプログラムBが暴走してプログラムAの領域に書き込みを行ったり、他のプログラムのメモリを読んで情報を盗んだりというのは防止できない。これを防ぐためには、開始アドレスに加えて、そのプログラムがどれだけのメモリを使えるかという値を記憶して、その値を超えないことをチェックすれば良い。

図5.2 セグメント方式のアドレス変換機構

これをブロック図で表すと図5.2のようになる。論理アドレスと許容されたLengthを比較回路でチェックし、論理アドレスがLengthより大きい場合には、アクセス違反の割り込みを上げてOSに処理を任せる。並行して、論理アドレスにOffsetを足しこんで論理アドレスを物理アドレスに変換し、アクセス違反でない場合は、この変換された物理アドレスでメモリをアクセスする。このアドレス変換の単位をセグメントと呼び、この方式をセグメント方式のメモリ管理という。

これで複数のプログラムをメモリ上にぶつかり無く配置することができ、かつ、他のプログラムの領域をアクセスするという問題も防止できるようになった。

また、このメモリ管理機構は、プロセサのセキュリティを改善するのにも使用される。インターネットでのバッファオーバフロー攻撃は、配列のサイズを超えた書き込みをチェックしていないプログラムに付け込み、配列のサイズを超えるデータを読ませてプログラムの部分にまでウイルスなどの命令データを書き込み、その命令を実行させてしまうことにより、コンピュータの制御権を奪ってしまう。

これに対して、プログラムの命令セグメントとデータセグメントを分離し、命令領域のメモリには書き込めず、データ領域のメモリの内容は命令として実行できないというようにすればバッファオーバフロー攻撃を押さえ込むことが出来る。

このため、セグメント単位に、書き込み可能とか、実行可能とかの属性を記憶し、アクセスのタイプ(読み出し、書き込み、実行)と属性をチェックして、許可されたタイプのアクセスだけを許可するという機構を設ける。そして、命令セグメントは実行可能であるが書き込みは不可、データセグメントは読み書きは可能だが、実行は不可という属性を設定してやれば良い。

当初のx86アーキテクチャではセグメントの実行の可/不可という属性を持っておらず、バッファオーバフロー攻撃に弱かったのであるが、64ビット化とともに、NX(No eXcute)とかXD(eXecute Disable)と呼ぶ実行不可の属性ビットを追加した。IntelやAMDはこの機能を新機能のようにアピールするが、コンピュータアーキテクチャの歴史から見ると、セキュリティーホールのバグを修正したという感じである。