セグメント方式

セグメント方式（セグメントほうしき、memory segmentation）は、メモリ管理の方式の一つ。プログラムやデータをセグメントまたはセクションという「使用目的に応じた、可変な」大きさのまとまりで管理する。セグメントは、メモリ空間上で、情報の属性などによって分類されたグループである。セグメント方式でメモリ位置を参照するには、セグメントを識別する値とセグメント内のオフセットを指定する。セグメントまたはセクションはプログラムをコンパイルした際に生成されるオブジェクトファイルでも使われており、それらがリンクされて実行ファイルが生成され、そのイメージがメモリにロードされる。
セグメントは仮想記憶やメモリ保護機能を実現する方式の一つである。プログラムのモジュール毎やメモリ使用法の異なるクラス毎に「コードセグメント」や「データセグメント」といった各種セグメントが生成される。1つのセグメントを複数のプログラムが共有することもある。

**仮想記憶におけるセグメント方式**
オペレーティングシステムは、必要なプログラムやデータを主記憶上に読み込み（ロールイン）、セグメントとして管理する。読み込む際に、空き領域が足りないときは、不要なセグメントを補助記憶装置に退避（ロールアウト）して必要な空き領域をつくる。
各セグメントは、セグメントテーブルで管理され、セグメント番号とセグメントの開始物理アドレスが保管されている。各セグメントに属するプログラムやデータの実アドレスは、セグメントテーブル内の開始アドレスとそこからの相対アドレスから算出する。
セグメントは、実記憶上に連続した領域として割り当てられる。セグメントの大きさが可変長なため、場合によっては、実記憶上には空き領域の合計が十分あるのに連続領域が空いていないことがある（フラグメンテーション）。

**ページ化セグメンテーション（多重仮想記憶）**
多重仮想記憶の概念図
セグメント方式とページング方式を組み合わせた方式。この方式では、プログラムコード用、データ用などの各セグメントが複数のページで構成される。これによって、1つのセグメントが連続した実メモリに存在する必要が無く、外部断片化を防ぎ、効率的にメモリを使用することができる。さらに、プログラムコード用のセグメントの書き換えを禁止するといったアクセス制限や、リードオンリーのセグメントをプログラム間で共有することによりメモリ消費を抑えるといったことができる。
ページ化セグメンテーションはMULTICSやACOS-4のメモリ管理に採用されている。

**フラットメモリモデルにおけるセグメンテーション**
ページングに対応したシステムにおいても、一つの（仮想）アドレス空間を区切ることでセグメントを実現する場合もある。たとえば、プロセスが使用するコード、データやスタックをそれぞれ、0x1000から0x2000までのコードセグメント、0x2000から0x4000までのデータセグメント、0xe000から0xffffffまでのスタックセグメント、に配置して使うことである。ハードウェアがこうした方式のセグメンテーションに対応していれば、それぞれの領域に対してデータ実行防止のような保護をセグメントごとに行うことができる。
この場合、CPUアーキテクチャの互換性が高くなるが、プロセス間でセグメントを共有するときに、ページテーブルを共有してメモリ使用量を削減するというメリットはなくなる。
Linuxはこの方式を採用している。

**ハードウェア実装**
セグメントはメモリ保護を実装する方式の1つである。ページ単位のメモリ保護もあり、両者を組み合わせることもできる。セグメントの大きさは可変であり、最小の場合1バイトとすることもできる。セグメントは通常、ルーチン群やデータテーブル群といったプログラム上の自然な領域に対応しており、プログラマから見えるようになっていることが多い。
セグメントには長さとパーミッションがある。プロセスがあるセグメントを参照しようとしたとき、その参照の種類がパーミッションで許可されていて、その際のオフセットがセグメントの長さの範囲内であるときのみ参照できる。さもなくば、セグメンテーション違反などの例外処理が呼び出される。
セグメントには、それがメモリ上のどこに配置されているかを示す情報も付属している。それは、セグメントの先頭アドレスという場合もあるし、ページ化セグメンテーションならページテーブルのアドレスの場合もある。前者の場合、あるセグメントの範囲内の位置への参照は、セグメント内オフセットをセグメントの先頭位置のアドレスに加算して参照すべきメモリアドレスを算出する、後者の場合はセグメント内オフセットとページテーブルの内容から参照すべきメモリアドレスを算出する。
セグメントには、そのセグメントが主記憶上にあるか否かを示すフラグも付属している。主記憶にないセグメントへの参照が発生すると、オペレーティングシステムが二次記憶装置からそのセグメントの内容を読み込む。
セグメントが対応するページテーブルをもたない場合、セグメントの先頭アドレスは一般に主記憶内のアドレスである。その場合ページングは全く関与しない。80386およびそれ以降においては、ページングを使わずにそのようなアドレッシングを行う場合と、ページングを使ってページ化アドレス空間内のアドレッシングを行う場合がある。
メモリ管理ユニット (MMU) は、セグメントとセグメント内オフセットからメモリアドレスを求める処理を行い、そのアクセスが許可されているものかどうかをチェックする役目を担っている。

