TL

アドレス空間とアドレッシングモード ─ 物理/仮想/I/Oマップ

ポインタやMMIOが「どこを指すか」を原理から掴めます。物理/仮想/I/O空間の分け方とカーネル分割、ベース+変位などの有効アドレス計算まで一気通貫で理解できます。

応用アドレス空間仮想メモリMMIOアドレッシングモード有効アドレス最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.アドレス空間は番地の集合で、物理アドレス空間にはDRAMだけでなくMMIOやROMがメモリマップで割り付けられる。x86はI/O命令で別系統のポート空間も持つが、現代のデバイスは大半がMMIO側に寄る。
  • 2.仮想アドレス空間はプロセスごとに独立で、ページテーブルとMMU/TLBが物理へ変換する。上位をカーネル、下位をユーザーに分けて固定写像し、特権ビットで保護する。
  • 3.アドレッシングモードは即値・レジスタ・ベース+変位・インデックス・スケールなどがあり、有効アドレスはベース+インデックス×スケール+変位で計算してから変換とアクセスに渡される。

アドレス空間とは番地の集合である

アドレス空間とは、ある主体が指し示せる番地(アドレス)の全体集合です。n ビットのアドレスを使えば 0 から 2^n - 1 までの 2^n 個の番地を区別でき、これが空間のサイズになります。32 ビットなら 4 GiB、48 ビットなら 256 TiB です。ここで重要なのは、アドレス空間は「メモリの容量」ではなく「区別できる番地の範囲」だという点です。番地の先に DRAM があるとは限らず、デバイスのレジスタや何も存在しない穴(ホール)であることもあります。

主体が誰かによって空間は分かれます。CPU コアが命令で扱う仮想アドレス空間、メモリコントローラやデバイスがバス上で見る物理アドレス空間、そして x86 が持つ独立したI/O ポート空間です。これらは別々の番地体系で、変換や写像によって結び付けられます。

物理アドレス空間とメモリマップ

物理アドレス空間は、CPU の外側(バス)に出た番地が指す世界です。ここには DRAM がまるごと割り付けられますが、それだけではありません。同じ番地体系の中に、デバイスのレジスタ・ROM(ファームウェア)・フレームバッファなどが区間を割り当てられて同居します。この割り付けの設計図をメモリマップと呼びます。

物理アドレス空間(例: 下位4GiB付近)
0x0000_0000  ┌────────────┐
             │ DRAM        │ ← 通常の主記憶
0x8000_0000  ├────────────┤
             │ (ホール)     │ ← 何もない/予約
0xC000_0000  ├────────────┤
             │ MMIO        │ ← デバイスレジスタ
0xFEE0_0000  │ Local APIC  │
0xFFFF_0000  │ Boot ROM    │ ← リセットベクタ
0xFFFF_FFFF  └────────────┘

DRAM が 4 GiB あっても、その一部の番地が MMIO に取られていると、その範囲の DRAM は隠れて見えなくなります(メモリホール)。これが昔の 32 ビット機で「4 GB 積んでも 3.x GB しか使えない」現象の正体です。

MMIO とポートマップド I/O

CPU がデバイスのレジスタを読み書きする方式は、大きく2系統あります。

項目メモリマップドI/O (MMIO)ポートマップドI/O (PMIO)
番地体系物理アドレス空間に同居独立したI/O空間
アクセス命令通常のload/store専用命令(IN/OUT)
空間サイズアドレス幅ぶん広大x86は64KiBと狭い
キャッシュuncacheable指定が必須そもそも対象外
採用Arm/RISC-V等ほぼ全てx86の一部レガシ

メモリマップド I/O は、デバイスレジスタを物理アドレス空間の一区画に写像し、普通の load/store でアクセスします。専用命令が要らず、ポインタ経由で扱えるのが利点で、Arm や RISC-V はこの方式に統一しています。

ポートマップド I/O は x86 固有で、メモリとは別の 16 ビット幅(0〜65535)のポート空間を持ち、INOUT 命令でのみアクセスします。バス上では専用の信号でメモリアクセスと区別されます。歴史的経緯で残るものの、PCIe など現代のデバイスは設定レジスタもデータ転送も MMIO に寄せており、PMIO は縮小傾向です。

