C 言語で waitpid 関数を使用する

胡金庫 2023年10月12日
  1. 関数 waitpid を使って C 言語で子プロセスの状態を監視する
  2. マクロを使って C 言語で子プロセスの待ち状態を表示する
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 が呼び出されると親プロセスは中断され、監視している子プロセスの状態が変化するまで実行を再開しないことに注意してください。次の例では、WUNTRACEDWCONTINUED を引数に指定して 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);
}
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C Process