我将我的程序作为守护进程运行。
父进程只等待子进程,当意外死亡时,再次fork和等待。
for (; 1;) {
if (fork() == 0) break;
int sig = 0;
for (; 1; usleep(10000)) {
pid_t wpid = waitpid(g->pid[1], &sig, WNOHANG);
if (wpid > 0) break;
if (wpid < 0) print("wait error: %s\n", strerror(errno));
}
}
但是当子进程被使用-9信号杀死时,它将变成僵尸进程。
waitpid
应该立即返回子进程的pid!
但是waitpid
需要大约90秒后才能获得pid号码。
cube 28139 0.0 0.0 70576 900 ? Ss 04:24 0:07 ./daemon -d
cube 28140 9.3 0.0 0 0 ? Zl 04:24 106:19 [daemon] <defunct>
这是父进程的strace
父进程未被卡住,wait4函数一直被调用。
strace -p 28139
Process 28139 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 0
wait4(28140, 0x7fff08a2681c, WNOHANG, NULL) = 0
nanosleep({0, 10000000}, NULL) = 0
wait4(28140, 0x7fff08a2681c, WNOHANG, NULL) = 0
约90秒后,父进程收到了SIGCHILD信号,并且wait4函数返回了已经结束的子进程的pid。
--- SIGCHLD (Child exited) @ 0 (0) ---
restart_syscall(<... resuming interrupted call ...>) = 0
wait4(28140, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGKILL}], WNOHANG, NULL) = 28140
为什么子进程不能立即退出?相反,它会意外地变成僵尸进程。
malloc
分配的内存,由open
指定的文件名分配的资源,由socket
指定的域分配的资源。文件名指定的回收会导致大量的OS IO
,如果文件很大,则会进行大量的OS IO
,并且在执行期间进程必须被转换为僵尸进程。lsof
命令将显示所有已打开的文件。最后我发现我忘记在打开后关闭fd
,导致了许多泄漏的fds
引起了问题。 - Cube