MMIO はキャッシュしてはならない

デバイスレジスタは「読むたびに値が変わる」「書くと副作用が起きる」ため、CPU が値をキャッシュしたり投機的に先読みしたり、書き込みをまとめたりすると正しく動きません。そのため MMIO 領域は uncacheable(または write-combining)に設定し、メモリオーダリングも厳しく扱う必要があります。メモリコンシステンシモデルで扱う順序保証が、ここで実害として効いてきます。

仮想アドレス空間とカーネル/ユーザー分割

仮想アドレス空間は、各プロセスに独立して与えられる番地の集合です。プロセスはあたかも空間全体を独占しているかのように振る舞え、互いのアドレスは衝突しません。CPU が命令で扱う番地はすべて仮想で、MMU がページ単位でこれを物理へ変換します。詳細は仮想メモリとTLBの内部で扱いますが、ここでは空間の構成に注目します。

64 ビット機でも実装するのは全 64 ビットではなく、典型的には 48 ビット(256 TiB)や 57 ビット程度です。この空間を上位と下位に二分し、上位を全プロセス共通のカーネル空間、下位を各プロセス固有のユーザー空間に割り当てるのが定石です。

仮想アドレス空間(x86-64, 48bit実装の例)
0x0000_0000_0000_0000  ┌──────────────┐
                       │ ユーザー空間   │ ← プロセス固有
                       │ (コード/ヒープ/ │   下位アドレス
                       │  スタック)      │
0x0000_7FFF_FFFF_FFFF  └──────────────┘
        (非正準アドレスの穴)
0xFFFF_8000_0000_0000  ┌──────────────┐
                       │ カーネル空間   │ ← 全プロセス共通
0xFFFF_FFFF_FFFF_FFFF  └──────────────┘

カーネル空間を高位に固定写像しておくと、システムコールや割り込みで特権モードへ入ったとき、ページテーブルを切り替えずにカーネルのコードとデータへ到達できます。ユーザーコードがカーネル領域を触ろうとすると、ページテーブルエントリの特権ビット(U/S)により保護違反となり、ページフォルトが上がります。実装上は 48 ビットの有効ビットを最上位まで符号拡張した正準(canonical)アドレスだけが有効で、中間の値はアクセス自体が例外になります。これにより上位・下位の二領域がきれいに分離されます。

アドレッシングモードと有効アドレス

命令がオペランドの在りかを指定する方法がアドレッシングモードです。CPU は指定から**有効アドレス(EA: effective address)**を計算し、それを仮想アドレスとして変換・アクセスに渡します。代表的なモードを示します。

モードオペランド指定有効アドレス用途例
即値命令内の定数(番地でなく値)定数の加算
レジスタレジスタ番号(番地でなくレジスタ)レジスタ間演算
直接命令内の絶対番地その番地グローバル変数
レジスタ間接レジスタ=番地(R)ポインタ参照
ベース+変位R+定数(R) + disp構造体メンバ
インデックス+スケールB+I×s+disp(B) + (I)×s + disp配列要素
PC相対PC+変位(PC) + disp位置独立コード

即値レジスタは番地計算を伴わず、オペランドが命令やレジスタの中にそのまま在ります。直接は絶対番地を命令に埋め込みます。実務で多用されるのはベース+変位で、ベースレジスタに構造体の先頭ポインタ、変位にメンバのオフセットを置けば、構造体->メンバ が一発で表せます。

配列アクセスに効くのがインデックス+スケールです。x86-64 の最も一般的な形は次の計算をハードウェアの**アドレス生成ユニット(AGU)**が1サイクルで行います。

有効アドレス = ベース + インデックス × スケール + 変位
EA = (B) + (I) * s + disp        ; s ∈ {1, 2, 4, 8}

