3 ポイント 投稿者 GN⁺ 2025-11-05 | まだコメントはありません。 | WhatsAppで共有
  • Linuxのプロセスメモリ構造を実際の動作レベルで説明し、仮想アドレス空間と物理メモリの関係を段階的に解説
  • ページテーブル、VMA、mmap、page fault、CoW などの中核メカニズムを中心に、プロセスがメモリをどのように所有しアクセスするかを具体的に説明
  • /proc ファイルシステムを通じて プロセスごとのメモリ状態 を観察する方法と、pagemapkpageflags など高度な診断ツールの役割を紹介
  • Transparent Huge Pages (THP)userfaultfdPAGEMAP_SCAN など最新カーネル機能による性能最適化とユーザー空間ダーティトラッキング手法を扱う
  • Meltdown対策のPTITLBフラッシュW^Xポリシー などセキュリティと性能に関わるカーネル設計原則もあわせて説明し、Linuxメモリ管理の全体像への理解を提供

プロセスメモリの基本構造

  • プログラムが実行されると巨大な連続メモリがあるように見えるが、実際には Linuxカーネルがページ単位で動的に構成 している
    • CPUはページテーブルを参照して仮想アドレスを物理フレームへ変換
    • マッピングがなければ ページフォルト(page fault) が発生し、カーネルが新しいページを割り当てるかエラーを返す
  • 物理RAMが不足すると、カーネルは未使用ページをディスクへ退避したり、ファイルページを削除したりして空間を確保
  • /proc はカーネルがメモリ上に構成した 仮想ファイルシステム で、プロセスやカーネルの状態をファイルとして公開する

アドレス空間とVMA

  • 各プロセスは 1つのアドレス空間オブジェクト を持ち、その内部は複数の VMA(Virtual Memory Area) で構成される
    • VMAは同じ権限(R/W/X)と同じバックエンド(匿名メモリまたはファイル)を持つ連続アドレス範囲
  • ページテーブルはハードウェアが参照する構造で、仮想ページと物理ページ間のマッピング情報(PTE) を保存する
  • アドレス空間の変更は3つのシステムコールで行われる
    • mmap: 新しい領域を作成
    • mprotect: 権限を変更
    • munmap: マッピングを削除
  • ページは 4KiBの基本単位 で、一部のシステムは2MiB・1GiBの大きなページもサポートする

/proc/self/mapsで見るメモリ構成

  • cat /proc/self/maps コマンドでプロセスのメモリマップを確認できる
    • 実行ファイルのコード・データ・bss、ヒープ、匿名マッピング、共有ライブラリ、スタックなどが表示される
  • [vdso][vvar] 領域は、カーネルがマッピングした 高速システムコール用のコードとデータ である

mmap の動作原理

  • mmap は実際のメモリ割り当てではなく、アドレス空間に対する約束を記録する もの
    • ページは最初にアクセスされた時点で割り当てられる
  • ファイルマッピングでは offset はページ境界に揃っている必要があり、ファイル終端を超えるアクセスでは SIGBUS が発生する
  • MAP_SHARED はファイルに直接反映され、MAP_PRIVATE書き込み時コピー(CoW) により独立したページを生成する
  • MAP_FIXED_NOREPLACE は指定アドレスにすでにマッピングがある場合に失敗させることで安全性を確保する

最初のアクセスとページフォルト

  • 新しいマッピングへの最初のアクセス時、CPUがページテーブルを見つけられないと ページフォルト が発生する
    • カーネルはアドレスの妥当性、アクセス権、存在有無を検査する
    • 匿名マッピングなら0で埋めた新しいページを割り当て、ファイルマッピングならページキャッシュから読み込む
  • minor fault はデータがすでにRAM上にある場合、major fault はディスクI/Oが必要な場合である
  • スタックは ガードページ で保護されており、下方向へ行き過ぎたアクセスでは SIGSEGV が発生する

fork()MAP_PRIVATE のCopy-on-Write

  • fork 時には親と子が 同じ物理ページを共有 し、両方とも読み取り専用としてマークされる
    • 書き込み時点でのみ新しいページが複製され、独立性が保たれる
  • MAP_PRIVATE のファイルマッピングも同じ原理で動作する
  • 関連オプション
    • vfork: 親のアドレス空間を共有
    • clone(CLONE_VM): スレッドを生成
    • MADV_DONTFORK, MADV_WIPEONFORK: 子プロセスでマッピングを除外、または0で初期化

権限変更とTLB無効化

  • mprotect でページ権限を変更すると、カーネルは VMAの分割とページテーブルの修正 を行い、その後 TLB無効化 を実施する
  • W^Xポリシー により、ページは同時に書き込み・実行可能にはできない
  • TLB(Translation Lookaside Buffer)は最近のアドレス変換を保持するキャッシュで、無効化時には一時的な遅延が発生する

