# 機能設計書 90-プロセス生成

## 概要

本ドキュメントは、libuvにおけるプロセス生成機能（uv_spawn）の設計仕様を記述する。uv_spawn()は子プロセスを生成し、標準入出力のリダイレクト、環境変数の設定、UID/GIDの変更、デタッチモードなど多様なオプションに対応する。

### 本機能の処理概要

**業務上の目的・背景**：外部コマンドや外部プログラムの実行はアプリケーション開発において頻繁に必要になる。uv_spawn()はfork+exec/posix_spawnのクロスプラットフォーム抽象化を提供し、非同期の子プロセス管理を実現する。プロセスの終了はコールバックで通知され、イベントループとシームレスに統合される。

**機能の利用シーン**：外部コマンドの実行（ビルドツール、コンパイラ等）、サーバープロセスのデーモン化、パイプラインによるプロセス間通信、子プロセスへの標準入出力のリダイレクト、権限分離のための別ユーザーでのプロセス実行などで使用される。

**主要な処理内容**：
1. プロセスハンドルをUV_PROCESSとして初期化する
2. 標準入出力用のパイプ/ソケットペアを作成する
3. SIGCHLDシグナルの監視を開始する（Linuxなど）
4. 子プロセスを生成する（macOSではposix_spawn優先、その他ではfork+exec）
5. 子プロセスの終了監視を登録する（kqueueではEVFILT_PROC、それ以外ではSIGCHLD）
6. 標準入出力ストリームをオープンする
7. プロセスハンドルをアクティブ化する

**関連システム・外部連携**：OSのプロセス管理機構（fork/exec、posix_spawn）、シグナル機構（SIGCHLD）、ファイルディスクリプタ管理、kqueueイベント機構（BSD系）と連携する。

**権限による制御**：UV_PROCESS_SETUIDフラグでUID変更、UV_PROCESS_SETGIDフラグでGID変更が可能。これらの操作にはroot権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 16 | プロセス | 主機能 | uv_spawn()による子プロセス生成のAPI定義 |
| 31 | ユーザーガイド一覧 | 遷移先機能 | プロセスガイド等の各章へのリンク |
| 37 | プロセス | 主機能 | uv_spawn()による子プロセス生成のサンプル解説 |

## 機能種別

プロセス管理 / 外部コマンド実行

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| loop | uv_loop_t* | Yes | イベントループ | NULLであってはならない |
| handle | uv_process_t* | Yes | プロセスハンドル | NULLであってはならない |
| options | const uv_process_options_t* | Yes | プロセス生成オプション | options->fileがNULLであってはならない |

#### uv_process_options_t構造体

| フィールド | 型 | 必須 | 説明 |
|-----------|-----|-----|------|
| exit_cb | uv_exit_cb | No | プロセス終了時コールバック |
| file | const char* | Yes | 実行するプログラムのパス |
| args | char** | No | コマンドライン引数（args[0]はプログラムパス） |
| env | char** | No | 環境変数（NULLの場合親プロセスの環境を継承） |
| cwd | const char* | No | 子プロセスの作業ディレクトリ |
| flags | unsigned int | No | プロセスフラグ（UV_PROCESS_DETACHED等） |
| stdio_count | int | No | stdioエントリ数 |
| stdio | uv_stdio_container_t* | No | 標準入出力設定配列 |
| uid | uv_uid_t | No | 子プロセスのユーザーID（UV_PROCESS_SETUIDフラグ時） |
| gid | uv_gid_t | No | 子プロセスのグループID（UV_PROCESS_SETGIDフラグ時） |

### 入力データソース

呼び出し元のアプリケーションコードから直接渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時0、失敗時はエラーコード |
| process->pid | int | 子プロセスのPID |
| exit_cb引数 exit_status | int64_t | プロセスの終了ステータス |
| exit_cb引数 term_signal | int | プロセスを終了させたシグナル番号 |

### 出力先

呼び出し元への戻り値。子プロセスの終了はexit_cbコールバックで非同期に通知される。

## 処理フロー

### 処理シーケンス

```
1. ハンドル初期化
   ├─ uv__handle_init()でUV_PROCESSとして初期化
   └─ キュー、ステータスの初期化
2. stdio準備
   ├─ stdio_countが3未満なら3に調整
   ├─ pipes配列の確保（8個以上ならヒープ確保）
   └─ 各stdioエントリのパイプ/FD初期化（uv__process_init_stdio）
3. SIGCHLD監視開始
   └─ [UV_USE_SIGCHLD] uv_signal_start(&child_watcher, uv__chld, SIGCHLD)
4. 子プロセス生成
   ├─ [macOS] posix_spawn優先（uv__spawn_and_init_child_posix_spawn）
   │      └─ ENOSYS時はfork/execにフォールバック
   └─ [その他] fork + exec（uv__spawn_and_init_child_fork）
5. プロセス監視登録
   ├─ [kqueue] EV_SET(EVFILT_PROC, NOTE_EXIT)で終了監視
   └─ [SIGCHLD] SIGCHLDシグナルベースの監視
6. ハンドル設定
   ├─ process->pid = pid
   ├─ process->exit_cb = exit_cb
   └─ uv__handle_start(process)
7. stdio ストリームオープン
   └─ 各stdioエントリのuv__process_open_stream
```

