特集
Windows 9x or Windows 2000?

コラム:Windowsの歴史、メモリの歴史(1)

デジタルアドバンテージ
2001/07/28

 Windowsのアーキテクチャ、その中でも特にメモリ管理にまつわるアーキテクチャについて理解するためには、Intel社のCPUや、それを使ったIBM-PCなどにおけるシステム アーキテクチャについて知っておく必要があるだろう。1GHzを超えるクロック周波数と、数Gbytesものメモリを実装できるようになっている現在でも、なぜWindowsは64Kbytesというメモリの制約に苦しんでいるのか、それを理解するためには、x86アーキテクチャについて避けて通るわけにはいかない。

 ここでは、Intel社のCPUと、それを使ったIBM-PCアーキテクチャ、およびMS-DOSやWindowsシステムについて、メモリ管理にまつわる部分を中心に取り上げて、それらを時間順に追ってみようと思う。

1978年――Intel 8086/8088発表(悪夢の始まり、セグメント)

 8086(以下,特別な断りがないかぎり、8bit版である8088も含む)は、同社の8bitマイクロプロセッサ、8080/8085に続く16bitプロセッサとして開発されたものである。当時一般的な8bitマイクロプロセッサ(Intel 8080/8085、Zilog Z80、Motorola 6800など)は64Kbytesのアドレス空間(アドレス バスは16bit幅)を持っていたが、プログラムやデータを格納するための領域としては不十分であった。そこで各社とも、より処理能力の高い16bitマイクロプロセッサを開発するにあたり、アドレス空間の拡大を最優先課題の1つとして掲げていた。アドレス空間を拡大するにはさまざまな方法が考えられるが、Intel社が選んだ方法は、既存の8080アーキテクチャからの変更が最も少なくなる方法であったといえる。実はこれが後に多大な禍根を残すことになるのであるが、当時としては、実装のしやすさや既存のプログラマなどに配慮した結果、このようなアーキテクチャが採用された(これに対しMotorolaでは、過去との互換性などはあまり重視せず、性能や将来性を考えてほとんど新規に設計している)。以下、簡単に8086アーキテクチャ(x86アーキテクチャ)について見ておこう。

 8086は、AX、BX、CX、DX、SI、DI、BP、SPという8本の16bit汎用レジスタを持っている(AX〜DXは8bitずつ半分に分けて使用することも可能)。各レジスタは命令によって用途がある程度決まっており、汎用性が低いのであまり使い勝手はよくないが、これは8080(およびZ80)のアーキテクチャをそのまま引き継いだからである。実際、8080のアセンブリ言語プログラムをそのまま8086のアセンブリ言語に変換するという変換ソフトウェアも用意されており、既存の8080ユーザーにとっては移行がしやすかった。

 しかしレジスタは16bit幅しかないので、そのままでは64Kbytes以上のアドレス空間を指し示すことができない。そこで考え出されたのが「セグメント方式」というアドレッシング方法である(セグメント(segment)は「区分」という意味)。広いアドレス空間を「セグメント」と呼ばれる細かい領域に分け、さらにその中のある特定の場所を指すために「オフセット」を使う。つまり、メモリ中のある特定の位置(「実効アドレス(effective address)」という)を指し示すために、「セグメント」と「オフセット」という2つの情報を使うのである。8086では1セグメントのサイズは64Kbytesとしている。よってオフセットも16bitで済むことになる(つまり16bitレジスタでかまわない)。セグメントとオフセットは「:」で結合して、たとえば「1234:5678」のように表記する(数値は16進数で表す)。「1234」がセグメントで、「5678」がオフセットである。いずれも16bitの数値だ。

セグメントとオフセットによるメモリ アクセス

