TL

メモリマップトファイルとページフォルトの連携

巨大ファイルを read/write の繰り返しなしに扱える mmap が、ページフォルトと結びついてどう遅延ロードし、CoW や書き戻しを実現するのかを原理から理解できます。

応用mmapページフォルトpage cacheCoWmsyncメモリ管理最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.mmap は仮想アドレス空間に区間(VMA)を登録するだけで物理ページは載せず、初回アクセスのページフォルトが契機となってページ単位で中身を埋める遅延ロード機構です。
  • 2.ファイルバックド(page cache 経由でファイルへ写る)と匿名(出所がなくゼロ埋め)、MAP_SHARED(共有・書き戻しあり)と MAP_PRIVATE(CoW で私的化・書き戻しなし)の組で挙動が決まります。
  • 3.ダーティページの永続化は writeback か msync が担い、巨大ファイルは madvise・部分マップ・SIGBUS 対策で設計します。

mmap とページフォルトはなぜ一体なのか

mmap を呼んだ瞬間にやることは、ファイルやメモリ領域をプロセスの仮想アドレス空間の一区画に対応づけ、その区間を記述する VMA(virtual memory area) を登録することだけです。物理ページの割り当ても、ファイルからの読み込みも、この時点では一切行われません。ページテーブルのエントリ(PTE)は 無効(not present) のまま据え置かれます。

実際にデータが載るのは、プログラムがその仮想アドレスへ初めて触れた瞬間です。MMU が無効な PTE を見つけて ページフォルト を起こし、カーネルのハンドラが VMA を引いて「この区間は何のマッピングか」を判定し、必要なページを用意して PTE を埋め、命令を再実行させます。つまり mmap の遅延ロードは、ページフォルト機構そのものを土台にして成り立っています。両者の連携は分離できません。基礎の流れは デマンドページングとページフォルト処理 を参照してください。

mmap が登録するのは「約束」であって「実体」ではない

mmap が返すポインタは、まだ中身のないアドレス範囲への約束です。約束を実体(resident なページ)に変えるのはアクセスであり、その変換器がページフォルトハンドラです。だから mmap 自体は巨大ファイルでも一瞬で返り、コストは触ったページにだけ後払いで発生します。

ファイルバックド vs 匿名:フォルトの埋め方が分岐する

ページフォルトハンドラは、フォルトしたアドレスの属する VMA がどの種類かで、ページの中身の用意の仕方を変えます。最大の軸が ファイルバックド(file-backed)か匿名(anonymous)か です。

観点ファイルバックド匿名(anonymous)
生成方法fd を渡した mmap(実行ファイル/データ)MAP_ANONYMOUS、ヒープ、スタック
初回フォルトの中身page cache 経由でファイルの該当オフセットを読むゼロ初期化したページを与える
裏付け(退避先)元のファイル本体スワップ領域(あれば)
初回コストcache ヒットならマイナー、ミスならメジャー(I/O)多くはマイナー(ゼロページ)

ファイルバックドマッピング では、フォルトしたページに対応するファイルオフセットを page cache から探します。すでに誰か(別プロセスや過去のアクセス)が読み込んでいればそのページに PTE を繋ぐだけで済み、I/O のないマイナーフォルトになります。無ければファイルから読み込むメジャーフォルトになり、I/O 完了までプロセスはブロックされます。鍵は、mmap が page cache のページを 直接ユーザー空間に見せている 点です。read のようにカーネルバッファからユーザーバッファへコピーする一段が省け、同じ物理ページを共有できます。

匿名マッピング には対応するファイルがありません。中身の出所がないので、初回フォルトでは ゼロで初期化したページ を与えます。多くの実装は、読み取りの初回フォルトを 1 枚の共有ゼロページに読み取り専用で繋ぎ、書き込みが来て初めて専用ページを割り当てる最適化を持ちます。匿名ページが追い出されるときの退避先はファイルではなくスワップで、仕組みは スワップ機構の内部実装 を参照してください。

MAP_SHARED と MAP_PRIVATE:共有か CoW か

