子プロセスを安全に扱うためのチェックリスト

· · Windows, Process, Job Object, IPC, C++, .NET, C#

まず結論

Windows アプリで子プロセスを安全に扱うコツは、起動 API を選ぶことではなく、プロセス木の所有者を決め、終了手順と I/O を設計することです。

実務で最も効くポイント:

  • 親の生死と子プロセス木の寿命を結びつけたいなら、基準点は Job Object
  • コンソールへの終了依頼(GenerateConsoleCtrlEvent)とプロセス木の回収(Job Object)は別物
  • 起動時点から Job に入れたいなら STARTUPINFOEX + PROC_THREAD_ATTRIBUTE_JOB_LIST
  • 標準出力/標準エラーは並列に吸い上げるのが基本
  • stdin を使うなら書き終えたら close して EOF を伝える
  • watchdog は監視対象の Job のに置く

4つの設計要素

子プロセス管理は次の4つに分けて考えます:

  1. プロセス木を誰が所有するか
  2. どうやって協調終了を依頼するか
  3. 標準入出力をどう流すか
  4. 異常終了とハングをどう監視するか

Job Object を基準にする

Job Object の一番強い点は、「誰の子か」ではなく「どの Job に属するか」でプロセス木を束ねられることです。

押さえたい4つのポイント

  1. 親終了で木ごと片づけたいなら KILL_ON_JOB_CLOSE
    • 最後の job handle が閉じられたときに全プロセスが終了
    • 親の異常終了時も cleanup される
  2. BREAKAWAY を軽く付けない
    • cleanup できるつもりの木からプロセスが抜ける原因になる
  3. 起動時点から Job に入れたいなら PROC_THREAD_ATTRIBUTE_JOB_LIST
    • 後から AssignProcessToJobObject するより筋が良い
  4. job handle の所有者を曖昧にしない
    • handle を複製・継承すると、想定どおり cleanup されない

終了伝播を3段階で設計する

  1. 協調終了を依頼する
  2. 短い timeout で待つ
  3. 最後に Job ごと強制終了する

この順番にすれば、正常な終了経路を保ちつつ、ハング時は回収できます。

プロセスタイプ別の終了方法

  • GUI 子プロセス: CloseMainWindow → 待機 → Job kill
  • コンソール子プロセス: CREATE_NEW_PROCESS_GROUP + CTRL_BREAK_EVENT → 待機 → Job kill
  • Worker/headless: 専用の終了 protocol(stdin に quit、named pipe で shutdown 等)

標準入出力を詰まらせない

  1. stdout/stderr は並列 drain - 片方を全部読んでからもう片方、は詰まる
  2. stdin を使うなら EOF まで設計する - 書き終えたら close しないと子が待ち続ける
  3. 不要な pipe end を必ず閉じる - 閉じないと EOF が伝わらない
  4. UseShellExecute=false と handle 継承を正しく設定する

Watchdog は「外」に置く

  • 監視対象と同じ Job に入れない - worker が落ちたら再起動したいのに、再起動役まで死ぬ
  • exit 監視は wait handle ベース - polling ループより WaitForSingleObject など
  • UI スレッドで無限待機しない - メッセージポンプが止まる
  • hang watchdog には heartbeat が必要 - プロセスが生きているだけではハングは判定できない
  • 再起動役は監視対象の外に置く - worker tree と restart authority を分離
  • restart policy は budget で持つ - backoff、回数上限、連続失敗時の停止

典型パターン別の推奨構成

場面 推奨構成
単発 CLI helper 起動 1起動=1Job。KILL_ON_JOB_CLOSE、stdio 並列 drain
helper が孫プロセスを起動 Job Object 前提、breakaway 禁止
長時間 worker tree 監視 watchdog は外部プロセス。世代ごとに Job 作成
コンソールツールを丁寧に停止 CREATE_NEW_PROCESS_GROUP + CTRL_BREAK_EVENT
GUI helper を閉じる CloseMainWindow → timeout → Job kill

やってはいけないこと

  • Kill(entireProcessTree: true) だけで tree lifecycle が解けたと思う
  • stdout を全部読んでから stderr を読む
  • pipe の未使用 end を閉じない
  • UI スレッドで WaitForSingleObject(INFINITE) する
  • watchdog を監視対象と同じ Job に入れる

まとめ

子プロセス管理で最も効くのは次の4つを先に決めることです:

  • 誰が process tree を所有するか
  • どうやって終了要求を伝えるか
  • 標準入出力をどう流し切るか
  • watchdog をどこへ置くか

CreateProcessProcess.Start は入口にすぎません。本当に事故率に効くのは終了責任の所在I/O の流し切りです。

関連する記事

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

Windowsシングルバイナリ化の限界と実践

Windows アプリを 1 EXE にしたいときの「配布物を 1 個にする」と「OS 依存を消す」の違いを、.NET、C++、WebView2、WinUI、サービス、ドライバまで段階別に整理し、技術選定と配布設計の判断軸が分かります。

記事を読む

共有メモリの安全な使い方

共有メモリを実務で扱うときの典型的な落とし穴を整理し、同期、可視性、寿命、ABI、権限、クラッシュ復旧まで踏まえた事故率の低い設計の出発点を、Windows と POSIX の両方の API を交えながら具体的に示します。

記事を読む

ClickOnce 入門:配布・更新・選定基準

ClickOnce の仕組みと向き不向きを実務目線で整理します。マニフェスト、自動更新、キャッシュ分離、署名、配布経路の注意点に加え、社内業務アプリで強い理由と MSI/MSIX が適するケースの見分け方が分かります。

記事を読む

関連トピック

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

このテーマがつながるサービス

この記事は次のサービスページにつながります。近い入口からご覧ください。

ブログ一覧に戻る