8086では、メモリ中のある特定の場所を指し示すために、「セグメント」と「オフセット」という2つの値を使用する(いずれも16bit幅のデータ)。実際の物理アドレスを求めるには、「セグメント」の値を16倍して、そこに「オフセット」を加えることによって求められる。これにより20bitの物理アドレスが得られ、最終的に1Mbytesの物理メモリ領域を自由にアクセスできるようになる。ただし、オフセットは16bit幅しかないため、1つのセグメント サイズは64Kbytesに制限され、これを超えた範囲へアクセスするためには、セグメント部の値を変更しなければならないので、手間がかかる(セグメント レジスタの内容を変更する命令をいちいち発行しなければならない)。

 そして、CS、DS、ES、SSという4つの16bitのセグメント レジスタを新たに用意し、ここにセグメント部を格納する。そして、データをアクセスするときはDSを使い、コード(プログラム)をアクセス(命令をフェッチ)するときはCSを使う(命令ごとにどのセグメント レジスタを使うかが決まっている)。そしてたとえばBXレジスタの内容が「5678」だとすると、“[BX]”というアドレス指定ならば、DSの指すセグメントのオフセット「5678」のアドレス位置を表すことになる。

 セグメントとオフセットから実際の物理アドレスを求めるためには、「セグメント」を16倍して、そこにオフセットを加える。「1234:5678」ならば、「0x01234×16+0x05678(16進)」⇒「0x179B8(16進)」となる。この操作により、20bitの物理アドレスが求められるので、結局のところ、8086でアクセスできる最大アドレス空間は「1Mbytes」ということになる。1Mbytesというサイズは、その当時としては使い切るのが難しいくらい広いと考えられていたが(当時の主力開発言語はアセンブリ言語だった)、今となってはいうまでもないだろう。ところで少し考えると分かるが、セグメント同士は完全に分離されているわけではなく、隣接するセグメントはほとんどオーバーラップしている(セグメントの値が1増えても、物理アドレスが16増えるだけなので、16bytesずつスライドしながら、オーバーラップしている)。間違って隣接するセグメントに書き込んだりしても、エラーとはならないので(システムがそのような違反を検出してくれないので)、システムの安全性や信頼性は高くない。

 このようなアドレッシング方式によってもたらされるプログラミング作業は、特にアセンブリ言語のプログラマにとって非常にやっかいなものであった。64Kbytes以内のプログラムなら特に難しくはないが、64Kbytesを超えるようなプログラムを作る場合は、いちいちセグメントを切り替えるコードを必要としたからだ。さらに、この混乱はC言語ユーザーなどでも問題になっている。データを指し示すポインタ タイプにおいて、オフセット部だけを使うnearポインタと、セグメントとオフセットを使うfarポインタの2つを使い分けなければならないからだ(farポインタはオーバーヘッドが大きいので、あらゆるポインタをfarポインタだけにすると、速度が大幅に低下する。そのためプログラマは、性能とコストに応じて、使い分けなければならない)。

1981年――MS-DOS Ver.1.x/IBM-PC(640Kbyteの壁、現る)

 1981年、IBMがビジネス/パーソナル用途向けのコンピュータ システム、IBM-PCを発売した。IBM-PCでは、Intelの8088 CPU(8086の外部データ バス幅を8bitにしたもの。ソフトウェア的には8086と同じだが、ハードウェア的には8bitマシンとほぼ同等ですむのでコストを抑えることができる)を使用しており、シリアルやパラレル ポート、ビデオ カードなどを拡張カードの形式で追加するシステムになっていた。

 8088の物理的なアドレス空間は1Mbytesあるので、IBM-PCでは、00000〜9FFFF(640Kbytes)がメイン メモリ用領域として予約され、そこから上の領域は、拡張カードやシステムBIOS ROM用領域として予約されている(出荷時に実装しているメモリ量は、最低で16Kbytes。後はオプションの拡張カードを使って追加する)。このようなメモリ マップになっているため、一般的には、メイン メモリは640Kbytesに制限されている(これはあくまでもIBM-PCアーキテクチャにおける制限であり、MS-DOS自体にはこのような制約はない。システムによっては、1Mbytes近いメイン メモリ領域を持つMS-DOSマシンもあった)。

IBM-PCアーキテクチャ のメモリ マップ

これはIBM-PC(およびPC/AT互換)アーキテクチャにおける、典型的なメモリ マップ。1Mbytesの物理メモリ アドレス空間のうち、00000〜9FFFFがメイン メモリ用の領域として確保されている(実メモリ サイズはモデルや構成によって変わる)。A0000よりも上位は、拡張カードなどで利用するための領域になっており、最上位にはシステムBIOSを収めたROMがマップされている(サイズは32K〜64Kbytes程度)。このため、メイン メモリは最大でも640Kbytesに制限される。一般的には、ビデオRAMはA0000〜BFFFFを占め、ビデオBIOS ROMはC0000〜C7FFFに置かれている。それ以外の領域は、使用する拡張カードによって異なる。

