WAIT(2) | Linux Programmer's Manual | WAIT(2) |
名前
wait, waitpid, waitid -プロセスの状態変化を待つ書式
#include <sys/types.h>glibc 向けの機能検査マクロの要件 ( feature_test_macros(7) 参照):
説明
これらのシステムコールはいずれも、呼び出し元プロセスの子プロセスの状態変化を待ち、状態が変化したその子プロセスの情報を取得するのに使用される。状態変化とは以下のいずれかである: 子プロセスの終了、シグナルによる子プロセスの停止、シグナルによる子プロセスの再開。子プロセスが終了した場合は、wait を実行することで、システムがその子プロセスに関連するリソースを解放できるようになる。 wait が実行されなかった場合には、終了した子プロセスは「ゾンビ」状態で残り続ける (下記の注意の章を参照のこと)。wait() と waitpid()
wait() システムコールは、子プロセスのいずれかが終了するまで呼び出し元のプロセスの実行を一時停止する。呼び出し wait(&status) は以下と等価である:
waitpid(-1, &status, 0);
waitpid() システムコールは、 pid 引き数で指定した子プロセスの状態変化が起こるまで、呼び出し元のプロセスの実行を一時停止する。デフォルトでは、 waitpid() は子プロセスの終了だけを待つが、この動作は options 引き数により変更可能である。
pid に指定できる値は以下の通り:
- <-1
- プロセスグループID が pid の絶対値に等しい子プロセスのいずれかが終了するまでを待つ。
- -1
- 子プロセスのどれかが終了するまで待つ。
- 0
- プロセスグループID が呼び出したプロセスのものと等しい子プロセスを待つ。
- > 0
- プロセスID が pid に等しい子プロセスを待つ。
options の値は次の定数の 0 個以上の論理和である:
- WNOHANG
- 状態変化が起こった子プロセスがない場合にすぐに復帰する。
- WUNTRACED
- 子プロセスが停止した場合にも復帰する (子プロセスが ptrace(2) でトレースされている場合は除く)。このオプションが指定されていない場合でも、停止したプロセスが「トレース (traced)」されていれば、子プロセスの状態が報告される。
- WCONTINUED (Linux 2.6.10 以降)
- 停止した子プロセスが SIGCONT の配送により再開した場合にも復帰する。
(Linux 専用オプションについては後述する)
status が NULL でなければ、 wait() や waitpid() は status で指す int に状態情報を格納する。この整数は以下のマクロを使って検査できる。 (これらのマクロの引き数には、 wait() や waitpid() が書き込んだ整数そのものを指定する。ポインタではない!)
- WIFEXITED( status )
- 子プロセスが正常に終了した場合に真を返す。「正常に」とは、 exit(3) か _exit(2) が呼び出された場合、もしくは main() から復帰した場合である。
- WEXITSTATUS( status )
- 子プロセスの終了ステータスを返す。終了ステータスは status 引き数の下位 8ビットで構成されており、 exit(3) や _exit(2) の呼び出し時に渡された値、もしくは main() の return 文の引き数として指定された値である。このマクロを使用するのは WIFEXITED が真を返した場合だけにすべきである。
- WIFSIGNALED( status )
- 子プロセスがシグナルにより終了した場合に真を返す。
- WTERMSIG( status )
- 子プロセス終了の原因となったシグナルの番号を返す。このマクロを使用するのは WIFSIGNALED が真を返した場合だけにすべきである。
- WCOREDUMP( status )
- 子プロセスがコアダンプを生成した場合に真を返す。このマクロを使用するのは WIFSIGNALED が真を返した場合だけにすべきである。このマクロは POSIX.1-2001 では規定されておらず、 (AIX, SunOS などの) いくつかの UNIX の実装では利用できない。必ず #ifdef WCOREDUMP ... #endif で括って使用すること。
- WIFSTOPPED( status )
- 子プロセスがシグナルの配送により停止した場合に真を返す。これが真になるのは、システムコールが WUNTRACED を指定して呼び出された場合か、子プロセスがトレースされている場合 ( ptrace(2) 参照) だけである。
- WSTOPSIG( status )
- 子プロセスを停止させたシグナルの番号を返す。このマクロを使用するのは WIFSTOPPED が 0 以外を返した場合だけにすべきである。
- WIFCONTINUED( status )
- (Linux 2.6.10 以降) 子プロセスが SIGCONT の配送により再開した場合に真を返す。
waitid()
waitid() システムコール (Linux 2.6.9 以降で利用可能) を使うと、子プロセスのどの状態変化を待つかについてより細かな制御ができる。- idtype == P_PID
- プロセスID が id と一致する子プロセスを待つ。
- idtype == P_PGID
- プロセスグループID が id と一致する子プロセスを待つ。
- idtype == P_ALL
- 子プロセス全部を対象に待つ。 id は無視される。
子プロセスのどの状態変化を待つかは以下のフラグで指定する ( options には 1個以上のフラグの論理和をとって指定する):
- WEXITED
- 子プロセスの終了を待つ。
- WSTOPPED
- 子プロセスがシグナルの配送により停止するのを待つ。
- WCONTINUED
- (停止していた) 子プロセスが SIGCONT が配送されて再開するのを待つ。
さらに以下のフラグを論理和の形で options に指定できる:
- WNOHANG
- waitpid() と同様。
- WNOWAIT
- waitable 状態のプロセスをそのままにする。この後で wait コールを使って、同じ子プロセスの状態情報をもう一度取得することができる。
成功した場合には、 waitid() は infop が指す siginfo_t 構造体の以下のフィールドを設定する:
- si_pid
- 子プロセスのプロセスID。
- si_uid
- 子プロセスの実ユーザID (このフィールドは他のほとんどの実装では設定されない)。
- si_signo
- 常に SIGCHLD が設定される。
- si_status
- _exit(2) (か exit(3)) に指定された子プロセスの終了ステータス、もしくは子プロセスの終了、停止、再開の原因となったシグナルが設定される。このフィールドをどう解釈するかは、 si_code フィールドを参照して決めることができる。
- si_code
- 以下のいずれかが設定される: CLD_EXITED (子プロセスが _exit(2) を呼び出した); CLD_KILLED (シグナルにより子プロセスが kill された); CLD_DUMPED (シグナルにより子プロセスが kill され、コア・ダンプが行われた); CLD_STOPPED (シグナルにより子プロセスが停止した); CLD_TRAPPED (トレースされていた子プロセスがトラップを受信した); CLD_CONTINUED ( SIGCONT により子プロセスが再開された)。
WNOHANG が options に指定されていて、 waitable 状態の子プロセスがなかった場合には、 waitid() はすぐに 0 を返す。このとき、 infop が指す siginfo_t 構造体の内容は不定である。この場合を waitable 状態の子プロセスがあった場合と区別するには、 waitid() を呼び出す前に si_pid を 0 にしておき、コールが復帰した後でこのフィールドが 0 以外の値かどうかをチェックすればよい。
返り値
wait(): 成功すると、終了した子プロセスのプロセスID を返す。エラーの場合-1 を返す。エラー
- ECHILD
- ( wait() の場合) 呼び出し元プロセスには、wait を行っていない子プロセスはない。
- ECHILD
- ( waitpid() か waitid() の場合) pid ( waitpid()) か idtype と id ( waitid()) で指定したプロセスが存在しないか、呼び出し元プロセスの子プロセスでない ( SIGCHLD の動作に SIG_IGN を設定した場合には、自分自身の子プロセスでも起こりうる。スレッドに関しては「Linux での注意」の節も参照すること)。
- EINTR
- WNOHANG が設定されておらず、禁止 (block) されていないシグナルや SIGCHLD を受信した。 signal(7) 参照。
- EINVAL
- options 引き数が不正である。
準拠
SVr4, 4.3BSD, POSIX.1-2001.注意
終了したが、wait されていない子プロセスは「ゾンビ」になる。後で親プロセスが wait を実行して子プロセスについての情報を取得できるように、カーネルはゾンビプロセスについて最小限の情報 (PID、終了ステータス、リソース使用状況) を保持する。ゾンビプロセスは、 wait によってシステムから削除されない限り、カーネルのプロセステーブルの 1 エントリを消費する。このプロセステーブルが一杯になると、新たにプロセスを作ることができなくなる。親プロセスが終了すると、その親プロセスの「ゾンビ」の子プロセスは (もしあれば) init(8) の養子となる。 init(8) は wait を自動的に実行し、ゾンビを削除する。Linux での注意
Linux カーネルでは、カーネルによってスケジュールされるスレッドはプロセスと明確に区別できる構成要素ではない。スレッドは Linux 固有の clone(2) システムコールを使用して生成されるプロセスに過ぎない。移植性のある pthread_create(3) コールのような他のルーチンは clone(2) を使用して実装されている;これらでは waitid() を使うことはできない。 Linux 2.4 より前では、スレッドは単に特殊なプロセスであったので、例え同じスレッドグループであっても、あるスレッドが別のスレッドの子プロセスが終了するのを待つことは出来なかった。しかし、POSIX ではこのような機能を規定しており、 Linux 2.4 以降では、あるスレッドが同じスレッドグループの他のスレッドの子プロセスが終了するのを待つことができるようになった。そして将来はこれがデフォルトの動作になるであろう。clone(2) を用いて作られた子プロセスには、以下の Linux 固有の options が使用できる。
- __WCLONE
- "clone"な子プロセスだけを待つ。指定されなかった場合は非 "clone"な子プロセスだけを待つ ("clone"な子プロセスは、終了時に親プロセスへ全くシグナルを送らないか、 SIGCHLD 以外のシグナルを送る)。このオプションは __WALL も指定された場合は無視される。
- __WALL (Linux 2.4 以降)
- "clone"であるかないかに関わらず、全ての子プロセスを待つ。
- __WNOTHREAD (Linux 2.4 以降)
- 同じスレッドグループの他のスレッドの子プロセスは待たない。 Linux 2.4 より前ではデフォルトであった。
バグ
According to POSIX.1-2008, an application calling waitid() must ensure that infop points to a siginfo_t structure (i.e., that it is a non-NULL pointer). On Linux, if infop is NULL, waitid() succeeds, and returns the process ID of the waited-for child. Applications should avoid relying on this inconsistent, nonstandard, and unnecessary feature.例
以下のプログラムは、 fork(2) と waitpid() の使用方法の例を示している。このプログラムでは子プロセスを生成する。コマンドライン引き数が指定されなかったときは、子プロセスは pause(2) を使ってその実行を一時停止し、ユーザがその子プロセスにシグナルを送信できるようにする。コマンドライン引き数が指定された場合は、子プロセスは直ちに終了し、コマンドラインで指定された整数を終了ステータスとして使用する。親プロセスは、 waitpid() を使って子プロセスを監視し、 wait のステータス値を上記の W*() マクロを使って解析するというループを実行する。
$ ./a.out &
Child PID is 32360
[1] 32359
$ kill -STOP 32360
stopped by signal 19
$ kill -CONT 32360
continued
$ kill -TERM 32360
killed by signal 15
[1]+ Done ./a.out
$
プログラムのソース
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %ld\n", (long) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
}
関連項目
_exit(2), clone(2), fork(2), kill(2), ptrace(2), sigaction(2), signal(2), wait4(2), pthread_create(3), credentials(7), signal(7)この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.51 の一部である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。2012-12-21 | Linux |