WAIT(2) | Linux Programmer's Manual | WAIT(2) |
wait, waitpid, waitid - プロセスの状態変化を待つ
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t
id, siginfo_t *infop, int
options);
/* これは glibc と POSIX
のインターフェイスである。
生のシステムコールについての情報は「注意」の節を参照。
*/
glibc
向けの機能検査マクロの要件
(feature_test_macros(7) 参照):
waitid():
これらのシステムコールはいずれも、呼び出し元プロセスの子プロセスの 状態変化を待ち、状態が変化したその子プロセスの情報を取得するのに 使用される。 状態変化とは以下のいずれかである: 子プロセスの終了、シグナルによる子プロセスの停止、 シグナルによる子プロセスの再開。 子プロセスが終了した場合は、wait を実行することで、 システムがその子プロセスに関連するリソースを解放できるようになる。 wait が実行されなかった場合には、終了した子プロセスは 「ゾンビ」状態で残り続ける (下記の注意の章を参照のこと)。
子プロセスの状態変化がすでに発生していた場合、これらのコールは すぐに復帰する。それ以外の場合は、子プロセスの状態変化が起こるか、 シグナルハンドラーによりシステムコールが中断されるまで、 停止 (block) する (後者は、 sigaction(2) の SA_RESTART フラグによりシステムコールが自動的に再スタートするようになっていない 場合の動作である)。 以下の説明では、状態変化が起こったがこれらのシステムコールのいずれかに よって待たれていない子プロセスを waitable (待ち可能) と呼ぶ。
wait() システムコールは、子プロセスのいずれかが終了するまで 呼び出し元のプロセスの実行を一時停止する。 呼び出し wait(&status) は以下と等価である:
waitpid(-1, &status, 0);
waitpid() システムコールは、 pid 引き数で指定した子プロセスの状態変化が起こるまで、 呼び出し元のプロセスの実行を一時停止する。デフォルトでは、 waitpid() は子プロセスの終了だけを待つが、この動作は options 引き数により変更可能である。
pid に指定できる値は以下の通り:
options の値は次の定数の 0 個以上の論理和である:
(Linux 専用オプションについては後述する)
status が NULL でなければ、 wait() や waitpid() は status で指す int に状態情報を格納する。 この整数は以下のマクロを使って検査できる。 (これらのマクロの引き数には、 wait() や waitpid() が書き込んだ整数そのものを指定する。ポインターではない!)
waitid() システムコール (Linux 2.6.9 以降で利用可能) を使うと、 子プロセスのどの状態変化を待つかについてより細かな制御ができる。
引き数 idtype と id でどの子プロセスを待つかを選択する:
子プロセスのどの状態変化を待つかは以下のフラグで指定する (options には 1個以上のフラグの論理和をとって指定する):
さらに以下のフラグを論理和の形で options に指定できる:
成功した場合には、 waitid() は infop が指す siginfo_t 構造体の以下のフィールドを設定する:
WNOHANG が options に指定されていて、 waitable 状態の子プロセスがなかった場合には、 waitid() はすぐに 0 を返す。このとき、 infop が指す siginfo_t 構造体の内容は不定である。 この場合を waitable 状態の子プロセスがあった場合と区別するには、 waitid() を呼び出す前に si_pid を 0 にしておき、コールが復帰した後でこのフィールドが 0 以外の値かどうか をチェックすればよい。
wait(): 成功すると、終了した子プロセスのプロセスID を返す。 エラーの場合 -1 を返す。
waitpid(): 成功すると、状態が変化した子プロセスのプロセスID を返す。 WNOHANG が指定されていて、 pid で指示された子プロセスが一つ以上存在するが、どの子プロセスでも 状態変化が起こっていなかった場合は、 0 を返す。 エラーの場合 -1 を返す。
waitid(): 成功すると 0 を返す。 WNOHANG が指定されていて、 pid で指示された子プロセスで状態変化が起こっていなかった場合にも 0 を返す。
エラーの場合 -1 を返す。 エラーの場合、これらのシステムコールはいずれも errno に適切な値を設定する。
SVr4, 4.3BSD, POSIX.1-2001.
終了したが、wait されていない子プロセスは「ゾンビ」になる。 後で親プロセスが wait を実行して子プロセスについての情報を取得できるように、 カーネルはゾンビプロセスについて最小限の情報 (PID、終了ステータス、 リソース使用状況) を保持する。 ゾンビプロセスは、 wait によってシステムから削除されない限り、 カーネルのプロセステーブルの 1 エントリーを消費する。このプロセステーブルが 一杯になると、新たにプロセスを作ることができなくなる。 親プロセスが終了すると、その親プロセスの「ゾンビ」の 子プロセスは (もしあれば) init(1) の養子となる。 init(1) は wait を自動的に実行し、ゾンビを削除する。
POSIX.1-2001 では以下のように規定されている。 SIGCHLD の動作が SIG_IGN に設定されたか、 SIGCHLD に対して SA_NOCLDWAIT フラグが設定された場合 (sigaction(2) 参照)、終了した子プロセスはゾンビにはならず、 wait() や waitpid() の呼び出しは全ての子プロセスが終了するまで停止し、 子プロセスが全部終了した後 errno に ECHILD を設定して失敗する。 (もともとの POSIX 標準は SIGCHLD に SIG_IGN を設定した場合の振る舞いを未規定のままにしている。 SIGCHLD のデフォルトの動作が「無視」であるにもかかわらず、 SIGCHLD の動作として SIG_IGN を明示的に設定した場合にはゾンビプロセスの子プロセスの扱いが 異なる点に注意すること。)
Linux 2.6 はこの仕様に準拠している。 しかし、Linux 2.4 (とそれ以前のバージョン) はそうではない: SIGCHLD が無視される状態で wait() または waitpid() が呼び出された場合、 SIGCHLD が無視されていないかのように振る舞う。 つまり、呼び出しによって次の子プロセスの終了までブロックされ、 終了した子プロセスの PID と状態が返される。
Linux カーネルでは、カーネルによってスケジュールされるスレッドは プロセスと明確に区別できる構成要素ではない。スレッドは Linux 固有の clone(2) システムコールを使用して生成されるプロセスに過ぎない。 移植性のある pthread_create(3) コールのような他のルーチンは clone(2) を使用して実装されている; これらでは waitid() を使うことはできない。 Linux 2.4 より前では、スレッドは単に特殊なプロセスであったので、 例え同じスレッドグループであっても、 あるスレッドが別のスレッドの子プロセスが終了するのを待つことは出来なかった。 しかし、POSIX ではこのような機能を規定しており、 Linux 2.4 以降では、あるスレッドが同じスレッドグループの他のスレッドの 子プロセスが終了するのを待つことができるようになった。 そして将来はこれがデフォルトの動作になるであろう。
clone(2) を用いて作られた子プロセスには、以下の Linux 固有の options が使用できる。
生の waitid() システムコールは struct rusage * 型の第 5 引数を取る。 この引数が NULL 以外の場合、 この引数が子プロセスのリソース使用状況を返すのに使用される。 これは wait4(2) と同じ方法である。 詳細は getrusage(2) を参照。
POSIX.1-2008 によると、 waitid() を呼び出すアプリケーションは、 infop が siginfo_t 構造体を指していること (つまり infop が NULL でないポインターであること) を保証しなければならない。 Linux では、 infop が NULL の場合、 waitid() は成功し、wait している子プロセスのプロセス ID を返す。 アプリケーションは、この食い違った、非標準で、不必要な機能に依存しないようにすべきである。
以下のプログラムは、 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.79 の一部である。 プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2014-08-19 | Linux |