198x年――EMSメモリ(メイン メモリが足りない!)

 表計算やデータベースなどのビジネス向けアプリケーションが使用されるようになると、さすがにメイン メモリ空間が640Kbytesでは狭くなってきた。この空間にMS-DOSやデバイス ライバ、ネットワーク システムなどの付加的なシステム コード、アプリケーション本体、そしてユーザー データまですべて格納しなければならないからだ。

 このような事態を打開するため、EMS(Expanded Memory Specification)というメモリ拡張機構が開発された。PCのメインメモリとは別に用意した巨大なメモリ(Expanded Memory)を、PCのメモリ空間上に設けた64Kbytesの窓から、間接的にアクセスするためのメカニズムである。Expanded Memoryを16Kbytesの「ページ」という単位に細かく分割し、それを64Kbytesの窓に同時に最大4枚までマップする(対応させる)。マップされたメモリは、メイン メモリと同じように高速にアクセスできるが、5ページ以上のページを使いたくなった場合は、いずれかのマッピングを解除して、別のページをマップし直す必要がある(後に、64Kbytes以上の窓が使えるように規格が拡張された)。EMSでは、ページをパタパタと切り替えながら使用しなければならないので、プログラミングはやや面倒であるが、それでも手軽に広大な(といっても最大32Mbytesであるが)メモリが利用できるようになったので、各アプリケーションでのEMS対応も広く進んだ。

 EMSメモリは、当初はハードウェアの拡張カードの形式で利用されたが、後には80386の仮想8086モード(後述)を使った、ソフトウェア的な方式で実現されるようになった。

EMSメモリ

EMSは、不足するメモリ領域を増やすために考案されたメモリ拡張規格である。メイン メモリとは別に用意したExpanded Memory領域からページを取り出して、メイン メモリ空間上に設けた64Kbytesの窓へマップする。1ページのサイズは16Kbytesなので、同時に最大4ページまでマップすることができる(EMSの最終規格では、より大きなサイズの窓が使えるようになった)。巨大なデータなどをEMSページに格納しておき、必要になった時点で順次窓に呼び出してきてアクセスすることにより、メイン メモリに入り切らないような大きなプログラムでも実行することができる。

1982年――80286発表(16bitプロテクト モード登場)

 80286は、80186/188の後継として開発されたプロセッサである。性能の大幅な向上はもちろんであるが、新たに組み込まれた「プロテクト モード(protect mode)」というメモリ保護メカニズムが特徴的なプロセッサである。

 プロテクト モードは、プログラムのコードやデータを他のプロセスから保護するためのメカニズムであり、これによりたとえばあるプロセスが誤って他のプロセスのコードやデータ領域を破壊したり、権限のない一般ユーザーのプログラムが、特権レベルで動作するOSカーネルのデータへアクセスしたりするのを防ぐことができる。80286以前のプロセッサではこのような保護機能はいっさい用意されておらず、信頼性の高いシステムを構築するのは不可能であった。またプロテクト モードでは、利用可能なメモリ量も最大16Mbytesまでに増やされ、プログラムから利用できるメモリのサイズも拡大されている。従来の「セグメント アドレス」は、「セレクタ」と呼ばれる、最大64Kbytesの可変長のメモリ ブロックを指し示すためのハンドルとなり、正しいセレクタを使わない限り、権限のないプロセスがセグメントの内容をアクセスしたり、変更したりするようなことはできなくなった。つまりセグメントの内容が「保護(プロテクト)」されるようになったのである。これがプロテクト モードと呼ばれるゆえんである。このプロテクト モードに対して、従来までのプロセッサのモードは、リアル モード(real mode、実モード)と呼ばれるようになった。

16bitプロテクト モードにおけるセグメントの扱い