もう一つの軸が、書き込みの見え方を決める MAP_SHARED / MAP_PRIVATE のフラグです。これはファイルバックドのときに最も意味を持ちます。

観点MAP_SHAREDMAP_PRIVATE
書き込みの伝播page cache を共有 → 最終的にファイルへ反映私的コピーに書く → ファイルには反映されない
他プロセスとの可視性同じファイルを共有マップした全員に見える自分だけに見える
書き込み時の動作共有ページに直接書く(ダーティ化)書き込みフォルトで CoW、私的ページを複製
代表用途共有メモリ IPC、ファイルの永続更新実行ファイルの .data、フォーク後の私的改変

MAP_SHARED では、マップした全プロセスが同じ page cache ページを共有します。誰かの書き込みは全員に見え、ダーティ化されたページはやがてファイル本体へ書き戻されます。これがファイルを介した共有メモリ IPC の実体です。

MAP_PRIVATEコピーオンライト(CoW) で動きます。読み取りの間は元の page cache ページを読み取り専用で共有しますが、書き込もうとすると読み取り専用ページへの書き込みとしてページフォルトが起き、ハンドラが そのページだけを複製 して書き込み側の PTE を私的コピーに繋ぎ直します。以後その書き込みは自分にしか見えず、ファイルにも他プロセスにも伝播しません。この CoW フォルトはコピー元が必ずメモリ上にあるためマイナーフォルトです。CoW 一般の論理は コピーオンライト(CoW) を参照してください。

同じ「書き込みフォルト」でも意味が三通りある

読み取り専用 PTE への書き込みフォルトは文脈で意味が変わります。MAP_PRIVATE の元ページなら CoW 複製、共有ゼロページへの初回書き込みなら専用ページの割り当て、本当に書き込み禁止の領域(.text など)なら権限違反で SIGBUS/SIGSEGV です。ハンドラは VMA の本来の許可と PTE の現状を突き合わせて見分けます。

四つの組み合わせを通しで押さえると、{ファイルバックド, 匿名} × {SHARED, PRIVATE} で挙動が一意に決まります。匿名 + PRIVATE は通常のヒープ/スタック、匿名 + SHARED はフォーク後も共有される無名共有メモリ、ファイル + SHARED は永続更新と IPC、ファイル + PRIVATE は実行ファイルの初期化済みデータ領域のように使われます。

msync とダーティページの書き戻し

MAP_SHARED でファイルバックドページに書き込むと、そのページは ダーティ(dirty、変更済みで未保存) とマークされます。ダーティページが実際にディスクへ書かれるのは、次のいずれかの契機です。

  • カーネルの writeback スレッドが周期的・あるいはダーティ比率が閾値を超えたときに非同期で書き出す
  • msync を明示的に呼んで書き戻しを要求する
  • マッピングを munmap する、またはプロセスが正常終了する

ここで重要なのは、普通の書き込みは永続性を保証しない 点です。p[i] = x はメモリ操作にすぎず、writeback がいつ走るかはカーネル任せです。電源断やクラッシュの時点で未書き戻しのダーティページは失われます。永続性が必要なら、書き込み後に msync(addr, len, MS_SYNC) を呼び、ディスクへの到達を待ち合わせます。MS_ASYNC は書き戻しのスケジュールだけ行って待ちません。writeback 機構そのものは ページキャッシュとライトバック で詳説しています。

char *p = mmap(NULL, len, PROT_READ | PROT_WRITE,
               MAP_SHARED, fd, 0);
p[off] = value;               /* ダーティ化。まだディスクには無い */
msync(p, len, MS_SYNC);       /* ここで書き戻しを待ち合わせ、永続化を保証 */
munmap(p, len);
msync を省くと「書いたつもり」になる

ファイルへ確実に反映したいのに msync を呼ばず、munmap や exit に任せると、クラッシュ時にデータを失います。逆に、毎回 MS_SYNC で同期するとフォルトのたびに I/O 待ちが入り、mmap の利点であるシステムコール削減が帳消しになります。永続性の要求点でだけ同期する設計が要ります。

