プログラムを実行する際,コンピュータは,ハード・ディスクなどの2次記憶装置からプログラムを主記憶(メモリー)に読み込み,その後,命令を順次解釈しながら実行していきます。プログラムが利用するデータもまた,メモリーを使って読み書きされるのが一般的です。そのため,現在のコンピュータ・システムにおいて,メモリーは最も重要な資源であると言えるでしょう。

 今回は,Linuxカーネルがどのようにコンピュータのメモリーを管理し,プロセス間での競合を防止しているかを解説します。

 なお,メモリー管理についてはアーキテクチャに依存する部分が多いため,特に明記しない場合は,最もよく利用されるIA-32(i386以降のx86互換プロセッサのアーキテクチャ)プロセッサを基に解説します。

メモリーの構成とアドレス

 プロセッサとメモリーはバスと呼ばれる配線で接続されています。バスは大きく分けて「アドレス・バス」と「データ・バス」の2つがあります。アドレス・バスは,データを読み書きするメモリー位置(アドレス)を指定するためのものです。プロセッサは,アドレス・バスで指定した位置のメモリーに対し,データ・バスを通じてデータを送受信することで読み書きを実施します。

 つまりメモリーは,データ・バスで一度に読み書きできる大きさのデータ(これをワードと呼びます)が,アドレス・バスで指示できるアドレスの個数並んでいる構成になっていると考えることができます。このような構成のコンピュータを「ワード・マシン」と呼びます。

 しかし歴史的な経緯から,現在のプロセッサではほとんどが,1つのアドレスに8ビット(1バイト)のデータを格納する「バイト・マシン」構成を採用しています。そのため,利用可能なメモリー容量は,アドレス・バスで指示できるアドレス数×8ビット(1バイト)となります。

 IA-32プロセッサは,32ビット幅のアドレス・バスを持っていますから,最大2の32乗バイトつまり最大4Gバイトまでのメモリーを利用できます*1

図1●32ビットのメモリー空間
図1●32ビットのメモリー空間
アドレス空間は32ビット分(4Gバイト)の広さがありますが,実際に利用できるのは搭載しているメモリー容量だけになります。

 アドレスは通常,16進数で表すことになっており,32ビットのアドレスの場合「0x0000000~0xFFFFFFFF」の範囲の数値で表されます。このアドレス範囲をメモリー空間(メモリー・アドレス空間)と呼びます。コンピュータに搭載されるメモリーは,このメモリー空間内に配置されます(図1)。もちろん,実際に利用できるのは,搭載している量のメモリーだけです。

 なお,パソコンの中を見たことのある方はお分かりかと思いますが,私たちが使っているほとんどのパソコンでは,メモリーを「DIMM」(Dual Inline Memory Module)などのモジュール形式で実装しています。場合によっては複数のメモリー・モジュールが搭載されていることもありますが,その場合でも総体として1つのメモリーとして認識されます。メモリーの大きさは,各メモリー・モジュールの合計値となります。これは,メモリーと他の資源との大きな違いです(図2)。

図2●メモリー・モジュールを増やすと容量が増える
図2●メモリー・モジュールを増やすと容量が増える
ハード・ディスクやプロセッサなどと異なり,メモリーは搭載モジュール数を増やしても個数ではなく容量が増加します。

マルチタスクで生じる問題

 さて,この唯一のメモリー空間をマルチタスクでそのまま利用するとなると,さまざまな問題が起こることが想像できるはずです。まず,お互い非干渉であるはずの2つのプロセスが同じメモリー空間(アドレス空間)を共有することになってしまいます。

 メモリー空間を共有すると,他のプロセスのプログラムやデータを読めるどころか,書き換えることだってできてしまいます。このままでは,安心してプログラムを動かすことができません。単一のメモリー空間しか提供されていない場合,マルチタスクでの利用はかなり難しいと言えるでしょう。

 この問題には,いくつかの解決策が考えられます。