🎓 レベル:発展 | 重要度:B(推奨)
📎 前提:仮想記憶とページング | 関連:物理メモリと論理アドレス・競合状態とクリティカルセクション
要点(BLUF)
- セグメンテーションは、メモリを「コード・スタック・ヒープ」など**意味のあるまとまり(セグメント)**単位で管理する方式。各セグメントは可変長で、ベース+長さで表す。
- 可変長ゆえに外部断片化(空きが細切れで使えない)が起きる。固定長ページングの内部断片化(ページ末尾の余り)と対をなす。
- 現代はページングを土台に、ページ単位の保護ビット(読み/書き/実行可否)で保護を実現。不正アクセス(NULL参照・実行不可領域の実行)をハードが弾く。
概念 ── ページとは別の切り方
仮想記憶とページング は空間を固定サイズで機械的に切りました。これは管理が楽な反面、プログラムの論理構造(ここはコード、ここはスタック)を無視します。
セグメンテーションは逆に、論理的なまとまりで切ります。プロセスのアドレス空間を「コードセグメント」「データセグメント」「スタックセグメント」のように、意味のある可変長ブロックに分け、各セグメントを「ベース(物理開始)+リミット(長さ)」で管理します(物理メモリと論理アドレス のベース+リミットをセグメント単位に拡張したもの)。
flowchart LR
subgraph L["論理アドレス(セグメント番号 + オフセット)"]
s0["セグ0: コード"]; s1["セグ1: データ"]; s2["セグ2: スタック"]
end
subgraph T["セグメントテーブル"]
t0["0: ベース/長さ/権限"]; t1["1: ..."]; t2["2: ..."]
end
s0-->t0; s1-->t1; s2-->t2
利点は、まとまり単位で権限(コードは実行可・読み取り専用、スタックは書き込み可・実行不可)を付けやすいこと、セグメント単位で共有しやすいこと。欠点が断片化です。
仕組み ── 2種類の断片化
メモリ割り当てには、形の違う「ムダ」が2種類あります。
| 種類 | どこで起きるか | 中身 |
|---|---|---|
| 外部断片化 | 可変長割り当て(セグメント方式) | 空きの総量は足りるのに、細切れで連続領域が取れず使えない |
| 内部断片化 | 固定長割り当て(ページング) | 要求が単位に満たず、割り当てた単位の末尾が余る |
flowchart TB
subgraph EXT["外部断片化(可変長)"]
a["使用"]; g1["空き(小)"]; b["使用"]; g2["空き(小)"]; c["使用"]
note1["空きの合計は足りても連続が取れない"]
end
subgraph INT["内部断片化(固定長ページ)"]
p["ページ4KB(実データ3KB + 余り1KB)"]
end
ページングが主流になったのは、外部断片化を避けられるからです(空きフレームはどれも等価で、連続性が要らない)。代償の内部断片化は最大でも1ページ未満で抑えられる。多くのCPUはページングを基盤に、セグメントの利点(領域別の権限)はページの保護ビットで実現する折衷を採ります。
仕組み ── 保護ビットによるメモリ保護
ページテーブル(仮想記憶とページング)の各エントリには、フレーム番号と有効ビットに加えて保護ビットがあります。
- R(読み取り可)/W(書き込み可)/X(実行可)
- ユーザ/カーネルのどちらがアクセスできるか
アクセスのたびにMMUがこれを照合し、違反(読み取り専用ページへの書き込み、実行不可ページのコード実行、未マップ領域への参照)があれば例外を上げ、OSが介入します。典型例:
- NULLポインタ参照:仮想アドレス0付近を未マップにしておけば、
*NULLはページフォルトでクラッシュ(暴走前に止まる)。 - W^X(書き込みと実行の排他):書き込み可能なページは実行不可、実行可能なページは書き込み不可にして、データを命令として実行する攻撃を防ぐ。
- コピーオンライト(CoW):fork(プロセスとスレッド)直後は親子で同じ物理ページを読み取り専用で共有し、どちらかが書いた瞬間にだけ複製する。書き込み保護違反を「複製の合図」として使う賢い応用。
要するに保護は、ハードが毎アクセス強制チェックするから成立します。ソフトの約束(規約)では破れますが、MMUの照合は迂回できません(OSの役割とカーネル の特権分離と同じ思想)。
仕組みの直観 ── なぜこの設計か
- ページングを土台にする理由:外部断片化を構造的に消せる。固定サイズの代償(内部断片化)は1ページ未満に限定でき、管理コストが小さい。
- 保護ビットをページに持たせる理由:変換のついでに権限照合できる(MMUがどうせ毎回引く)。専用の保護機構を別に作るより安い。
- W^XやCoWの理由:保護違反という「例外」を、セキュリティ強化や遅延コピーのフックとして再利用している。例外機構の使い回しが効いている。
⚠️ よくある誤解・落とし穴
- 「セグメンテーションとページングは排他」→ 併用できる(x86は歴史的に両方持つ)。現代は実質ページング+保護ビット。
- 「断片化は1種類」→ 外部(可変長)と内部(固定長)で原因が逆。方式選択のトレードオフ。
- 「Segmentation faultはセグメント方式のエラー」→ 名前の由来はそうだが、実体は保護違反/未マップアクセス全般。ページングでも出る。
- 「保護はOSが毎回チェック」→ 毎アクセスのチェックはMMU(ハード)。OSは表と権限を設定し、違反時に呼ばれるだけ。
対応ラボ
なし(保護違反は OSの役割とカーネル のstraceや、NULL参照プログラムのクラッシュで観察可能)。
関連
- 基盤のページングと保護ビットの置き場は 仮想記憶とページング
- ベース+リミットの原型は 物理メモリと論理アドレス
- CoWが効くforkは プロセスとスレッド