今回は fork(2) で子プロセスの終了理由を判定してみる。 結論から先に述べると、子プロセスの終了を待つとき wait(2) に int のポインタを渡すと終了理由をセットしてくれる。 それをマクロで判定していけば良い。
使った環境は次のとおり。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=20.04 DISTRIB_CODENAME=focal DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS" $ uname -rm 5.4.0-91-generic aarch64
もくじ
下準備
あらかじめ C のビルドに必要な build-essential をインストールしておく。
$ sudo apt-get update $ sudo apt-get install -y build-essential
子プロセスの終了理由を判定してみる
以下に、子プロセスの終了理由を「exit(3)」「シグナル」「その他」に判定するサンプルコードを示す。 前述したとおり、親プロセスが wait(2) で子プロセスの終了を待つときに、引数として int のポインタを渡してやる。 すると、終了理由を示す数値がセットされるので、それをマクロで判定してやる。 子プロセスでは bash を起動している。
#define _GNU_SOURCE #include <sched.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> int main(int argc, char *argv[]) { // プロセスを fork(2) でフォークする pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "Failed to fork a new process: %s\n", strerror(errno)); exit(-1); } if (pid != 0) { // PID が非ゼロは親プロセス // wait(2) で子プロセスの完了を待つ int status; wait(&status); // 子プロセスの終了の仕方を出力する if (WIFEXITED(status)) { // WIFEXITED() が非ゼロなら exit(3) による終了 // WEXITSTATUS() で終了コードが得られる printf("exit(3), status=%d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { // WIFSIGNALED() が非ゼロならシグナルによる終了 // WTERMSIG() でシグナル番号が得られる printf("signal, sig=%d\n", WTERMSIG(status)); } else { // それ以外の終了 printf("aborted"); } exit(EXIT_SUCCESS); } // 以降は子プロセスでの処理 // 子プロセスでシェル (Bash) を起動する char* const exec_argv[] = {"bash", NULL}; if (execvp(exec_argv[0], exec_argv) < 0) { fprintf(stderr, "Failed to exec \"%s\": %s\n", exec_argv[0], strerror(errno)); exit(EXIT_FAILURE); } return EXIT_SUCCESS; }
ちなみに、理由を知る必要がないときは wait(2) の引数に NULL
を指定すれば良い。
上記をコンパイルする。
$ gcc -Wall example.c
現在の PID は 927 だった。
$ echo $$ 927
先ほどコンパイルしたバイナリを実行する。
$ ./a.out
すると、fork(2) した子プロセスで新たに bash が起動する。
$ echo $$ 1135
試しに組み込みの exit コマンドでプロセスを終了してみよう。
ステータスコードには適当に 10
を指定する。
$ exit 10 exit exit(3), status=10
上記のとおり、ちゃんと WIFEXITED
のブロックに入ってステータスコードが得られた。
続いてはシグナルで終了させてみよう。
$ echo $$ 927 $ ./a.out $ echo $$ 1536
子プロセスに KILL シグナルを送る。
$ kill -KILL 1536 signal, sig=9
上記のとおり、ちゃんと WIFSIGNALED
のブロックに入ってシグナルによる終了と判定された。
いじょう。 それ以外のパターンは、どう確認すれば良いのかな。