# 機能設計書 2-メモリ管理（仮想メモリ）

## 概要

本ドキュメントは、FreeBSDカーネルにおける仮想メモリ管理機能の設計を記述する。仮想メモリ空間の管理、ページング、スワップ、物理メモリ割り当てなど、メモリ管理サブシステム全般を対象とする。

### 本機能の処理概要

**業務上の目的・背景**：仮想メモリ管理はオペレーティングシステムがアプリケーションに対して物理メモリ以上のアドレス空間を提供し、プロセス間のメモリ保護を実現するために不可欠な機能である。Mach VMアーキテクチャに由来するFreeBSDの仮想メモリシステムは、効率的なメモリ利用とプロセス隔離を同時に実現する。

**機能の利用シーン**：プロセス起動時のアドレス空間構築、malloc/mmap等によるメモリ割り当て、ページフォルト処理、メモリ不足時のスワップアウト、fork時のCopy-on-Write処理、共有メモリの管理など、システムのあらゆるメモリ操作で利用される。

**主要な処理内容**：
1. **vm_map管理**: 仮想アドレス空間のマッピング管理。vm_map_entry構造体によるアドレス範囲の管理、マッピングの挿入・削除・検索を行う。
2. **vm_object管理**: 仮想メモリオブジェクト（ファイルバッキング、無名メモリ等）の管理。シャドウオブジェクトチェーンによるCOW実現。
3. **vm_page管理**: 物理ページフレームの管理。空きページリスト、ページ状態遷移（free/inactive/active/laundry）の制御。
4. **ページフォルト処理**: vm_fault()による仮想アドレスから物理ページへの解決。デマンドページング、COWフォルトの処理。
5. **スワップ管理**: swap_pagerによるスワップ領域へのページアウト・ページイン処理。

**関連システム・外部連携**：プロセス管理（fork時のvmspace_fork）、ファイルシステム（mmap時のvnodeページャ）、デバイスドライバ（デバイスページャ）、スワップデバイスとの連携がある。

**権限による制御**：mlock()によるページロックにはスーパーユーザ権限または適切なリソース制限設定が必要。mmap/mprotectの権限フラグ（PROT_READ/WRITE/EXEC）によるページ保護。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 仮想メモリ管理はカーネル内部機能であり、直接的なUI画面は存在しない |

## 機能種別

カーネル基盤機能（仮想メモリ管理・ページング・スワップ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| mmap_args.addr | void * | No | マッピング先仮想アドレス（ヒント） | ページアライメント |
| mmap_args.len | size_t | Yes | マッピングサイズ | 0より大きいこと |
| mmap_args.prot | int | Yes | 保護属性（PROT_READ等） | 有効なフラグの組み合わせ |
| mmap_args.flags | int | Yes | MAP_SHARED/MAP_PRIVATE等 | 矛盾するフラグの排除 |
| mmap_args.fd | int | No | ファイルディスクリプタ | MAP_ANONの場合は-1 |
| mmap_args.offset | off_t | No | ファイルオフセット | ページアライメント |
| munmap_args.addr | void * | Yes | アンマップ先アドレス | ページアライメント |
| munmap_args.len | size_t | Yes | アンマップサイズ | 0より大きいこと |

### 入力データソース

システムコール経由のユーザ空間要求（mmap, munmap, mprotect, madvise, mlock等）およびカーネル内部からの呼び出し（fork時のvmspace_fork、exec時のexec_new_vmspace等）。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| mmap戻り値 | void * | マッピングされた仮想アドレス、失敗時はMAP_FAILED |
| ページフォルト結果 | int | 成功（物理ページ割り当て済み）または失敗（SIGSEGV送信） |

### 出力先

プロセスの仮想アドレス空間（ページテーブル更新）、スワップデバイス。

## 処理フロー

### 処理シーケンス

```
1. メモリマッピング（mmap）
   ├─ sys_mmap() → kern_mmap()
   │    ├─ vm_mmap_object(): オブジェクト生成
   │    ├─ vm_map_find(): 空きアドレス検索
   │    └─ vm_map_insert(): マップエントリ挿入
   │
2. ページフォルト処理
   ├─ trap() → vm_fault()
   │    ├─ vm_map_lookup(): マップエントリ検索
   │    ├─ vm_page_alloc(): 物理ページ割り当て
   │    ├─ pmap_enter(): ページテーブル更新
   │    └─ COWの場合: ページコピー＋新オブジェクト作成
   │
3. スワップ処理
   ├─ vm_pageout_worker(): ページアウトデーモン
   │    ├─ vm_pageout_scan(): スキャン対象ページ選択
   │    ├─ swap_pager_putpages(): スワップ書き出し
   │    └─ vm_page_free(): ページ解放
```