プロテクト モードでは、従来の「セグメント」アドレスは、「セレクタ」と呼ばれることになった。セレクタとは、セグメント ディスクリプタを選択する(指し示す)ためのハンドル、という意味がある。セグメント ディスクリプタには、メモリ セグメントの物理ベース アドレス(セグメントの開始物理アドレス)、セグメント サイズ、セグメント属性などが含まれている。物理メモリはリアル モードのときと同じく、セグメントと呼ばれるブロックに分けて管理されているが、それをアクセスするためには、正しいセレクタを経由しないとアクセスできなくなっている。セグメント サイズは最大64Kbytesの可変長となり(セグメントの上限を超えるようなオフセットを指定するとエラーとなる)、不正なアクセスからも保護されるようになった。アクセスできる物理メモリは、プロテクト モードでは最大16Mbytesまでであるが、リアル モードではやはり1Mbytesだけに限定されている。

 80286は、プロテクト モードを備え、EMSのような面倒な方法を使わなくても、大量のメモリにアクセスできる優れたプロセッサであるが、いったんプロセッサをプロテクト モードにしてしまうと、もうリアル モードに戻れないという問題点があった(電源投入時やリセット直後はリアルモードで起動する)。さらにリアル モードのプログラムとプロテクト モードのプログラムはまったく異なるものであり(たとえば、セグメントとセレクタの扱いがまったく異なる)、同じように実行することはできない。そのため従来との互換性を考慮すると、既存のプログラムはリアル モードで実行し、新しい(プロテクト モード対応の)プログラムはプロテクト モードで実行する必要があるのである。しかし80286では、プロセッサが動作モードをいったんプロテクト モードにしてしまうと、もうリアル モードには戻れない。このような問題があるため、実際のところ、80286の16bitプロテクト モードは当初はほとんど使われることなく、80286マシンは単に速い8086マシンとしてのみ使われることが多かった。本格的に16bitプロテクト モードが活用されたのは、1987年に発表されたOS/2からである(WindowsではWindows 3.0以降でやっとサポートされた)。

198x年――HMA、UMB、EMB(メイン メモリを拡張したい!)

 80286では実装可能な最大メモリ容量が従来の1Mbytesから16Mbytesまで拡大されたが、それに伴い、リアルモードのMS-DOSの世界でも、わずかだが利用できるメモリ領域が増加している。それをまとめたのが、XMS(eXtended Memory Specification)規格である。

 HMA(High Memory Area、ハイ メモリ)は、8086と80286の物理アドレスの扱いの違いから生まれたメモリ領域である。FFFF:000F(FFFFF。メモリの最終バイト)の次のバイト位置は、8086では20本しかアドレス線がないので00000(メモリの先頭)に戻ってしまうが、80286では24本のアドレス線があるので、100000(1Mbyte目)を指す。こうやって、1Mbytesを超えても、最大64Kbytesの領域が余分にアクセスできるようになったのである。これがHMAであり、MS-DOS本体のコードをここに格納することにより、ユーザー領域が64Kbytesほど増えることになった。

 さらに、UMB(Upper Memory Block、上位メモリ)というメモリ領域も利用できるようになった。これは、本来はデバイスのためのアドレス空間として予約されている、A000:0000〜FFFF:0000(384Kbytes)のうち、拡張カードなどで使っていない部分の空き領域をメイン メモリとして使うための規格である。これにより、さらに数100Kbytesの新たなメモリ空間を手に入れることができた(UMBが実際に利用可能になるのは、次に述べる「仮想8086モード」が登場してから)。

 EMB(Extended Memory Block、「XMSメモリ」とか「プロテクト モード メモリ」などとも呼ばれる)は1Mbytes以上の拡張メモリ空間(Extended Memoryという。EMSのExpanded Memoryと紛らわしいので注意)を利用したメモリ領域のことである。しかしこの領域はプロテクト モードのOSでなければ活用することができず、リアル モードのMS-DOSでは、せいぜいRAMディスクか、ディスクの読み書き用バッファ領域などにしか利用することはできなかった。本格的な拡張メモリ空間の活用には、後のWindowsの登場を待つ必要があった。

 なお、このような各種のメモリ領域拡大規格の登場により、従来からある1Mbytes以下のメモリ領域は「コンベンショナル メモリ(conventional memory、従来のメモリ)」と呼ばれるようになった。

XMS規格(HMA、UMB、EMBメモリ)

