仮説
POSIXは次のように述べていますSIG_IGN
(厳密には XSI 拡張ノートの下で):
SIGCHLD シグナルのアクションが SIG_IGN に設定されている場合、呼び出しプロセスの子プロセスは、終了時にゾンビ プロセスに変換されません。呼び出しプロセスがその後その子を待機し、プロセスにゾンビ プロセスに変換された待機していない子がない場合、そのプロセスはすべての子が終了するまでブロックし、wait()、waitid()、および waitpid() は、失敗し、errno を [ECHILD] に設定します。
の説明で<signal.h>
は、 のデフォルトの信号処理SIGCHLD
はSIG_IGN
「無視」(コード「I」) であると述べています。ただし、SIG_DFL
信号を「無視」するのは動作です。信号は生成されません。これは動作とは異なりSIG_IGN
ます。
したがって、シグナル ハンドラーを設定する必要はありませんが、プロセスに関する情報を取得するべきではありません。
デモコード
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;
static void catcher(int signum, siginfo_t *info, void *vp)
{
sig_num = signum;
sig_info = *info;
sig_ctxt = vp;
}
static void set_handler(int signum)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);
if (sigaction(signum, &sa, 0) != 0)
{
int errnum = errno;
fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
exit(1);
}
}
static void prt_interrupt(FILE *fp)
{
if (sig_num != 0)
{
fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid);
sig_num = 0;
}
}
static void five_kids(void)
{
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid < 0)
break;
else if (pid == 0)
{
printf("PID %d - exiting with status %d\n", (int)getpid(), i);
exit(i);
}
else
{
int status = 0;
pid_t corpse = wait(&status);
printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF));
prt_interrupt(stdout);
fflush(0);
}
}
}
int main(void)
{
printf("SIGCHLD set to SIG_IGN\n");
signal(SIGCHLD, SIG_IGN);
five_kids();
printf("SIGCHLD set to catcher()\n");
set_handler(SIGCHLD);
five_kids();
return(0);
}
このfflush(0);
呼び出しにより、標準出力 (特に) が確実にフラッシュされます。これは、サンプル プログラムの出力が別のプログラムにパイプされる場合に重要です。
出力例
コード例の出力は理論と一致していますが、少し解釈が必要です。
SIGCHLD set to SIG_IGN
PID 4186 - exiting with status 0
SIGCHLD set to SIG_IGN
Child: 4186; Corpse: -1; Status = 0x0000
PID 4187 - exiting with status 1
Child: 4187; Corpse: -1; Status = 0x0000
PID 4188 - exiting with status 2
Child: 4188; Corpse: -1; Status = 0x0000
PID 4189 - exiting with status 3
Child: 4189; Corpse: -1; Status = 0x0000
PID 4190 - exiting with status 4
Child: 4190; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 4191 - exiting with status 0
SIGCHLD set to catcher()
Child: 4191; Corpse: -1; Status = 0x0000
Signal 20 from PID 4191
Child: 4192; Corpse: 4191; Status = 0x0000
PID 4192 - exiting with status 1
Child: 4193; Corpse: 4192; Status = 0x0100
Signal 20 from PID 4192
PID 4193 - exiting with status 2
Child: 4194; Corpse: 4193; Status = 0x0200
Signal 20 from PID 4193
PID 4194 - exiting with status 3
Child: 4195; Corpse: 4194; Status = 0x0300
Signal 20 from PID 4194
PID 4195 - exiting with status 4
出力の最初のセクションは理論と完全に一致しています。呼び出し元のコードは、待機する死んだ子 (左) がないことを除いて、死んだ子に関する情報を取得しません。
出力の 2 番目のセクションも理論と一致しますが、親の前に子が実行されていないように見えるため、親wait()
からの最初のセクションには待機する死んだ子がなく、-1 が返されます。後続の呼び出しでさまざまな子が取得されますが、1 つは待機せずに脱出します (ただし、死体はシステムによってクリーンアップされます)。waitpid()
次のようなコードを使用pid_t corpse = waitpid(pid, &status, 0);
すると、各子を順番に待機します。
この解説の一部は、上記の「無視」に関する修正されたコメントに照らして修正が必要な場合があります。TBD — 今は時間切れです。
3 GHz Intel Core 2 Duo、GCC 4.6.0、64 ビット コンパイル上の Mac OS X 10.8.4:
gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition sigchld.c -o sigchld
SIGINT
これは、との動作をテストするために、いくつかの既存のコードを手早く適応させたものpause()
です。余計な素材が入っている可能性があります。wait()
シグナル ハンドラで、またはその親戚の 1 つを呼び出す必要はありません。ただし、必要に応じてそうすることができます。