### フローチャート

```mermaid
flowchart TD
    A[uv_spawn 呼び出し] --> B[ハンドル初期化]
    B --> C[stdio パイプ準備]
    C --> D[SIGCHLD監視開始]
    D --> E{macOS?}
    E -->|Yes| F[posix_spawn 試行]
    F --> G{ENOSYS?}
    G -->|Yes| H[fork + exec フォールバック]
    G -->|No| I[プロセス生成完了]
    E -->|No| H
    H --> I
    I --> J{生成成功?}
    J -->|Yes| K[プロセス監視登録]
    K --> L[ハンドルアクティブ化]
    L --> M[stdio ストリームオープン]
    M --> N[return 0]
    J -->|No| O[クリーンアップ]
    O --> P[return エラーコード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-90-01 | stdio最小数 | stdio_countが3未満の場合、3に調整される（stdin/stdout/stderr） | stdio_count < 3 |
| BR-90-02 | macOS posix_spawn優先 | macOSではposix_spawnを優先使用し、ENOSYS時にfork/execにフォールバック | macOSプラットフォーム |
| BR-90-03 | SIGCHLD vs kqueue | BSD系ではkqueue EVFILT_PROCで子プロセスの終了を検出、その他ではSIGCHLDを使用 | プラットフォーム依存 |
| BR-90-04 | デタッチモード | UV_PROCESS_DETACHEDフラグでsetsid()が呼ばれ、プロセスグループリーダーになる | flags & UV_PROCESS_DETACHED |
| BR-90-05 | UID/GID変更 | UV_PROCESS_SETUID/SETGIDフラグでUID/GIDが変更される。setgroups(0, NULL)も呼ばれる | flags & UV_PROCESS_SETUID/SETGID |
| BR-90-06 | シグナルリセット | 子プロセスでは全シグナルがデフォルト動作にリセットされる | 常時 |

### 計算ロジック

特になし。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_ENOMEM | メモリ不足 | pipes配列のメモリ確保失敗 | リソース解放してリトライ |
| UV_EINVAL | 無効引数 | stdioのflagsが不正、またはstreamがUV_NAMED_PIPEでない | 引数を修正 |
| UV_ENOENT | ファイル不在 | 指定されたfileが見つからない | パスを確認 |
| UV_EACCES | 権限不足 | fileに実行権限がない | 権限を確認 |
| UV_EPIPE | パイプエラー | 子プロセス生成前にパイプが壊れた | リトライ |
| UV_ENOSYS | 未サポート | Apple TV/Watchではforkが禁止 | 対応プラットフォームで実行 |

### リトライ仕様

通常はリトライ不要。一時的なリソース不足の場合はリトライ可能。

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

トランザクション制御なし。ただし、cloexec_lockによる排他制御が行われ、fork中に他スレッドが新しいFDを開くことを防止する。

## パフォーマンス要件

- fork()はプロセスの仮想メモリサイズに依存する場合がある
- macOSではposix_spawnにより、MAP_JIT使用時のfork性能劣化を回避する

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

- UV_PROCESS_SETUID/SETGIDによる権限変更時、setgroups(0, NULL)で余分なグループを削除する
- 全シグナルハンドラがデフォルトにリセットされ、親プロセスのシグナル設定が子プロセスに漏れない
- fork前にcloexec_lockを取得し、FDリークを防止する
- CLOEXEC付きのパイプにより子プロセスへのFD漏洩を防止する

## 備考

- macOSではposix_spawnのPOSIX_SPAWN_CLOEXEC_DEFAULTフラグを使用し、明示的に操作されたFD以外は全て閉じられる。
- fork/execパスでは、signal_pipeを使用して子プロセスでのexec失敗を親プロセスに通知する。
- Apple TV/Apple Watchではfork()が禁止されているため、UV_ENOSYSが返される。
- pipes_storage[8][2]によりスタック上に8エントリ分のバッファを確保し、小規模なstdioの場合はヒープ確保を回避する。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | **1065-1108行目**: uv_process_options_t構造体。file、args、env、cwd、flags、stdio等のフィールド |
| 1-2 | uv.h | `include/uv.h` | **1056-1063行目**: uv_stdio_container_t構造体。flags、data（stream/fd）のunion |
| 1-3 | uv.h | `include/uv.h` | **1113-1163行目**: uv_process_flags列挙型。SETUID、SETGID、DETACHED等 |
| 1-4 | uv.h | `include/uv.h` | **1169-1174行目**: uv_process_s構造体。exit_cb、pid、プライベートフィールド |

**読解のコツ**: uv_process_options_tのstdioフィールドは可変長配列へのポインタで、各要素のflagsによってパイプ作成/FD継承/ストリーム継承が切り替わる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | process.c | `src/unix/process.c` | **966-1094行目**: uv_spawn()関数の実装 |

**主要処理フロー**:
1. **981-989行目**: flagsのバリデーション（assert）
2. **991-993行目**: ハンドル初期化
3. **995-997行目**: stdio_countの最小値3への調整
4. **999-1005行目**: pipes配列の確保
5. **1007-1016行目**: 各stdioエントリのパイプ初期化
6. **1018-1020行目**: SIGCHLD監視開始
7. **1023行目**: uv__spawn_and_init_child()で子プロセス生成
8. **1036-1057行目**: 成功時のプロセス監視登録とハンドルアクティブ化
9. **1059-1068行目**: stdioストリームのオープン

#### Step 3: 子プロセス生成の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | process.c | `src/unix/process.c` | **860-963行目**: uv__spawn_and_init_child()。macOSではposix_spawn優先、その後fork/execフォールバック |
| 3-2 | process.c | `src/unix/process.c` | **819-858行目**: uv__spawn_and_init_child_fork()。fork()の実行とシグナルブロック |
| 3-3 | process.c | `src/unix/process.c` | **287-417行目**: uv__process_child_init()。子プロセス内のセットアップ（シグナルリセット、stdio設定、chdir、UID/GID変更、exec） |

#### Step 4: macOS posix_spawnパスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | process.c | `src/unix/process.c` | **778-817行目**: uv__spawn_and_init_child_posix_spawn()。posix_spawnのattrs/file_actions設定と実行 |
| 4-2 | process.c | `src/unix/process.c` | **469-538行目**: uv__spawn_set_posix_spawn_attrs()。CLOEXEC_DEFAULT、SETSIGDEF等の設定 |
| 4-3 | process.c | `src/unix/process.c` | **678-775行目**: uv__spawn_resolve_and_spawn()。PATH解決とposix_spawn実行 |

#### Step 5: 子プロセス終了検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | process.c | `src/unix/process.c` | **75-78行目**: uv__chld()。SIGCHLDシグナルハンドラ |
| 5-2 | process.c | `src/unix/process.c` | **101-177行目**: uv__wait_children()。waitpid()による子プロセス終了の検出とexit_cbの呼び出し |

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

```
uv_spawn(loop, process, options)
    |
    +-- uv__handle_init(loop, handle, UV_PROCESS)
    +-- uv__process_init_stdio()  [各stdioエントリ]
    |       +-- uv_socketpair() [UV_CREATE_PIPE]
    |       +-- uv__stream_fd() [UV_INHERIT_STREAM]
    |
    +-- [SIGCHLD] uv_signal_start(&child_watcher, uv__chld, SIGCHLD)
    |
    +-- uv__spawn_and_init_child(loop, options, stdio_count, pipes, &pid)
    |       |
    |       +-- [macOS] uv__spawn_and_init_child_posix_spawn()
    |       |       +-- uv__spawn_set_posix_spawn_attrs()
    |       |       +-- uv__spawn_set_posix_spawn_file_actions()
    |       |       +-- uv__spawn_resolve_and_spawn()
    |       |               +-- posix_spawn()
    |       |
    |       +-- [fork/exec] uv__spawn_and_init_child_fork()
    |               +-- fork()
    |               +-- [子] uv__process_child_init()
    |                       +-- signal(SIG_DFL)
    |                       +-- setsid() [DETACHED]
    |                       +-- dup2() [stdio]
    |                       +-- chdir() [cwd]
    |                       +-- setgid()/setuid()
    |                       +-- execvp()
    |
    +-- [kqueue] kevent(EVFILT_PROC, NOTE_EXIT)
    +-- uv__handle_start(process)
    +-- uv__process_open_stream()  [各stdioエントリ]
```

### データフロー図

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

uv_process_options_t     uv_spawn()                    子プロセスPID
  file                      |                            exit_cb通知
  args                      +-- stdio準備
  env                       +-- fork/posix_spawn
  cwd                       +-- 終了監視登録
  flags                     |
  stdio[]                uv__wait_children()
                            |
                            +-- waitpid()
                            +-- exit_cb(process, exit_status, term_signal)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダ | API宣言（1176-1178行目）、構造体定義（1065-1174行目） |
| process.c | `src/unix/process.c` | ソース | uv_spawn()実装（966-1094行目）、子プロセス生成/初期化/終了検出 |
| uv-common.h | `src/uv-common.h` | ヘッダ | uv__handle_init/startマクロ |
| internal.h | `src/unix/internal.h` | ヘッダ | 内部関数宣言 |
