WAIT(2) | Linux Programmer's Manual | WAIT(2) |
wait, waitpid, waitid - プロセスの状態変化を待つ
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
int waitid(idtype_t idtype, id_t
id, siginfo_t *infop, int
options);
/* これは glibc と POSIX
のインターフェイスである。
生のシステムコールについての情報は「注意」の節を参照。
*/
waitid():
これらのシステムコールはいずれも、呼び出し元プロセスの子プロセスの 状態変化を待ち、状態が変化したその子プロセスの情報を取得するのに 使用される。 状態変化とは以下のいずれかである: 子プロセスの終了、シグナルによる子プロセスの停止、 シグナルによる子プロセスの再開。 子プロセスが終了した場合は、wait を実行することで、 システムがその子プロセスに関連するリソースを解放できるようになる。 wait が実行されなかった場合には、終了した子プロセスは 「ゾンビ」状態で残り続ける (下記の注意の章を参照のこと)。
子プロセスの状態変化がすでに発生していた場合、これらのコールは すぐに復帰する。それ以外の場合は、子プロセスの状態変化が起こるか、 シグナルハンドラーによりシステムコールが中断されるまで、 停止 (block) する (後者は、 sigaction(2) の SA_RESTART フラグによりシステムコールが自動的に再スタートするようになっていない 場合の動作である)。 以下の説明では、状態変化が起こったがこれらのシステムコールのいずれかに よって待たれていない子プロセスを waitable (待ち可能) と呼ぶ。
wait() システムコールは、子プロセスのいずれかが終了するまで 呼び出し元のスレッドの実行を一時停止する。 呼び出し wait(&wstatus) は以下と等価である:
waitpid(-1, &wstatus, 0);
waitpid() システムコールは、 pid 引数で指定した子プロセスの状態変化が起こるまで、 呼び出し元のスレッドの実行を一時停止する。デフォルトでは、 waitpid() は子プロセスの終了だけを待つが、この動作は options 引数により変更可能である。
pid に指定できる値は以下の通り:
options の値は次の定数の 0 個以上の論理和である:
(Linux 専用オプションについては後述する)
wstatus が NULL でなければ、 wait() や waitpid() は status で指す int に状態情報を格納する。 この整数は以下のマクロを使って検査できる。 (これらのマクロの引数には、 wait() や waitpid() が書き込んだ整数そのものを指定する。ポインターではない!)
waitid() システムコール (Linux 2.6.9 以降で利用可能) を使うと、 子プロセスのどの状態変化を待つかについてより細かな制御ができる。
引数 idtype と id でどの子プロセスを待つかを選択する:
子プロセスのどの状態変化を待つかは以下のフラグで指定する (options には 1個以上のフラグの論理和をとって指定する):
さらに以下のフラグを論理和の形で options に指定できる:
成功した場合には、 waitid() は infop が指す siginfo_t 構造体の以下のフィールドを設定する:
If WNOHANG was specified in options and there were no children in a waitable state, then waitid() returns 0 immediately and the state of the siginfo_t structure pointed to by infop depends on the implementation. To (portably) distinguish this case from that where a child was in a waitable state, zero out the si_pid field before the call and check for a nonzero value in this field after the call returns.
POSIX.1-2008 Technical Corrigendum 1 (2013) adds the requirement that when WNOHANG is specified in options and there were no children in a waitable state, then waitid() should zero out the si_pid and si_signo fields of the structure. On Linux and other implementations that adhere to this requirement, it is not necessary to zero out the si_pid field before calling waitid(). However, not all implementations follow the POSIX.1 specification on this point.
wait(): 成功すると、終了した子プロセスのプロセスID を返す。 エラーの場合 -1 を返す。
waitpid(): 成功すると、状態が変化した子プロセスのプロセスID を返す。 WNOHANG が指定されていて、 pid で指示された子プロセスが一つ以上存在するが、どの子プロセスでも 状態変化が起こっていなかった場合は、 0 を返す。 エラーの場合 -1 を返す。
waitid(): 成功すると 0 を返す。 WNOHANG が指定されていて、 pid で指示された子プロセスで状態変化が起こっていなかった場合にも 0 を返す。
エラーの場合 -1 を返す。 エラーの場合、これらのシステムコールはいずれも errno に適切な値を設定する。
SVr4, 4.3BSD, POSIX.1-2001.
A child that terminates, but has not been waited for becomes a "zombie". The kernel maintains a minimal set of information about the zombie process (PID, termination status, resource usage information) in order to allow the parent to later perform a wait to obtain information about the child. As long as a zombie is not removed from the system via a wait, it will consume a slot in the kernel process table, and if this table fills, it will not be possible to create further processes. If a parent process terminates, then its "zombie" children (if any) are adopted by init(1), (or by the nearest "subreaper" process as defined through the use of the prctl(2) PR_SET_CHILD_SUBREAPER operation); init(1) automatically performs a wait to remove the zombies.
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 以降では、あるスレッドが同じスレッドグループの他のスレッドの 子プロセスが終了するのを待つことができるようになった。 そして将来はこれがデフォルトの動作になるであろう。
The following Linux-specific options are for use with children created using clone(2); they can also, since Linux 4.7, be used with waitid():
Since Linux 4.7, the __WALL flag is automatically implied if the child is being ptraced.
wait() is actually a library function that (in glibc) is implemented as a call to wait4(2).
On some architectures, there is no waitpid() system call; instead, this interface is implemented via a C library wrapper function that calls wait4(2).
生の 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 <stdint.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> int main(int argc, char *argv[]) {
pid_t cpid, w;
int wstatus;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
printf("Child PID is %jd\n", (intmax_t) getpid());
if (argc == 1)
pause(); /* Wait for signals */
_exit(atoi(argv[1]));
} else { /* Code executed by parent */
do {
w = waitpid(cpid, &wstatus, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(wstatus)) {
printf("exited, status=%d\n", WEXITSTATUS(wstatus));
} else if (WIFSIGNALED(wstatus)) {
printf("killed by signal %d\n", WTERMSIG(wstatus));
} else if (WIFSTOPPED(wstatus)) {
printf("stopped by signal %d\n", WSTOPSIG(wstatus));
} else if (WIFCONTINUED(wstatus)) {
printf("continued\n");
}
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
exit(EXIT_SUCCESS);
} }
_exit(2), clone(2), fork(2), kill(2), ptrace(2), sigaction(2), signal(2), wait4(2), pthread_create(3), core(5), credentials(7), signal(7)
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。
2020-11-01 | Linux |