waitシステムコール(その1)
Rev.3を表示中。最新版はこちら。
waitシステムコールはwaitpid/wait4/waitidの3つを有し、これらはどれもdo_wait関数をコールすることで、指定されたプロセスをwaitします。ところでwait関数は子プロセスがexitして、ゾンビ状態になったプロセスの統計情報を取得するものと言うことですが、waitにはゾンビ状態だけでなく、子プロセスがトレースされて停止した場合、またそこからの再動作によっても、その時の統計情報を取得できるようになっています。waitpidは直接指定するpidをwaitします。引数から見て取れるように、exit関数の返り値をstat_addrに受け取るだけで、子プロセスの統計情報を取得しません。なおこのシステムコールはtruct rusage __user *, ru引数をNULLにしてwait4をコールするだけです。ここのでpidは構造体のpidでなく、pid_tの型で示すようにプロセスIDになります。
SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options) { return sys_wait4(pid, stat_addr, options, NULL); }wait4は指定されたupidで、pidを設定したのち、do_wait関数をコールすることで子プロセスをwaitします。まずユーザが指定したoptionsフラグのチェックをします。WNOHANG|WUNTRACED|WCONTINUED|__WNOTHREAD|__WCLONE|__WALL以外のものが指定されていたらダメって事です。
WNOHANGはwaitするプロセスが無かった場合、呼び出したプロセスに復帰します。
WUNTRACEDは子プロセスがトレース以外で停止した場合、呼び出したプロセスに復帰します。
WCONTINUEDは子プロセスが停止から再実行した場合、呼び出したプロセスに復帰します。
って感じです。~ですから、反対の場合エラーって事で、上記の条件でも復帰しないでウエイトし続けるって事です。
if (upid == -1)以下はpid_t upidによって、対象となるstruct pidを決定します。if (upid == -1)なら、pid = NULL/type = PIDTYPE_MAXです。これはPIDTYPE_PGID/PIDTYPE_PGID/PIDTYPE_PIDの全てから、すなわち任意の子プロセスを待ちます。
if (upid < 0)なら、プロセスグループ内のpid の絶対値に等しい子プロセスを待ちます。
if (upid == 0)なら、プロセスグループ内のpid の、waitを呼び出したプロセスのプロセスIDと同じ子プロセスを待ちます。
upid > 0なら、upidと同じ子プロセスIDの子プロセスを待ちます。
補足
プロセスIDはtgidのことでpidではありません。またプロセスグループはCLONE_THREADで作成されたプロセス群です。if (upid < 0)とif (upid == 0)の違いですが、たぶんif (upid < 0)は、waitを呼び出したプロセスに、親を差し替えられた子プロセスも、対象にすると言う事だと思いますが・・・
do_wait関数をコールする時、options | WEXITEDでWEXITEDフラグが設定されています。従ってwait4はかならずexitの子プロセスをウエイトする事になります。
SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, int, options, struct rusage __user *, ru) { struct pid *pid = NULL; enum pid_type type; long ret; if (options & ~(WNOHANG|WUNTRACED|WCONTINUED| __WNOTHREAD|__WCLONE|__WALL)) return -EINVAL; if (upid == -1) type = PIDTYPE_MAX; else if (upid < 0) { type = PIDTYPE_PGID; pid = find_get_pid(-upid); } else if (upid == 0) { type = PIDTYPE_PGID; pid = get_pid(task_pgrp(current)); } else /* upid > 0 */ { type = PIDTYPE_PID; pid = find_get_pid(upid); } ret = do_wait(type, pid, options | WEXITED, NULL, stat_addr, ru); put_pid(pid); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(4, ret, upid, stat_addr, options, ru); return ret; }waitidはwait4システムコールにいま少し自由度を持たせた感じです。どの状態で待つかは引数で指定しなければなりません。WEXITED|WSTOPPED|WCONTINUEDのどれかです。まずWNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED以外のものが指定してはダメのようで、次のif文でWEXITED|WSTOPPED|WCONTINUEDのどれかが指定しないといけないようです。どのタイプのプロセスでwaitするかは、whichで指定し、PIDTYPE_PID/PIDTYPE_PGIDから指定したプロセスIDをもつ子プロセスを対象としています。なお、do_wait関数ではwait4と異なって、WEXITEDは付加されていなく、stat_addr引数がなく、従ってexitでの終了コードは取得しないようになっています。
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, infop, int, options, struct rusage __user *, ru) { struct pid *pid = NULL; enum pid_type type; long ret; if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED)) return -EINVAL; if (!(options & (WEXITED|WSTOPPED|WCONTINUED))) return -EINVAL; switch (which) { case P_ALL: type = PIDTYPE_MAX; break; case P_PID: type = PIDTYPE_PID; if (upid <= 0) return -EINVAL; break; case P_PGID: type = PIDTYPE_PGID; if (upid <= 0) return -EINVAL; break; default: return -EINVAL; } if (type < PIDTYPE_MAX) pid = find_get_pid(upid); ret = do_wait(type, pid, options, infop, NULL, ru); put_pid(pid); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(5, ret, which, upid, infop, options, ru); return ret; }