Scroll to navigation

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 のインターフェイスである。
生のシステムコールについての情報は「注意」の節を参照。 */

glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):

waitid():

glibc 2.26 以降: _XOPEN_SOURCE >= 500 ||
_POSIX_C_SOURCE >= 200809L
glibc 2.25 以前:
_XOPEN_SOURCE
|| /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L
|| /* Glibc versions <= 2.19: */ _BSD_SOURCE

説明

これらのシステムコールはいずれも、呼び出し元プロセスの子プロセスの 状態変化を待ち、状態が変化したその子プロセスの情報を取得するのに 使用される。 状態変化とは以下のいずれかである: 子プロセスの終了、シグナルによる子プロセスの停止、 シグナルによる子プロセスの再開。 子プロセスが終了した場合は、wait を実行することで、 システムがその子プロセスに関連するリソースを解放できるようになる。 wait が実行されなかった場合には、終了した子プロセスは 「ゾンビ」状態で残り続ける (下記の注意の章を参照のこと)。

子プロセスの状態変化がすでに発生していた場合、これらのコールは すぐに復帰する。それ以外の場合は、子プロセスの状態変化が起こるか、 シグナルハンドラーによりシステムコールが中断されるまで、 停止 (block) する (後者は、 sigaction(2)SA_RESTART フラグによりシステムコールが自動的に再スタートするようになっていない 場合の動作である)。 以下の説明では、状態変化が起こったがこれらのシステムコールのいずれかに よって待たれていない子プロセスを waitable (待ち可能) と呼ぶ。

wait() と waitpid()

wait() システムコールは、子プロセスのいずれかが終了するまで 呼び出し元のスレッドの実行を一時停止する。 呼び出し wait(&wstatus) は以下と等価である:


waitpid(-1, &wstatus, 0);

waitpid() システムコールは、 pid 引数で指定した子プロセスの状態変化が起こるまで、 呼び出し元のスレッドの実行を一時停止する。デフォルトでは、 waitpid() は子プロセスの終了だけを待つが、この動作は options 引数により変更可能である。

pid に指定できる値は以下の通り:

< -1
プロセスグループID が pid の絶対値に等しい子プロセスのいずれかが終了するまでを待つ。
-1
子プロセスのどれかが終了するまで待つ。
0
プロセスグループ ID が、waitpid() が呼ばれた時点での呼び出し元のプロセスのプロセスグループ ID と等しい子プロセスを待つ。
> 0
プロセスID が pid に等しい子プロセスを待つ。

options の値は次の定数の 0 個以上の論理和である:

状態変化が起こった子プロセスがない場合にすぐに復帰する。
子プロセスが停止した場合にも復帰する (子プロセスが ptrace(2) でトレースされている場合は除く)。 このオプションが指定されていない場合でも、停止したプロセスが 「トレース (traced)」されていれば、子プロセスの状態が報告される。
停止した子プロセスが SIGCONT の配送により再開した場合にも復帰する。

(Linux 専用オプションについては後述する)

wstatus が NULL でなければ、 wait() や waitpid() は status で指す int に状態情報を格納する。 この整数は以下のマクロを使って検査できる。 (これらのマクロの引数には、 wait() や waitpid() が書き込んだ整数そのものを指定する。ポインターではない!)

子プロセスが正常に終了した場合に真を返す。 「正常に」とは、 exit(3)_exit(2) が呼び出された場合、もしくは main() から復帰した場合である。
子プロセスの終了ステータスを返す。 終了ステータスは status 引数の下位 8ビットで構成されており、 exit(3)_exit(2) の呼び出し時に渡された値、もしくは main() の return 文の 引数として指定された値である。 このマクロを使用するのは WIFEXITED が真を返した場合だけにすべきである。
子プロセスがシグナルにより終了した場合に真を返す。
子プロセス終了の原因となったシグナルの番号を返す。 このマクロを使用するのは WIFSIGNALED が真を返した場合だけにすべきである。
returns true if the child produced a core dump (see core(5)). This macro should be employed only if WIFSIGNALED returned true.
This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif.
子プロセスがシグナルの配送により停止した場合に真を返す。 これが真になるのは、システムコールが WUNTRACED を指定して呼び出された場合か、子プロセスがトレースされている場合 (ptrace(2) 参照) だけである。
子プロセスを停止させたシグナルの番号を返す。 このマクロを使用するのは WIFSTOPPED が 0 以外を返した場合だけにすべきである。
(Linux 2.6.10 以降) 子プロセスが SIGCONT の配送により再開した場合に真を返す。

