子プロセスを起動できたことと、その処理が終わったことは別です。さらに、処理が終わっていても、親が終了情報をまだ受け取っていない場合があります。waitpidを読むときは、この三つを分けると、待ち続ける理由やエラーの意味が見えやすくなります。
簡単にまとめると?
- waitpidの正の戻り値は、状態を受け取った子のPIDです。子の終了コードそのものではありません。
- WNOHANG付きで0が返るのは、対象の子が存在するものの、取得対象の状態変化がまだない場合です。−1のエラーとは分けます。
- 子が終了したことと、親が終了情報を回収したことは別の段階です。通常終了かシグナル終了かを確かめてから、対応する情報を読みます。
一つの呼び出しに、二つの結果がある
waitpidは、呼び出したプロセスの子について状態変化を待ち、その情報を受け取るインターフェースです。ここではLinux man-pages 6.19のwait(2)に沿い、waitpid(pid, &status, options)という形を見ます。[1]
pidに正の数を指定すると、そのPIDの子を対象にします。−1なら任意の子、0なら呼び出し時に親と同じプロセスグループの子が対象になるため、数字の符号にも意味があります。本稿の例では、特定の一つの子を対象にする正のpidだけを使います。
呼び出しの戻り値と、statusへ格納される情報は別です。戻り値が正なら、それは状態変化を取得した子のPIDです。一方、statusは状態の種類と詳細を含む値で、専用のマクロを使って解釈します。戻り値をそのまま子の終了コードとして表示したり、status全体を単純な成功・失敗の数値として扱ったりすると、別の情報を読んでしまいます。
通常終了を示すWIFEXITED(status)が真だった場合に、WEXITSTATUS(status)から終了コードを取り出します。シグナルで終了したなら、WIFSIGNALED(status)を確かめ、WTERMSIG(status)で原因のシグナル番号を読みます。どちらの種類かを確認する前に、詳細を取り出す順番にはしません。[1]
待つ場合と、今ある情報だけを調べる場合
オプションを0にした通常のwaitpidは、対象の子がまだ終了していなければ、呼び出したスレッドを待機させます。すでに回収できる終了情報があれば、すぐに戻ります。「wait」という名前でも、必ず一定時間待つわけではありません。
WNOHANGは、取得できる状態変化がないときに待ち続けない指定です。対象の子が存在し、まだ対象の変化がなければ戻り値は0です。0を「子が正常終了した」と扱うと、実行中の処理を完了として扱ってしまいます。ここではstatusを終了情報として使わず、後で状態を調べる機会を設けます。
戻り値が−1なら呼び出しが失敗しており、errnoを確認します。対象が子でない、すでに回収済みで待つべき子がない、といった場合のECHILDは、WNOHANGの0とは違います。また、待機中に捕捉したシグナルによってEINTRとなる場合もあります。再試行するかどうかは、そのエラーとアプリケーションの終了方針を見て決めます。
終了コード7の子を、一度ずつ調べる
独自の状態モデルで、PIDが4242の子を考えます。これは説明のために置いた識別子で、実在するプロセスには操作しません。子はトレースされておらず、親はSIGCHLDの扱いを明示的な無視や自動回収へ変更せず、ほかのスレッドも同じ子を回収しないと仮定します。使うオプションはWNOHANGだけです。
最初に子が実行中なら、調べた結果は0です。次に子が通常終了し、終了コード7の情報がまだ残っていれば、waitpidの戻り値は4242となり、statusを正しく解釈すると通常終了・終了コード7だと分かります。ここで「通常終了」は、終了方法がexitやmainからのreturnであることを示します。アプリケーションの仕事が成功した、という意味まで含みません。
この終了情報を取得すると、通常の待機では回収も行われます。同じ子だけをもう一度待とうとしても、同じ結果を何度でも取り出せるわけではありません。このモデルでは、二度目は−1とECHILDになります。実行中の0、終了情報を取得した正のPID、回収後の−1を、同じ「まだ返事がない」へまとめないことが要点です。

別の分岐として、子がシグナルで終了する場合もモデルで確認しました。この場合も状態を取得できれば戻り値は子のPIDですが、通常終了の分岐へ入らず、シグナル終了として読みます。戻り値だけでは、終了コード7とシグナル終了を区別できません。
終了しているのに、情報が残っている理由
子が終了しても、親が後から結果を受け取るために、PIDや終了状態などの最小限の情報が保持されます。終了済みで、まだ待機による回収をしていない子は、通常「ゾンビ」と呼ばれます。[1] 言葉の印象とは違い、子がそのままプログラムを実行し続けているという意味ではありません。
そのため、終了したかどうかを調べる設計と、誰が終了情報を回収するかという設計を一緒に決めます。子を複数起動するプログラムでは、任意の子を待つ処理と、特定の子を待つ処理が混在していないかも確認します。別の処理がすでに回収した情報を、後からもう一度待つつもりになっていないかが手掛かりになります。
ただし、すべての設定で同じ段階が残るわけではありません。SIGCHLDを明示的にSIG_IGNへ設定する場合やSA_NOCLDWAITを使う場合には、終了後の扱いが変わります。マニュアルは、SIGCHLDの既定の処置が無視と記されていることと、明示的にSIG_IGNへ設定することも区別しています。[1] 本稿の三段階を、その条件を変えたプログラムへそのまま当てはめません。
ログには、対象・戻り値・状態の種類を残す
不具合を調べる際は、対象にしたpid、オプション、呼び出しの戻り値を一組で残します。−1ならerrno、正の値なら状態の種類と、その種類に対応する詳細を追加します。0の回に以前のstatusの値が見えていても、それを新しい終了結果として扱わないようにします。
WUNTRACEDやWCONTINUEDを加えると、停止や再開も取得対象になります。トレースされている子にも別の条件があります。その場合、正のPIDが返ることを終了の証拠として扱えません。本稿はその全挙動を実装する例ではなく、トレースされていない特定の子について、通常終了の情報を読む基本の条件を固定した比較です。
独自モデルでは、実行中の照会、通常終了の回収、回収後の再照会、シグナル終了の分岐を確認しました。実機のスケジューリング、シグナル処理、スレッド間の競合や性能を試したものではありません。WNOHANGを短い間隔で無制限に呼ぶ設計を推奨するものでもなく、待つ方法や通知の受け取り方は、処理全体の設計として別途検討します。
出典
- Linux man-pages 6.19 — wait(2)(ページ日付2026年6月5日)。DESCRIPTION、RETURN VALUE、ERRORS、NOTES。2026年10月23日に掲載HTMLを確認。例のPID、状態遷移、図表は独自作成。特定版の記述を参照し、実機試験や他OSの動作保証とは区別。
