HOWTO · C
C 言語で waitpid 関数を使用する
この記事では、C 言語での waitpid 関数を使用する方法を紹介します。
このページの内容
親プロセスが子プロセスの状態を取得するには、特定の子プロセスを選ぶ場合も、条件に合う任意の子プロセスを対象にする場合も、waitpid を使えます。引数で対象の子プロセスと報告する状態変化を指定し、返されたステータスは終了コードそのものとして扱わず、<sys/wait.h> のマクロで判定します。
特定の子プロセスを待つために waitpid を使う
プロセスは fork で子プロセスを作成できます。子プロセスは呼び出し位置から処理を続け、親プロセスは子プロセスのプロセス ID を受け取ります。終了した子プロセスの終了ステータスを取得し、そのプロセスを回収するには、親プロセスから wait 系の関数を呼び出します。
呼び出しの形式は waitpid(pid, &status, options) です。pid が正の値なら、そのプロセス ID の子プロセスを選択します。-1 はオプションに一致する任意の子プロセスを選択します。これは wait が使う選択方法です。0 は呼び出し元と同じプロセスグループの子プロセスを選び、-1 より小さい値は、pid の絶対値を ID とするプロセスグループの子プロセスを選びます。このように waitpid は対象を指定して待つのに便利ですが、子プロセスを待つ wait 系の関数はこれだけではありません。
第 2 引数は整数を指すポインターで、子プロセスの状態が報告されると、カーネルがエンコード済みのステータスを書き込みます。そのステータスが必要な場合は、有効なポインターを渡してください。第 3 引数はオプションのビットマスクです。0 を指定すると、選択した子プロセスの終了を待ちます。一致する状態変化がまだなければ、呼び出しはブロックします。
報告する子プロセスのイベントを選ぶ
WNOHANG を指定すると呼び出しは非ブロッキングになります。選択した子プロセスに報告可能な状態変化がなければ、waitpid は直ちに 0 を返します。その間、親プロセスは別の処理を行い、後で再確認できます。正の戻り値は、ステータスが書き込まれた子プロセスのプロセス ID です。WUNTRACED を指定すると、まだ報告されていない停止中の子プロセスも報告されます。WCONTINUED は、停止後に再開した子プロセスを報告します。これらのオプションは、呼び出しを完了させる状態変化を指定するもので、1 回の呼び出しが以後のすべての状態遷移を監視するわけではありません。フラグはビット単位の OR 演算子で組み合わせます。たとえば WUNTRACED | WCONTINUED のように指定します。
waitpid が返すステータスを判定する
整数のステータスはエンコードされているため、対応するマクロで判定します。WIFEXITED(status) は正常終了かどうかを調べ、その場合に WEXITSTATUS(status) で終了コードを取り出します。WIFSIGNALED(status) はシグナルによる終了を判定し、WTERMSIG(status) でそのシグナルを取得します。停止中の子プロセスを要求した場合は、WIFSTOPPED(status) と WSTOPSIG(status) で停止を判定できます。環境が対応し、再開状態を要求している場合は、WIFCONTINUED(status) で再開した子プロセスを判定できます。WCOREDUMP は一部のシステムでのみ使用できるため、使うコードではプリプロセッサの条件判定で利用可能性を確認してください。これらのマクロは <sys/wait.h> で宣言されています。
WIFEXITED が真でない限り WEXITSTATUS を呼び出さないでください。同様に、WIFSIGNALED が真でない限り WTERMSIG を呼び出さないでください。ステータスの整数自体は子プロセスの終了コードではありません。マクロを使えば、実装ごとに異なるビット配置に依存せずに判定できます。
戻り値とエラーを処理する
waitpid が正の値を返した場合、その値はステータスを取得した子プロセスの ID です。0 は一致するステータスがまだないことを示し、WNOHANG を指定した場合に返されます。-1 はエラーを示すため、errno を調べて原因を確認します。たとえば ECHILD は選択条件に一致する子プロセスがないことを、EINTR は捕捉されたシグナルによって待機が中断されたことを示します。EINTR の後も待機を続ける必要がある場合は呼び出しを再試行します。そうでなければ、アプリケーションのシグナル処理方針に従って中断を扱ってください。WNOHANG を指定しない呼び出しでは、選択した子プロセスが動作中で要求した状態変化が起きなければ、いつまでもブロックすることがあります。
子プロセスを扱う一連の例を実行する
この POSIX の例は子プロセスを 1 つ作成し、その終了を指定して待ちます。シグナルによって待機が中断された場合は再試行し、判定した終了ステータスを表示します。fork と waitpid を使い、別のプログラムや対話入力には依存しません。このコードは C コンパイラーのある POSIX 環境を対象としており、ISO C だけで移植可能なコードではありません。提示された実行記録では、表示例の出力とともに正常に実行され、終了コードは 0 でした。表示例のラベルは英語のままです。
次の例を実行します。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
pid_t child = fork();
if (child == -1) {
perror("fork");
return EXIT_FAILURE;
}
if (child == 0) {
_exit(7);
}
int status;
pid_t result;
do {
result = waitpid(child, &status, 0);
} while (result == -1 && errno == EINTR);
if (result == -1) {
perror("waitpid");
return EXIT_FAILURE;
}
if (WIFEXITED(status)) {
printf("child exit status: %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("child terminated by signal: %d\n", WTERMSIG(status));
}
return EXIT_SUCCESS;
}
child exit status: 7