たとえば int 配列[i](要素 4 バイト)なら、ベースに配列先頭、インデックスに i、スケールに 4 を置けば arr + i*4 が一命令で求まります。スケールが要素サイズに対応するため、添字をバイト数へ変換する乗算が不要になります。PC 相対はプログラムカウンタを基準にした変位で、コードをどの番地に置いても正しく動く位置独立コード(PIC)や、近傍の定数・分岐先の参照に使われます。RISC では命令長を一定に保つため即値の幅が限られ、大きな定数は上位/下位に分けて2命令で合成します。

アドレッシングモードは ISA の性格を映す

CISC(x86)は1命令に複雑な EA 計算(ベース+インデックス×スケール+変位)を許し、命令密度を稼ぎます。RISC(Arm/RISC-V)はロードストア型で、メモリ参照を load/store に限定し、モードもベース+変位中心に絞ってデコードを単純化します。どこまでをハードのアドレス計算に任せるかは、命令セットアーキテクチャの設計思想そのものです。

計算から実アクセスまでの流れ

一つのメモリ参照命令が実行される道筋を追うと、3つの空間がどう連なるかが見えます。

  1. 有効アドレス計算: AGU がベース+インデックス×スケール+変位を加算し、仮想アドレスを得る。
  2. アドレス変換: その仮想アドレスを TLB/ページテーブルで物理アドレスへ変換し、同時に特権・読み書き権限を検査する。
  3. 空間の振り分け: 物理アドレスがメモリマップ上どの区画かで、DRAM・MMIO・ROM のどれへ向かうかが決まる。MMIO ならキャッシュを迂回してデバイスへ届く。

この一貫した流れがあるからこそ、プログラマは「ポインタを足し引きする」だけで、その先が主記憶でもデバイスレジスタでも同じ構文で扱えます。x86 の PMIO だけは2の変換を経ず、専用命令で I/O 空間へ直接向かう例外的な経路をたどります。

試験のポイント

「アドレス空間サイズ=2のアドレス幅乗(番地数)であって搭載メモリ量ではない」「MMIO は通常のload/storeで物理空間に同居、PMIO はIN/OUT命令で独立空間」「仮想空間は上位カーネル/下位ユーザーに分割し特権ビットで保護」「有効アドレス=ベース+インデックス×スケール+変位」は頻出です。MMIO 領域は uncacheable にする理由(副作用と最新値の保証)も問われます。

まとめ

  • アドレス空間は番地の集合であり、物理空間には DRAM だけでなく MMIO・ROM がメモリマップで同居する。x86 はこれと別に I/O ポート空間を持つが、現代は MMIO 主流。
  • 仮想アドレス空間はプロセスごとに独立し、上位をカーネル・下位をユーザーに固定写像して特権ビットで保護する。CPU が扱う番地はすべて仮想で、MMU が物理へ変換する。
  • アドレッシングモードは即値・レジスタ・ベース+変位・インデックス×スケールなどがあり、有効アドレスは「ベース+インデックス×スケール+変位」で計算してから変換・アクセスへ渡される。

メモリへ届いた後の挙動はメモリコントローラのスケジューリングが、デバイスへの大量転送がアドレス空間とどう関わるかはDMAとIOMMUの原理が掘り下げます。

CPU/メモリ/ディスク Article

アドレス空間とアドレッシングモード ─ 物理/仮想/I/Oマップを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

アドレス空間

比較で見る軸

難易度: advanced / カテゴリ: CPU/メモリ/ディスク / タグ数: 5

導入後に効く点

仮想アドレス空間はプロセスごとに独立で、ページテーブルとMMU/TLBが物理へ変換する。上位をカーネル、下位をユーザーに分けて固定写像し、特権ビットで保護する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
CPU/メモリ/ディスク
タグ数
5

判断チェックリスト

  • 自社の用途が「アドレス空間 / 仮想メモリ」に近いか確認する。
  • 強みである「アドレス空間は番地の集合で、物理アドレス空間にはDRAMだけでなくMMIOやROMがメモリマップで割り付けられる。x86はI/O命令で別系統のポート空間も持つが、現代のデバイスは大半がMMIO側に寄る。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

アドレス空間仮想メモリMMIOアドレッシングモード有効アドレスアドレス空間仮想メモリMMIO
参考: 公式情報