メイン メモリを拡張するためのいくつかの方法をまとめた、XMS(eXtended Memory Specification)という規格が作られた。ここにはHMA、UMB、EMBが含まれる。HMA(ハイ メモリ)は、8086と80286の物理アドレスの扱いの違いから生まれたもので、拡張メモリ空間の先頭の64Kbytesの領域である。MS-DOS本体をここに格納することにより、メイン メモリ中でのMS-DOSの占有サイズを20Kbytes以下にすることができる。UMB(上位メモリ)は、拡張カード用の予約領域(386Kbytes)のうち、未使用の部分にRAMを割り当てて、メイン メモリの延長として利用できるようにしたもの。おもにデバイス ドライバなどをロードしておくのに使われる。EMB(XMSメモリもしくはプロテクト モード メモリ)は、1Mbytes以上の拡張メモリ空間に確保されたメモリ領域のこと(上限は物理メモリ サイズまで)。80286以上のプロセッサで、プロテクト モードをサポートしたOSでのみ利用できるが、MS-DOSでも、RAMディスクやディスク バッファなどで利用されていた。

1985年――80386発表(本命、32bitプロテクト モード参上)

 80286の後継として、より性能が向上した80386が開発された。最も大きな特徴は、32bitプロテクト モードと、仮想8086モード、およびページング方式の仮想記憶機構を新たに装備した点にある。

 32bitプロテクト モードとは、すべてのレジスタを32bit幅に拡張し、4Gbytesのフラットなアドレス空間を実現した新しい動作モードである。セグメント方式という面倒な手法を駆使することなく、最大4Gbytesものアドレス空間にフラットにアクセスできる(本当は、先の「16bitプロテクト モードにおけるセグメントの扱い」の図と比べると、1セグメントのサイズが4Gbytesになっただけであり、セグメント セレクタやその機能も同じく残っているし、カーネル内部の割り込みディスクリプタ テーブルなどではセグメント セレクタを意識したプログラミングが必要である。しかし1セグメントが4Gbyteもあるので、少なくともユーザー プログラム レベルでは、いちいちセグメントを切り替える必要性はほとんどなくなった)。これにより、従来の64Kbytesや1Mbytesの壁に悩まされることなく、アプリケーションを作成できるようになった。ただしこのモードを活用するためには、リアル モードのプログラムとは異なる、32bitプロテクト モード専用のプログラム(および、それをサポートしたOS)が必要となるので、実際に80386の真価が発揮されるのはずっと後(Windows/386以降)になってからである。

 仮想8086モード(virtual 8086 mode)とは、リアルモードの8086環境をエミュレートするためのメカニズムであり、1つの物理的な80386プロセッサ上に、複数の8086環境(リアル モード環境)を作り出してマルチタスクで実行させることができる。また、ハードウェア環境をエミュレーションすることができるので、ハードウェア コンフィギュレーションを自由に変更することができる。たとえば仮想的なデバイスを実現したり、EMSやUMBをサポートしたりもできる(すでに述べたとおり、EMSは当初はハードウェア拡張カードで実現されていた)。

 80386では、以上の2つのモードや16bitプロテクト モード、およびリアル モードを混在させて、自由に切り替えて使用することができ(80286と違って、リアル モードとの切り替えは自由にできる)、オンデマンド方式の仮想記憶システム(使用頻度の少ないメモリ ページの内容をディスクに退避しておき、必要になればディスクからメモリにロードしてくる方式。物理メモリサイズを超えるような大きなプログラムでも実行できるようになる)を実現することができる。


 INDEX
  [特集]Windows 9x or Windows 2000?
     1.イントロダクション
     2.Windows 9xカーネルの概要
    コラム:Windows歴史、メモリの歴史 (1)
      コラム:Windows歴史、メモリの歴史 (2)
     3.Windows 2000カーネルの概要 (1)
     4.Windows 2000カーネルの概要 (2)
     5.プロセス管理の概要
     コラム:Windows 3.xのマルチタスク システム
     6.Windows 9xのプロセス管理メカニズム (1)
     7.Windows 9xのプロセス管理メカニズム (2)
     8.Windows 2000のプロセス管理メカニズム (1)
     9.Windows 2000のプロセス管理メカニズム (2)
 
 特集


Windows Server Insider フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Windows Server Insider 記事ランキング

本日 月間