巨大ファイル処理の設計

mmap は巨大ファイルのランダムアクセスで本領を発揮しますが、ページフォルトの性質を踏まえた設計が要ります。

第一に アクセスパターンのヒント。逐次に舐めるなら madvise(MADV_SEQUENTIAL) で先読み窓を広げ、メジャーフォルトをまとめて減らせます。ランダムなら MADV_RANDOM で無駄な先読みを止め、page cache の汚染を防ぎます。直後に使う範囲が分かっていれば MADV_WILLNEED で先行ロード、もう使わない範囲は MADV_DONTNEED で物理ページを解放できます。

第二に マップ範囲の制御。64bit 空間なら数十 GB のファイルでもアドレス的にはマップできますが、触れたページはすべて RSS と page cache を圧迫します。必要な範囲だけ window 状にマップして使い終えたら munmap する、あるいは page cache 量を madvise(MADV_DONTNEED) で抑える設計が有効です。書き込み主体の逐次処理では、むしろ write のほうがダーティページ管理の点で素直なこともあります。

第三に SIGBUS への備え。ファイルバックドマッピングでは、マップ済み領域がファイル末尾を越えたページや、別プロセスに truncate で切り詰められた領域に該当する場合、そのページに触れた瞬間にページフォルトを解決できず SIGBUS が送られます(VMA 外アクセスの SIGSEGV とは区別されます)。共有ファイルを mmap するプログラムは、ファイルサイズの変化に対する取り決めやエラーハンドリングを設計に含める必要があります。

試験・面接で問われる勘どころ

(1)mmap は VMA 登録だけで、ロードはページフォルト契機という遅延性。(2)ファイルバックド=page cache 経由でファイルへ写る/匿名=ゼロ埋めでスワップ退避、の区別。(3)MAP_SHARED は共有・書き戻しあり、MAP_PRIVATE は CoW で私的化・ファイル不変。(4)永続性は writeback か msync が担い、普通の代入は保証しない。(5)切り詰めや末尾越えは SIGBUS。この 5 点が頻出です。

まとめ

  • mmap は VMA を登録するだけ で物理ページを載せず、初回アクセスの ページフォルトを契機 にページ単位で中身を埋める。遅延ロードはページフォルト機構の上に成り立つ。
  • ファイルバックド は page cache 経由でファイルへ写り、ヒットでマイナー・ミスでメジャー。匿名 は出所がなくゼロ埋めで、退避先はスワップ。
  • MAP_SHARED は共有して最終的にファイルへ書き戻し、MAP_PRIVATE は書き込みフォルトを CoW で私的化しファイルには反映しない。
  • ダーティページの永続化は writeback か msync が担い、普通の代入は保証しない。巨大ファイル は madvise・部分マップ・SIGBUS 対策で設計する。

土台は デマンドページングとページフォルト処理、入門は メモリマップトファイル(mmap)、書き戻しは ページキャッシュとライトバック、CoW の詳細は コピーオンライト(CoW) も合わせて読むと全体像が繋がります。

OS Article

メモリマップトファイルとページフォルトの連携を実務で読む

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

解決すること

mmap

比較で見る軸

難易度: advanced / カテゴリ: OS / タグ数: 6

導入後に効く点

ファイルバックド(page cache 経由でファイルへ写る)と匿名(出所がなくゼロ埋め)、MAP_SHARED(共有・書き戻しあり)と MAP_PRIVATE(CoW で私的化・書き戻しなし)の組で挙動が決まります。

先に潰すリスク

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

数字・仕様の読み方
難易度
advanced
カテゴリ
OS
タグ数
6

判断チェックリスト

  • 自社の用途が「mmap / ページフォルト」に近いか確認する。
  • 強みである「mmap は仮想アドレス空間に区間(VMA)を登録するだけで物理ページは載せず、初回アクセスのページフォルトが契機となってページ単位で中身を埋める遅延ロード機構です。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

mmapページフォルトpage cacheCoWmsyncmmapページフォルトpage cache
参考: 公式情報