ページフォルトとは|マイナ・メジャの違いとLinuxでの診断・対策

ページフォルトとは — 概念の整理

ページフォルト(page fault)は、プログラムがアクセスしようとした仮想アドレスに対応するページ(仮想ページ→物理フレームの対応)が現在メモリに存在しない、またはアクセス条件が満たされない場合に発生する例外(トラップ)です。仮想メモリ方式を採るOSでは、プロセスは連続した仮想アドレス空間を使いますが、その背後にある実メモリ(物理メモリ)は限られるため、ページ単位で割当・管理します。ページフォルトはその管理機構が動作する契機になります。

ページフォルトの種類

  • マイナ(ソフト)ページフォルト(minor / soft fault)
    対応するページ自体は既に物理メモリ上にあるが、ページテーブルやアクセス権の一時的な不一致(例:ページテーブルにエントリがないが、別のキャッシュや副次構造で参照可能)などにより再設定だけで解決できるもの。ディスクI/Oを伴わないため比較的軽い。

  • メジャ(ハード)ページフォルト(major / hard fault)
    対応するページが物理メモリに存在せず、ディスク(スワップ領域やメモリマップされたファイル)からページを読み込む必要がある場合。ディスクI/Oが発生するため遅延が大きく、性能への影響が顕著。

  • 不正アクセスによるページフォルト(invalid)
    そのアドレスがプロセスの有効領域外/保護違反(例:書き込み禁止ページへの書き込み)などで発生するもの。OSはSIGSEGV(UNIX系)やアクセス違反としてプロセスを殺すか、例外処理を行う。

  • コピーオンライト(Copy-On-Write, COW)によるページフォルト
    フォーク後に親子で共有している読み取り専用ページに対して子プロセスが書き込みを試みると、OSは書き込み用にページを複製してプライベート領域へ割り当てる。これもページフォルトとして扱われるが、ディスクI/Oを伴わないことが多い。

ページフォルト発生時の基本的な処理フロー

  • CPUがメモリアクセスを行い、該当する仮想アドレスに対するページテーブルエントリ(PTE)を参照する。
  • 該当PTEが無効や不在ならば、CPUはページフォルト例外を発生させてカーネルへ制御を移す。
  • カーネルのページフォルトハンドラが発動。まずアクセスの合法性(アクセス権、アドレス有効性)をチェックする。違反ならプロセスにシグナルを送る(SIGSEGV等)。
  • 正当なページ欠如なら、バックアップ領域(スワップ、ファイル、メモリマップ領域)を調べ、必要なら空き物理フレームを確保する。フレームが不足していればページ置換ポリシーに従って犠牲ページを選び、必要ならディスクへ書き戻す。
  • 対象ページをディスクから読み込む(メジャフォルトの場合)か、ページテーブルの再設定(マイナフォルトやCOWの複製処理)を行う。
  • ページテーブルを更新し、必要ならTLB(翻訳キャッシュ)をフラッシュ/更新してユーザ処理を復帰させる。

TLB(Translation Lookaside Buffer)とページフォルトの関係

TLBは仮想アドレス→物理アドレス変換の高速キャッシュです。TLBミスが起きるとCPUはページテーブル参照を試みますが、その過程でページが無効であればページフォルトとなります。つまり、TLBミスは必ずしもページフォルトを招かない(ページが存在すれば単にページテーブル参照で済む)が、ページフォルトはTLBやページテーブルの状態とも密接に結びついています。

ページ置換アルゴリズム(代表例)

  • 最適置換(OPT): 理論上最良だが未来の参照を知る必要があるため実運用では使えない(ベースラインとしての理論)。
  • FIFO: 先入れ先出。実装は簡単だがBeladyの異常を生むことがある。
  • LRU(Least Recently Used): 最近使われていないページを追い出す。実装コストが高いので完全LRUは近似手法(ハードウェア支援やソフトウェア近似)で行われる。
  • Clock(第二チャンス): LRUの近似でよく使われる。参照ビットを巡回してクリア/残すロジックで犠牲ページを決める。

