CatCoding

Kernel analysis: Defunct Process

2013-11-23

我发现带着问题去看内核代码比较容易理解。如果一个父进程显示的设置 SIGCHLD 为 Ignore,子进程将自己清理自己。

#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main() {
	struct sigaction sa;
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = SIG_IGN;
	sigaction(SIGCHLD, &sa, NULL);
	int pid = fork();
	if(pid > 0) {
		printf("parent:%d\n", getpid());
		sleep(30);
	} else {
		printf("child:%d\n", getpid());
		sleep(4);
	}
	printf("finished\n");
	return 0;
}

我们可以顺便看看内核里面是怎么写的,

linux/kernel/exit.c 里面这部分是负责进程退出的,我截取了相关的代码:

/*
 * Send signals to all our closest relatives so that they know
 * to properly mourn us..
 */
static void exit_notify(struct task_struct *tsk, int group_dead)
{
    bool autoreap;

    forget_original_parent(tsk);
    write_lock_irq(&tasklist_lock);

    /* .... */

    } else if (thread_group_leader(tsk)) {
        autoreap = thread_group_empty(tsk) &&
            do_notify_parent(tsk, tsk->exit_signal);
    } else {
        autoreap = true;
    }

    tsk->exit_state = autoreap ? EXIT_DEAD : EXIT_ZOMBIE;

    /*..... */

    /* If the process is dead, release it - nobody will wait for it */
    if (autoreap)
        release_task(tsk);
}

其中有一段是判断是否 autoreap,我们继续可以看看
linux/kernel/signal.c 里面的 do_notify_parent 函数:

bool do_notify_parent(struct task_struct *tsk, int sig)
{
    struct siginfo info;
    unsigned long flags;
    struct sighand_struct *psig;
    bool autoreap = false;

    /* .... */

    if (!tsk->ptrace && sig == SIGCHLD &&
        (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||
         (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) {
        /*
         * We are exiting and our parent doesn't care.  POSIX.1
         * defines special semantics for setting SIGCHLD to SIG_IGN
         * or setting the SA_NOCLDWAIT flag: we should be reaped
         * automatically and not left for our parent's wait4 call.
         * Rather than having the parent do it as a magic kind of
         * signal handler, we just set this to tell do_exit that we
         * can be cleaned up without becoming a zombie.  Note that
         * we still call __wake_up_parent in this case, because a
         * blocked sys_wait4 might now return -ECHILD.
         *
         * Whether we send SIGCHLD or not for SA_NOCLDWAIT
         * is implementation-defined: we do (if you don't want
         * it, just use SIG_IGN instead).
         */
        autoreap = true;
        if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)
            sig = 0;
    }
    if (valid_signal(sig) && sig)
        __group_send_sig_info(sig, &info, tsk->parent);

    __wake_up_parent(tsk, tsk->parent);
	return autoreap;
}

可以看到如果父进程对子进程的生死不关心,那么设置 autoreap 为 TRUE,甚至这个信号也可以不发送了。

公号同步更新,欢迎关注👻