C 言語で waitpid 関数を使用する
この記事では、C 言語で waitpid
関数を利用する方法について複数の方法を示します。
関数 waitpid
を使って C 言語で子プロセスの状態を監視する
Unix ベースのシステムでは、単にプログラムの実行中のインスタンスであるプロセスという概念があります。このプロセスは fork
システムコールを使って他のプロセスを作成し、コードの指定された部分を実行することができます。このトピックでは、システムコールは C スタイルの関数としてユーザに提供されるオペレーティングシステムのサービスであることに注意してください。一般的に、多くのシナリオでは、プログラムは子と呼ばれる自分が作成したプロセスを監視する必要があります。この機能を提供するのが wait
関数ファミリであり、waitpid
もその一つです。
wait
システムコールには複数の制限があり、より高度な機能をカバーするためには waitpid
関数を利用する必要があります。すなわち、プロセスが複数の子プロセスを生成し、親が特定の子プロセスを監視する必要がある場合、waitpid
だけがこれを行うことができます。次の例では、新しい子プロセスを生成して別のプログラムを実行する spawnChild
という名前の関数を実装します。この例の良いデモンストレーションのために、ユーザが終了するまで実行している top
プログラム(ほとんどすべての Unix ベースのシステムで利用可能)を実行しています。関数が親プロセスに戻ると、子プロセスの ID を格納し、それを waitpid
関数に渡してステータスを監視します。
waitpid
は 3つの引数を取り、最初の引数はプロセス ID 番号(pid)です。PID は異なる効果を持つ複数の値を指定することができますが、ここでは -1
と >0
のみを指定します。1
の値は、最初に状態を変更した子プロセスを監視するために渡すことができます。0
の値は fork
関数から返された実際のプロセス ID でなければならないことを意味し、特定の子プロセスのみを監視するために使われます。2 番目の引数は int
ポインタ型であり、そのアドレスを関数に渡すために整数変数を宣言しなければならません。一方、waitpid
は子プロセスの状態情報を int
変数に格納し、定義済みのマクロを使ってデコードすることができます。最後の引数は int
型で、デフォルトのイベントに加えて監視する特定の子プロセスイベントを指定するために使用されます。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
pid_t spawnChild(const char* program, char** arg_list) {
pid_t ch_pid = fork();
if (ch_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (ch_pid > 0) {
printf("spawn child with pid - %d\n", ch_pid);
return ch_pid;
} else {
execvp(program, arg_list);
perror("execve");
exit(EXIT_FAILURE);
}
}
int main(void) {
const char* args[] = {"top", NULL, NULL};
pid_t child;
int wstatus;
child = spawnChild("top", args);
if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
マクロを使って C 言語で子プロセスの待ち状態を表示する
関数 waitpid
が呼び出されると親プロセスは中断され、監視している子プロセスの状態が変化するまで実行を再開しないことに注意してください。次の例では、WUNTRACED
と WCONTINUED
を引数に指定して waitpid
を呼び出しているが、これは親プロセスが子プロセスの状態を監視していることを意味します。また、取得した子の状態を表示するために呼び出すことができる printWaitStatus
関数も実装しました。この関数は status
整数変数からエンコードされた情報を抽出するために <sys/wait.h>
ヘッダで定義されている W*
型マクロを利用していることに注目してください。
すべてのマクロがすべてのプラットフォームで利用できるわけではないので、いくつかの条件付きプリプロセッサの定義があります。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
pid_t spawnChild(const char* program, char** arg_list) {
pid_t ch_pid = fork();
if (ch_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (ch_pid > 0) {
printf("spawn child with pid - %d\n", ch_pid);
return ch_pid;
} else {
execvp(program, arg_list);
perror("execve");
exit(EXIT_FAILURE);
}
}
void printWaitStatus(int status) {
if (WIFEXITED(status)) {
printf("child exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("child killed by signal %d (%s)", WTERMSIG(status),
strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP
if (WCOREDUMP(status)) printf(" (core dumped)");
#endif
printf("\n");
} else if (WIFSTOPPED(status)) {
printf("child stopped by signal %d (%s)\n", WSTOPSIG(status),
strsignal(WSTOPSIG(status)));
#ifdef WIFCONTINUED
} else if (WIFCONTINUED(status)) {
printf("child continued\n");
#endif
} else {
printf("status=%x\n", (unsigned int)status);
}
}
int main(void) {
const char* args[] = {"top", NULL, NULL};
pid_t child;
int wstatus;
child = spawnChild("top", args);
if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
printWaitStatus(wstatus);
exit(EXIT_SUCCESS);
}