診断とモニタリング(主にLinuxを例に)

  • /proc/[pid]/stat:プロセス個別の情報。フィールドに minor/major page faults(minflt, majflt) が含まれる。

  • getrusage(getrusage の ru_minflt, ru_majflt):プロセスが経験したページフォルト数を取得できる。

  • vmstat, sar, iostat:システム全体のページング/スワップ活動を観察。vmstat の si/so(swap in/out)はスワップI/Oの度合いを示す。

  • perf, dtrace/eBPF:より詳細なイベント測定(ページフォルト発生箇所、スタックトレースなど)に使える。

  • Windowsでは Performance Monitor の "Page Faults/sec"(総ページフォルト)や "Page Reads/sec"(ディスク読み取り)を参照。

パフォーマンス上の意味と「スラッシング」

ページフォルト自体は仮想メモリの正常な動作の一部ですが、メジャフォルトが頻発してディスクI/OがCPUやアプリの進行を阻害すると「スラッシング」と呼ばれる状態になります。スラッシングではプロセスの作業集合(working set)をメモリに維持できず、ページ移動ばかりが発生してスループットが著しく低下します。スラッシングの対策は主にメモリ増設、ワーキングセットの削減、OSのスワップ/スケジューリング調整などです。

実務上の切り分けと対応策

  • まずページフォルトの種類を把握する。Linuxなら /proc//stat の minflt/majflt や getrusage を見る。メジャフォルトが多ければI/Oが原因であり、ディスクやスワップの問題を疑う。

  • メジャフォルトが多い場合の対策例:物理メモリの増設、スワップの削減(swappiness の調整)、アプリケーションのメモリ使用見直し、プロセスのメモリマップの使用方法を最適化、I/O性能向上(SSD化等)。

  • マイナフォルトが多くても必ずしも問題とは限らない(共有ページのマッピングやCOW処理で発生するため)。ただしCOWによるフォルトが頻繁であればプロセスのfork/execロジックを見直す。

  • 誤ったポインタ参照やアクセス権ミスでの無効ページフォルト(SIGSEGV)はバグなので、デバッガで原因コードを特定する。

  • 巨大なメモリ断片化・TLBミスが問題なら hugepages の導入などでページサイズを大きくしてTLB効率を上げる方法が有効な場合がある。

  • リアルタイムや応答性が重要なアプリケーションでは mlock()/mlockall() 等で重要ページをメモリに固定する方法もあるが、乱用するとシステム全体のメモリ運用に悪影響を与える。

OS別の実装上の特徴(概観)

  • Linux:ページングは demand paging を基本に行われ、ページ置換とスワップはカーネルのVMサブシステムが管理。/proc や vmstat で細かな情報が得られる。minflt/majflt はプロセス単位で取得可能。

  • Windows:ページフォルトは OS の仮想メモリマネージャが処理。パフォーマンスモニタで Page Faults/sec を観察できるが、この値はマイナー/メジャーを区別しない点に注意。ディスクI/O(Page Reads/sec)と併せて判断する。

  • macOS / BSD:同様に仮想メモリとページング機構を持つ。実装の詳細は異なるが基本概念は共通。

開発者向けの注意点・ベストプラクティス

  • 大きな領域を確保して逐次アクセスしない(例:巨大な配列をランダムアクセスで頻繁に参照する)とページフォルトが増える可能性がある。局所性(temporal / spatial locality)を意識してアクセスパターンを改善する。
  • fork を多用するサーバでは COW によるフォルトを考慮する。必要ならプロセス設計を見直す(スレッド化、事前ウォームアップなど)。
  • メモリマップ(mmap)を使う場合、アクセスパターンに応じたマッピング(MAP_POPULATE 等)やファイルI/Oのシンクロナイズを検討する。
  • プロファイラ/トレースツールでページフォルトの発生箇所を特定し、ホットスポットの最適化(データレイアウトの改善、キャッシュフレンドリーなアルゴリズムへの変更)を行う。

最後に(まとめ)

ページフォルトは仮想メモリの中心的な機構であり、正しく動作する限りは効率的なメモリ利用を可能にします。一方で、ディスクI/Oを伴うメジャページフォルトが多発するとシステム性能を大きく損なうため、発生原因の種類(マイナ/メジャ/不正)を正しく見分け、適切な診断と対策を行うことが重要です。

参考文献