### フローチャート

```mermaid
flowchart TD
    A[メモリアクセス] --> B{TLBヒット?}
    B -->|Yes| C[物理メモリアクセス]
    B -->|No| D[ページテーブル参照]
    D --> E{ページテーブルエントリ有効?}
    E -->|Yes| F[TLBリフィル]
    E -->|No| G[ページフォルト]
    G --> H{vm_map_lookup成功?}
    H -->|No| I[SIGSEGV]
    H -->|Yes| J{ページ種別}
    J -->|COW| K[ページコピー]
    J -->|デマンド| L[ページ割り当て]
    J -->|スワップ| M[スワップイン]
    K --> N[pmap_enter]
    L --> N
    M --> N
    N --> C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | COWセマンティクス | fork時のメモリページは書き込み時にのみコピー | fork後の書き込みアクセス時 |
| BR-02 | デマンドページング | ページは実際にアクセスされるまで物理メモリを割り当てない | 新規マッピングアクセス時 |
| BR-03 | ページアウト閾値 | 空きメモリが低水位マークを下回るとページアウト開始 | vm_pageout_wakeup_handler |
| BR-04 | スーパーページ | 連続した物理ページをまとめて大きなTLBエントリに | ハードウェアサポートがある場合 |
| BR-05 | OOMキラー | メモリ枯渇時にプロセスを強制終了 | 物理メモリ+スワップが枯渇した場合 |

### 計算ロジック

ページアウト対象の選択にはNot Recently Used (NRU)アルゴリズムのバリエーションを使用。ページのアクセスビットと変更ビットに基づいて優先度を決定する。

## データベース操作仕様

### 操作別データベース影響一覧

本機能はカーネル内データ構造を操作する。RDBMSは使用しない。

| 操作 | 対象データ構造 | 操作種別 | 概要 |
|-----|-------------|---------|------|
| mmap | vm_map | INSERT | マップエントリの挿入 |
| munmap | vm_map | DELETE | マップエントリの削除 |
| fault | vm_page | UPDATE | ページ状態の遷移（free→active等） |
| pageout | vm_page | UPDATE | ページ状態の遷移（active→laundry→free） |

### テーブル別操作詳細

#### vm_map_entry

| 操作 | 項目（フィールド名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | start, end | マッピング範囲 | ページアライメント必須 |
| INSERT | protection | PROT_READ/WRITE/EXEC | ユーザ指定 |
| INSERT | object | バッキングオブジェクト | vnodeまたはanonymous |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENOMEM | メモリ不足 | 物理メモリ・スワップ枯渇 | メモリ解放またはスワップ追加 |
| EINVAL | 引数不正 | 不正なアドレスアライメントまたはサイズ | 正しいパラメータを指定 |
| EACCES | 権限不足 | 保護属性違反のアクセス | mprotect()で権限変更 |
| SIGSEGV | セグメンテーションフォルト | マップされていない領域へのアクセス | プログラムのバグ修正 |
| SIGBUS | バスエラー | マップされたファイル範囲外アクセス | ファイルサイズ確認 |

### リトライ仕様

ページフォルト処理中にページが他スレッドにより回収された場合、vm_fault()は内部でリトライを行う。

## トランザクション仕様

vm_mapはmap->lockによるリーダ・ライタロックで保護される。vm_objectはobject->lockによるリーダ・ライタロックで保護される。vm_pageはページキュー毎のmutexで保護される。ネストしたロック取得順序は vm_map → vm_object → vm_page の順に厳守される。

## パフォーマンス要件

- ページフォルト処理: マイクロ秒オーダー（物理メモリ内）
- スワップイン: ディスクI/O速度に依存（ミリ秒オーダー）
- vm_map_find: Splay木による対数オーダーの検索
- TLBミス: ハードウェアページウォークにより高速処理

## セキュリティ考慮事項

- W^X（Write XOR Execute）ポリシーの強制が可能
- ASLR（Address Space Layout Randomization）によるアドレス空間配置のランダム化
- mmap時のMAC（Mandatory Access Control）チェック
- カーネルとユーザ空間の厳格な分離（スーパバイザビットによるページ保護）
- guard pageによるスタックオーバーフロー検出

## 備考

- FreeBSDのVMシステムはMach VMを基盤としており、vm_object/vm_mapの二層構造が特徴
- UMA（Universal Memory Allocator）はカーネル内のスラブアロケータとしてvm上に構築される
- vm_page構造体はシステム内の全物理ページを表現し、ページ状態の遷移がVM全体の動作を決定する

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | vm_map.h | `sys/vm/vm_map.h` | struct vm_map、struct vm_map_entryの定義。アドレス空間の表現方法 |
| 1-2 | vm_object.h | `sys/vm/vm_object.h` | struct vm_objectの定義。バッキングストア抽象化 |
| 1-3 | vm_page.h | `sys/vm/vm_page.h` | struct vm_pageの定義。物理ページ状態管理 |
| 1-4 | pmap.h | `sys/vm/pmap.h` | pmapインタフェース定義。アーキテクチャ依存のページテーブル操作 |

**読解のコツ**: FreeBSDのVMは「vm_map → vm_map_entry → vm_object → vm_page → pmap」の階層構造になっている。vm_mapが仮想アドレス空間全体、vm_objectがバッキングストア、vm_pageが物理ページ、pmapがハードウェアページテーブルに対応する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | vm_mmap.c | `sys/vm/vm_mmap.c` | sys_mmap()、sys_munmap()等のシステムコールハンドラ |
| 2-2 | vm_fault.c | `sys/vm/vm_fault.c` | vm_fault()（1671行目）: ページフォルトハンドラの主要関数 |

**主要処理フロー**:
1. **1671行目（vm_fault.c）**: vm_fault()がページフォルトの種別を判定し処理を分岐
2. **938行目（vm_map.c）**: vm_map_entry_create()でマップエントリを生成
3. **1862行目（vm_map.c）**: vm_map_insert()でマッピングを挿入
4. **2133行目（vm_map.c）**: vm_map_find()で空きアドレス範囲を検索して挿入

#### Step 3: ページ管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | vm_page.c | `sys/vm/vm_page.c` | 物理ページの割り当て・解放・状態遷移（5947行） |
| 3-2 | vm_object.c | `sys/vm/vm_object.c` | VMオブジェクトのライフサイクル管理（2886行） |

#### Step 4: スワップ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | swap_pager.c | `sys/vm/swap_pager.c` | スワップページャの実装（3488行）。ページアウト・ページイン処理 |
| 4-2 | vm_glue.c | `sys/vm/vm_glue.c` | カーネルとVMの接続層 |

### プログラム呼び出し階層図

```
sys_mmap()
    |
    +-- kern_mmap()
            +-- vm_mmap_object()
                    +-- vm_map_find()
                            +-- vm_map_findspace()
                            +-- vm_map_insert()