/proc を通じた詳細観察

  • /proc/<pid>/mapssmapssmaps_rollup で領域ごとの権限・RSS・HugePage使用量を確認できる
  • /proc/<pid>/pagemap はページ単位の状態(存在、スワップ、PFNなど)を提供するが、PFNは一般ユーザーには非公開
  • /proc/kpagecount/proc/kpageflags はPFNごとのマッピング数とページ属性(匿名、ファイル、dirty など)を表示する
  • mincoreSEEK_DATA/SEEK_HOLE により疎ファイルのデータ/ホール領域を識別できる
  • PAGEMAP_SCANuserfaultfd を組み合わせることで ユーザー空間ダーティトラッキング を実装できる

Transparent Huge Pages (THP) と mTHP

  • THP は頻繁にアクセスされるメモリを自動的に大きなページ(2MiBなど)へまとめ、TLB効率を向上 させる
    • khugepaged スレッドが隣接ページをマージする
  • mTHP は16KiB・64KiBなど、さまざまなサイズの 可変大型ページ(folio) をサポートする
  • /proc/self/smapsAnonHugePagesFilePmdMapped で使用有無を確認できる
  • /sys/kernel/mm/transparent_hugepage/ でシステム全体の設定を管理する
  • MADV_HUGEPAGEMADV_NOHUGEPAGE で領域単位の制御が可能

ユーザー空間ダーティトラッキング

  • userfaultfdPAGEMAP_SCAN を使うことで 変更されたページだけをコピー できる
    • カーネルが1回の原子的操作でスキャンと書き込み保護を行う
    • スナップショット、ライブマイグレーションなどで効率的

TLBフラッシュのメカニズム

  • x86におけるTLB無効化には2つの方式がある
    • INVLPG: 単一ページを無効化
    • ページテーブルルートの再読み込みによる全体フラッシュ
  • PCIDINVPCIDプロセスごとのTLBタグ管理 により不要なフラッシュを減らす
  • tlb_single_page_flush_ceiling は、カーネルがページ単位フラッシュと全体フラッシュのどちらを選ぶかのしきい値である

Meltdown対策: Page Table Isolation (PTI)

  • Meltdown は投機実行中にカーネルデータがキャッシュ経由で漏えいしうる脆弱性である
  • Linuxは PTI(Page Table Isolation) によりユーザー・カーネルのアドレス空間を分離する
    • エントリ時に CR3 を切り替えてカーネル専用ページテーブルを使う
    • PCID を活用してTLBフラッシュを最小化する
  • デフォルトで有効化されており、nopti で無効化できる

カーネルの安全なマッピング変更手順

  • マッピング変更時の順序
    1. キャッシュ規則を処理
    2. ページテーブルを修正
    3. TLBを無効化
  • カーネル内部マッピング(vmapvmalloc)でもI/Oの前後でキャッシュ・TLB同期を行う
  • 一部アーキテクチャではコードコピー後に 命令キャッシュのフラッシュ が必要になる

x86のスタックと呼び出し構造

  • 64ビットモードでは RIP、RSP、RBP レジスタを使用し、スタックは下方向へ成長する
  • System V AMD64 ABIに従い、引数は RDI、RSI、RDX、RCX、R8、R9、戻り値は RAX で受け渡される
  • ユーザーモードは ring 3、カーネルは ring 0 で、システムコールや割り込みはゲートを通じて切り替わる

エラー状況と診断

  • mmapEINVAL: ファイルオフセットの整列エラー
  • mmapENOMEM: 仮想空間不足または overcommit 制限
  • ファイルマッピングアクセス時の SIGBUS: EOF超過アクセス
  • mprotect(PROT_EXEC)EACCES: noexec マウントまたは W^Xポリシー
  • fork() 後のRSS増加: CoWによるページ複製
  • MAP_FIXED で既存マッピングを上書き → MAP_FIXED_NOREPLACE 推奨

実務向けチェックリスト

  • 即時にメモリを確保: mmap + PROT_READ|PROT_WRITE + MAP_PRIVATE|MAP_ANONYMOUS
  • コード生成時: W^Xを維持し、mprotect(PROT_READ|PROT_EXEC) を使う
  • ファイルマッピング時: offset をページ境界に揃え、EOF超過アクセスを禁止
  • ページフォルトが多いとき: MADV_WILLNEED または事前アクセス
  • メモリ使用量分析: /proc/<pid>/smaps_rollup/proc/<pid>/maps
  • 大規模プロセスの fork: CoWを考慮し、子では exec を使う
  • 遅延に敏感な環境: THP/mTHP、mlock、TLBの挙動を観察

まだコメントはありません。

まだコメントはありません。