**歴史**
一般に言うセグメント方式を実装した初期のコンピュータとしてバロース B5000 があり、セグメント方式で仮想記憶を提供した最初期の商用コンピュータの1つとされている"。B5000は、Program Reference Table (PRT)と呼ばれるセグメント情報テーブルを持ち、該当セグメントに関して、主記憶上にあるか否かを示す情報、サイズ、base addressの保持などに利用されたp=371。このアーキテクチャの改良版は、Unisys ClearPath Libra サーバで2012年現在も使われている。
GE-635を改造したGE-645はセグメントとページングを追加サポートしており、1964年のMulticsのために設計された。
Intel iAPX 432は1975年に開発が始まったが、マイクロプロセッサ上で真のセグメント・アーキテクチャによるメモリ保護を実装することを意図していた。
、ストラタス、アポロといったコンピュータはいずれもセグメント方式を採用している。

**x86**

**リアルモード**
リアルモードx86（8086、および80286以後のプロセッサのリアルモード）におけるプログラミングモデルでは、「セグメントレジスタ」と呼ばれるレジスタが存在するが、その振舞は上記で説明したアドレッシング手法とは全く異なる。また、メモリ保護や仮想アドレスは無い。
これらのプロセッサ、あるいはリアルモードにおけるプログラミングモデルでは、アドレス空間は20ビット（1MiB）だが、アドレスレジスタ幅や通常の命令フォーマットにおけるアドレス指定フィールドは16ビットであり、これらの値は「オフセット」と呼ばれる。また、セグメントレジスタの幅も16ビットである。そして「セグメントレジスタの値×16 + オフセット」が実アドレスとなり、1MiBの全アドレス空間へアクセスする機構が8086における「セグメント」と称されたものである。
8086にはCS/DS/SS/ESの4つの16ビットの「セグメントレジスタ」があり、メモリアクセスの種類に応じて暗黙のうちにセグメントレジスタが選択される。命令フェッチならCS、データの読み書きならDS、スタックへのアクセスならSSが選ばれる。以上のようなアクセス種別による暗黙の選択の他、セグメント・オーバーライド・プレフィックスという命令の前置修飾機能があり、どれかのセグメントレジスタを明示的に選択することもできる。

**メモリーモデル**
8086のC言語における「メモリーモデル」は、以上のような命令セット・アーキテクチャ的な仕様に対応するための規約のようなもので、「複数のオブジェクトファイル間でセグメントを共有する際の規約」とか「セグメントの使用法や結合法が規定されている」といったような一般的な概念で説明されるようなものではない。前の節で説明でわかるように、8086ではセグメントを跨がずにアクセスできるメモリの範囲は64KiB（65,536バイト）であるので、コード量あるいはデータ量がその範囲に収まるならば収めてしまい、暗黙のセグメント指定のみでアドレッシングするようなコードにすると、ビルドされたプログラムが効率的になる。そこで、明示的にfarなどと指定されないアドレスについて、コードとデータのそれぞれのどちらも64KiB以内の「スモール」、片方だけが64KiBより大きい「コンパクト」と「ミディアム」、両方とも64KiBより大きい「ラージ」、さらに、1個の配列などが64KiBより大きくなることも考慮する「ヒュージ」のような各EXEファイルと、COMファイルのようにコードもデータも同一の64KiBに入る「タイニー」、といったようなモデルに分けられており、アプリケーションの規模等によって使い分けていた、というのが、8086のC言語における「メモリーモデル」である。

**プロテクトモード**
x86ファミリが、「一般的なセグメントの仕組み」を持つようになったのは、80286（のプロテクトモード）からである。
80286で、セグメント方式によるメモリ保護機能を持つプロテクトモードが追加された。プロテクトモードにおけるセグメントレジスタは、グローバルディスクリプタテーブル (GDT)・ローカルディスクリプタテーブル (LDT) 等により示されるセグメントを選択するセグメントセレクタとされた。全体のアドレス空間は24ビット (16MiB) に拡大されたが、オフセットは16ビットのままだった。
80386 (IA-32) のプロテクトモードは、ページング方式も取り入れられ（ページ化セグメンテーション）、物理・論理とも32ビットの仮想記憶機能を持つようになった。オフセットは32ビットに拡張された。FSとGSというセグメントレジスタが追加された。
（以上とは異なり、インテルの先導により策定されたアーキテクチャではないが）x64では、Microsoft Windowsのx64版は、GSセグメントレジスタがスレッド局所記憶へのポインタを指すようになっている。LinuxカーネルではGSがCPU単位のデータを指している。

**オブジェクトファイル**
セグメントまたはセクションはオブジェクトファイルでも定義されている。異なるオブジェクトファイルにあるセグメント群は、セグメント定義時に指定されたルールに従ってリンケージエディタによって結合される。
ELFでは、実行ファイルおよび共有ライブラリを実行する目的でロードする際に用いるプログラムヘッダにてセグメントという用語を用いている。これはリンク時などに使用するセクションとは異なる抽象度を持つもので、セグメント方式に由来した名称となっている。

**脚注**

**参考文献**
 Operating Systems: Internals and Design Principles by William Stallings. Publisher:  Prentice Hall. ISBN 0-13-147954-7. ISBN 978-0-13-147954-8.

**関連項目**
 メモリ管理- 仮想記憶- ページング方式- セグメンテーション違反- .bss

**外部リンク**
  Intel 64 and IA-32 Architectures Software Developer Manuals