vm_fault() [ページフォルトハンドラ]
    |
    +-- vm_map_lookup()
    +-- vm_page_alloc()
    +-- pmap_enter()
    +-- [COW] vm_page_copy() + vm_object_allocate()

vm_pageout_worker() [ページアウトデーモン]
    |
    +-- vm_pageout_scan()
            +-- swap_pager_putpages()
            +-- vm_page_free()
```

### データフロー図

```
[入力]                  [処理]                         [出力]

ユーザ mmap()要求 ──> vm_map_find() ─────────────> 仮想アドレス返却
                      vm_map_insert()

ページフォルト ────> vm_fault() ──────────────────> 物理ページマッピング
                    vm_map_lookup()                  pmap更新
                    vm_page_alloc()

メモリ不足 ────────> vm_pageout_worker() ──────────> スワップデバイス書出
                    swap_pager_putpages()            ページ解放
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| vm_map.c | `sys/vm/vm_map.c` | ソース | 仮想アドレス空間マップ管理（5455行） |
| vm_page.c | `sys/vm/vm_page.c` | ソース | 物理ページ管理（5947行） |
| vm_fault.c | `sys/vm/vm_fault.c` | ソース | ページフォルトハンドラ（2497行） |
| vm_object.c | `sys/vm/vm_object.c` | ソース | VMオブジェクト管理（2886行） |
| swap_pager.c | `sys/vm/swap_pager.c` | ソース | スワップページャ（3488行） |
| vm_mmap.c | `sys/vm/vm_mmap.c` | ソース | mmap等システムコール実装 |
| vm_glue.c | `sys/vm/vm_glue.c` | ソース | カーネル-VM接続層 |
| vm_kern.c | `sys/vm/vm_kern.c` | ソース | カーネルメモリ割り当て |
| vm_meter.c | `sys/vm/vm_meter.c` | ソース | VMメトリクス・統計情報 |
| vm_init.c | `sys/vm/vm_init.c` | ソース | VM初期化 |
| vm_map.h | `sys/vm/vm_map.h` | ヘッダ | vm_map構造体定義 |
| vm_page.h | `sys/vm/vm_page.h` | ヘッダ | vm_page構造体定義 |
| vm_object.h | `sys/vm/vm_object.h` | ヘッダ | vm_object構造体定義 |
| uma_core.c | `sys/vm/uma_core.c` | ソース | UMAスラブアロケータ |
