• task는 죽으면, 모든 resource를 release하지않고 왜 죽었는지 정도를 남겨둔다.
  • 그 정보는 부모 task에서 자식의 죽음을 이용할때, 회수한다 예를 들면 join()
  • do_noitfy_parent 는 task가 죽을때, 죽는 task의 부모에게 자식의 죽음을 알려주기 위한 함수이다
  • do_notify_pidfd() 는 죽는 task의 FD에 대해서 죽는 event를 기다리는 task들을 깨워준다
  • sig ID가 SIGCHLD가 아니더라도, parent == real_parent인 경우에는 sig ID를 SIGCHLD 로 바꿔준다.
  • task->exit_code를 통해서 parent에게 왜 task가 죽었는지를 전달해준다, (info.si_code)
  • 부모가 SIGCHLD 시그널을 받는다면, 부모에게 알려준다, __send_signal_locked().
  • 만약 부모가 SIGCHLD 에 대해서 시그널을 안 받겠다고 해놨다면 (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN), 부모에게 알려주지않고 do_exit()에서 자체적으로 resource를 release한다
  • 부모가 SIGCHLD를 안 받겠다고했지만, 부모가 자식의 죽음을 조사하는 과정에서 wait/blocked되어있는 race condition상황을 대비해서 부모를 깨워준다 (__wake_up_parent).
    • 이 부분에 대해서 한번 확인해볼 필요가 있음. 20년 전 코드가 아직도 유효하고 필요한지.

 

/*
 * Let a parent know about the death of a child.
 * For a stopped/continued status change, use do_notify_parent_cldstop instead.
 *
 * Returns true if our parent ignored us and so we have switched to
 * self-reaping.
 */
bool do_notify_parent(struct task_struct *tsk, int sig)
{
	struct kernel_siginfo info;
	unsigned long flags;
	struct sighand_struct *psig;
	bool autoreap = false;
	u64 utime, stime;

	WARN_ON_ONCE(sig == -1);

	/* do_notify_parent_cldstop should have been called instead.  */
	WARN_ON_ONCE(task_is_stopped_or_traced(tsk));

	WARN_ON_ONCE(!tsk->ptrace &&
	       (tsk->group_leader != tsk || !thread_group_empty(tsk)));
	/*
	 * tsk is a group leader and has no threads, wake up the
	 * non-PIDFD_THREAD waiters.
	 */
	if (thread_group_empty(tsk))
		do_notify_pidfd(tsk);

	if (sig != SIGCHLD) {
		/*
		 * This is only possible if parent == real_parent.
		 * Check if it has changed security domain.
		 */
		if (tsk->parent_exec_id != READ_ONCE(tsk->parent->self_exec_id))
			sig = SIGCHLD;
	}

	clear_siginfo(&info);
	info.si_signo = sig;
	info.si_errno = 0;
	/*
	 * We are under tasklist_lock here so our parent is tied to
	 * us and cannot change.
	 *
	 * task_active_pid_ns will always return the same pid namespace
	 * until a task passes through release_task.
	 *
	 * write_lock() currently calls preempt_disable() which is the
	 * same as rcu_read_lock(), but according to Oleg, this is not
	 * correct to rely on this
	 */
	rcu_read_lock();
	info.si_pid = task_pid_nr_ns(tsk, task_active_pid_ns(tsk->parent));
	info.si_uid = from_kuid_munged(task_cred_xxx(tsk->parent, user_ns),
				       task_uid(tsk));
	rcu_read_unlock();

	task_cputime(tsk, &utime, &stime);
	info.si_utime = nsec_to_clock_t(utime + tsk->signal->utime);
	info.si_stime = nsec_to_clock_t(stime + tsk->signal->stime);

	info.si_status = tsk->exit_code & 0x7f;
	if (tsk->exit_code & 0x80)
		info.si_code = CLD_DUMPED;
	else if (tsk->exit_code & 0x7f)
		info.si_code = CLD_KILLED;
	else {
		info.si_code = CLD_EXITED;
		info.si_status = tsk->exit_code >> 8;
	}

	psig = tsk->parent->sighand;
	spin_lock_irqsave(&psig->siglock, flags);
	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;
	}
	/*
	 * Send with __send_signal as si_pid and si_uid are in the
	 * parent's namespaces.
	 */
	if (valid_signal(sig) && sig)
		__send_signal_locked(sig, &info, tsk->parent, PIDTYPE_TGID, false);
	__wake_up_parent(tsk, tsk->parent);
	spin_unlock_irqrestore(&psig->siglock, flags);

	return autoreap;
}

 

+ Recent posts