waitid()

waitid() システムコール (Linux 2.6.9 以降で利用可能) を使うと、 子プロセスのどの状態変化を待つかについてより細かな制御ができる。

引数 idtypeid でどの子プロセスを待つかを選択する:

プロセスID が id と一致する子プロセスを待つ。
Wait for the child referred to by the PID file descriptor specified in id. (See pidfd_open(2) for further information on PID file descriptors.)
Wait for any child whose process group ID matches id. Since Linux 5.4, if id is zero, then wait for any child that is in the same process group as the caller's process group at the time of the call.
子プロセス全部を対象に待つ。 id は無視される。

子プロセスのどの状態変化を待つかは以下のフラグで指定する (options には 1個以上のフラグの論理和をとって指定する):

子プロセスの終了を待つ。
子プロセスがシグナルの配送により停止するのを待つ。
(停止していた) 子プロセスが SIGCONT が配送されて再開するのを待つ。

さらに以下のフラグを論理和の形で options に指定できる:

waitpid() と同様。
waitable 状態のプロセスをそのままにする。この後で wait コールを 使って、同じ子プロセスの状態情報をもう一度取得することができる。

成功した場合には、 waitid() は infop が指す siginfo_t 構造体の以下のフィールドを設定する:

子プロセスのプロセスID。
子プロセスの実ユーザーID (このフィールドは他のほとんどの実装では設定されない)。
常に SIGCHLD が設定される。
_exit(2) (か exit(3)) に指定された子プロセスの終了ステータス、もしくは 子プロセスの終了、停止、再開の原因となったシグナルが設定される。 このフィールドをどう解釈するかは、 si_code フィールドを参照して決めることができる。
以下のいずれかが設定される: CLD_EXITED (子プロセスが _exit(2) を呼び出した); CLD_KILLED (シグナルにより子プロセスが kill された); CLD_DUMPED (シグナルにより子プロセスが kill され、コアダンプが行われた); CLD_STOPPED (シグナルにより子プロセスが停止した); CLD_TRAPPED (トレースされていた子プロセスがトラップを受信した); CLD_CONTINUED (SIGCONT により子プロセスが再開された)。

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 に適切な値を設定する。

エラー

(wait() の場合) 呼び出し元プロセスには、wait を行っていない子プロセスはない。
(waitpid() か waitid() の場合) pid (waitpid()) か idtypeid (waitid()) で指定したプロセスが存在しないか、呼び出し元プロセスの子プロセスでない (SIGCHLD の動作に SIG_IGN を設定した場合には、自分自身の子プロセスでも起こりうる。 スレッドに関しては「Linux での注意」の節も参照すること)。
WNOHANG が設定されておらず、禁止 (block) されていないシグナルや SIGCHLD を受信した。 signal(7) 参照。
options 引数が不正である。

準拠

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() の呼び出しは全ての子プロセスが終了するまで停止し、 子プロセスが全部終了した後 errnoECHILD を設定して失敗する。 (もともとの POSIX 標準は SIGCHLDSIG_IGN を設定した場合の振る舞いを未規定のままにしている。 SIGCHLD のデフォルトの動作が「無視」であるにもかかわらず、 SIGCHLD の動作として SIG_IGN を明示的に設定した場合にはゾンビプロセスの子プロセスの扱いが 異なる点に注意すること。)

Linux 2.6 はこの仕様に準拠している。 しかし、Linux 2.4 (とそれ以前のバージョン) はそうではない: SIGCHLD が無視される状態で wait() または waitpid() が呼び出された場合、 SIGCHLD が無視されていないかのように振る舞う。 つまり、呼び出しによって次の子プロセスの終了までブロックされ、 終了した子プロセスの PID と状態が返される。

Linux での注意

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():

__WCLONE
"clone" な子プロセスだけを待つ。 指定されなかった場合は非 "clone" な子プロセスだけを待つ ("clone" な子プロセスは、終了時に親プロセスへ全くシグナルを送らないか、 SIGCHLD 以外のシグナルを送る)。 このオプションは __WALL も指定された場合は無視される。
__WALL (Linux 2.4 以降)
"clone" であるかないかに関わらず、 全ての子プロセスを待つ。
__WNOTHREAD (Linux 2.4 以降)
同じスレッドグループの他のスレッドの子プロセスは待たない。 Linux 2.4 より前ではデフォルトであった。

Since Linux 4.7, the __WALL flag is automatically implied if the child is being ptraced.

C ライブラリとカーネルの違い

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() を呼び出すアプリケーションは、 infopsiginfo_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