do_exit——>exit_notify()

12600 단어
/*
 * 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)
{
	int signal;
	void *cookie;

	/*
	 * This does two things:
	 *
  	 * A.  Make init inherit all the child processes
	 * B.  Check to see if any process groups have become orphaned
	 *	as a result of our exiting, and if they have any stopped
	 *	jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2)
	 */
	forget_original_parent(tsk);
	exit_task_namespaces(tsk);  // task namespace  NULL

	write_lock_irq(&tasklist_lock);
	if (group_dead)
		kill_orphaned_pgrp(tsk->group_leader, NULL);

	/* Let father know we died
	 *
	 * Thread signals are configurable, but you aren't going to use
	 * that to send signals to arbitrary processes.
	 * That stops right now.
	 *
	 * If the parent exec id doesn't match the exec id we saved
	 * when we started then we know the parent has changed security
	 * domain.
	 *
	 * If our self_exec id doesn't match our parent_exec_id then
	 * we have changed execution domain as these two values started
	 * the same after a fork.
	 */
	if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) &&
	    (tsk->parent_exec_id != tsk->real_parent->self_exec_id ||
	     tsk->self_exec_id != tsk->parent_exec_id))
		tsk->exit_signal = SIGCHLD;

	signal = tracehook_notify_death(tsk, &cookie, group_dead);
	if (signal >= 0)
		signal = do_notify_parent(tsk, signal);

	tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE;

	/* mt-exec, de_thread() is waiting for group leader */
	if (unlikely(tsk->signal->notify_count < 0))
		wake_up_process(tsk->signal->group_exit_task);
	write_unlock_irq(&tasklist_lock);

	tracehook_report_death(tsk, signal, cookie, group_dead);

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

이 함수가 처음에 설명한 바와 같이 이 함수는 이 프로세스와 관련된 모든 가족에게 해당하는 변화를 하거나 죽어가는 프로세스를 추모하도록 통지한다.
우리가 만난 첫 번째 중요한 함수는:forgetoriginal_parent()
주석에서 이 함수의 역할을 보여 줍니다. (1) init 프로세스가 이 프로세스의 모든 하위 프로세스를 계승하도록 합니다.(2) 프로세스가 있는 프로세스 그룹이 프로세스가 죽은 후에 고아 프로세스 그룹으로 변할지 확인합니다. 작업을 중지한 경우
SIGHUP 신호와 SIGCONT 신호를 선후로 보내고 싶다.
이 함수에서 관건은 프로세스의'양부'프로세스인 p 를 분명히 하는 것이다.pptr와 "생부 프로세스"- poptr의 관계, 상세한 해석은 P343을 볼 수 있다.
static void forget_original_parent(struct task_struct *father)
{
	struct task_struct *p, *n, *reaper;
	LIST_HEAD(dead_children);

	write_lock_irq(&tasklist_lock);
	/*
	 * Note that exit_ptrace() and find_new_reaper() might
	 * drop tasklist_lock and reacquire it.
	 */
	exit_ptrace(father);
/*
 * Detach all tasks we were using ptrace on. Called with tasklist held
 * for writing, and returns with it held too. But note it can release
 * and reacquire the lock.
 */
	reaper = find_new_reaper(father); //                   。              。
/*
 * When we die, we re-parent all our children.
 * Try to give them to another thread in our thread
 * group, and if no such member exists, give it to
 * the child reaper process (ie "init") in our pid
 * space.
 */
	list_for_each_entry_safe(p, n, &father->children, sibling) { //            
		struct task_struct *t = p;
		do {
			t->real_parent = reaper;  //               real_parent  reaper  
			if (t->parent == father) {
				BUG_ON(task_ptrace(t));
				t->parent = t->real_parent;
			}
			if (t->pdeath_signal)
				group_send_sig_info(t->pdeath_signal,
						    SEND_SIG_NOINFO, t); //  SEND_SIG_NOINFO  ,  task_struct    pdeath_dignal                ,       。
		} while_each_thread(p, t); //                 
		reparent_leader(father, p, &dead_children);//Any that need to be release_task'd are put on the @dead list.     :
	}
	write_unlock_irq(&tasklist_lock);

	BUG_ON(!list_empty(&father->children));

	list_for_each_entry_safe(p, n, &dead_children, sibling) {
		list_del_init(&p->sibling);  //????????????????????????????????? p        ,
		release_task(p);//       ,     task_struct    ,              。
	}
}

forgetoriginal_parent 함수에서 왜 두 개의 순환이 하위 프로세스를 처리합니까?나는 아버지 프로세스의 하위 프로세스가 반드시 같은 슬라이딩 대기열에 있는 것은 아니기 때문이다. 예를 들어 우리가 앞에서 말한 바와 같이 만약 누군가를 죽였기 때문이라면
프로세스 A, 그리고 A 프로세스의 하위 프로세스 C의 부모 프로세스를 프로세스 B로 가리켰다. 그러나 우리는 B 프로세스의 하위 프로세스 D의 슬라이딩 대기열에 어떤 변화가 있는지 보지 못했다. 따라서 D의 슬라이딩 대기열은 C 프로세스를 연결하지 않았기 때문에 이런 상황이 발생했다. 즉, 부모 프로세스에 여러 개의 하위 프로세스가 있지만 이런 일부 하위 프로세스 간은 서로 독립될 수 있다.따라서 취소할 부모 프로세스의 모든 하위 프로세스의 부모 프로세스가reaper를 가리키도록 두 층의 순환이 필요합니다.
포켓몬스터를 보도록 하겠습니다.original_parent () 의 중요한 함수 Reparentleader():
/*
* Any that need to be release_task'd are put on the @dead list.
 */
static void reparent_leader(struct task_struct *father, struct task_struct *p,
				struct list_head *dead)
{
	list_move_tail(&p->sibling, &p->real_parent->children); //   p             ,       real_parent   children   。

	if (task_detached(p)) //         , p->exit_signal == -1,       。
		return;
	/*
	 * If this is a threaded reparent there is no need to
	 * notify anyone anything has happened.
	 */
	if (same_thread_group(p->real_parent, father)) //                            ,     。            。
                                                       //     /   /        《UNIX      》    
		return;

	/* We don't want people slaying init.  */
	p->exit_signal = SIGCHLD;

	/* If it has exited notify the new parent about this child's death. */
	if (!task_ptrace(p) &&
	    p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { //               ,                      
		do_notify_parent(p, p->exit_signal); //  Let a parent know about the death of a child.         。
		if (task_detached(p)) {
			p->exit_state = EXIT_DEAD;
/*
 *EXIT_DEAD is the state after an appropriate wait system call has been issued and before the task
 *is completely removed from the system. This state is only of importance if multiple threads issue
 *wait calls for the same task.
 *EXIT_DEAD   EXIT_ZOMBIE         《  Linux    》
*/
			list_move_tail(&p->sibling, dead); //   p  sibling         dead   。                ?
		}
	}

	kill_orphaned_pgrp(p, father);  //       ,    :
}

리파렌트leader 함수 중 중요한 함수donotify_parent (): 프로세스가 추적되지 않고 경직된 상태이며, 프로세스가 비어 있다면, 이 함수로 아버지 프로세스에 신호를 보내서 하위 프로세스의 생명이 끝났다는 것을 알리고 뒷일을 처리합니다.
/*
 * Let a parent know about the death of a child.
 * For a stopped/continued status change, use do_notify_parent_cldstop instead.
 *
 * Returns -1 if our parent ignored us and so we've switched to
 * self-reaping, or else @sig.
 */
int do_notify_parent(struct task_struct *tsk, int sig)
{
	struct siginfo info;
	unsigned long flags;
	struct sighand_struct *psig;
	int ret = sig;

	BUG_ON(sig == -1);

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

	BUG_ON(!task_ptrace(tsk) &&
	       (tsk->group_leader != tsk || !thread_group_empty(tsk)));

	info.si_signo = sig;
	info.si_errno = 0;
	/*
	 * we are under tasklist_lock here so our parent is tied to
	 * us and cannot exit and release its namespace.
	 *
	 * the only it can is to switch its nsproxy with sys_unshare,
	 * bu uncharing pid namespaces is not allowed, so we'll always
	 * see relevant namespace
	 *
	 * 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, tsk->parent->nsproxy->pid_ns);
	info.si_uid = __task_cred(tsk)->uid;
	rcu_read_unlock();

	info.si_utime = cputime_to_clock_t(cputime_add(tsk->utime,
				tsk->signal->utime));
	info.si_stime = cputime_to_clock_t(cputime_add(tsk->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 (!task_ptrace(tsk) && 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).
		 */
		ret = tsk->exit_signal = -1;
		if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)
			sig = -1;
	}
	if (valid_signal(sig) && sig > 0)
		__group_send_sig_info(sig, &info, tsk->parent);
	__wake_up_parent(tsk, tsk->parent);
	spin_unlock_irqrestore(&psig->siglock, flags);

	return ret;
}

리파렌트leader의 마지막 호출 함수killorphanded_pgrp:
/*
 * Check to see if any process groups have become orphaned as
 * a result of our exiting, and if they have any stopped jobs,
 * send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
 */
static void
kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)
{
	struct pid *pgrp = task_pgrp(tsk);
	struct task_struct *ignored_task = tsk;

	if (!parent)
		 /* exit: our father is in a different pgrp than
		  * we are and we were the only connection outside.
		  */
		parent = tsk->real_parent;
	else
		/* reparent: our child is in a different pgrp than
		 * we are, and it was the only connection outside.
		 */
		ignored_task = NULL;

	if (task_pgrp(parent) != pgrp &&  //  tsk            
	    task_session(parent) == task_session(tsk) && //  tsk           
	    will_become_orphaned_pgrp(pgrp, ignored_task) && //         ******************I ask you, have you ever kown what it is to be an orphan?******************************
	    has_stopped_jobs(pgrp)) { //       job,           debugger,    《  Linux    》
		__kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp); //      SIGHUP  ,     ?
		__kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);//      SIGCONT  ,     ?
	}
}

그럼 진행팀이 고아가 될 거라고 어떻게 판단합니까?킬orphanded_pgrp중willbecome_orphanded_pgrp는 이 작업을 완성했다.
/*
 * Determine if a process group is "orphaned", according to the POSIX
 * definition in 2.2.2.52.  Orphaned process groups are not to be affected
 * by terminal-generated stop signals.  Newly orphaned process groups are
 * to receive a SIGHUP and a SIGCONT.
 *
 * "I ask you, have you ever known what it is to be an orphan?"
 */
static int will_become_orphaned_pgrp(struct pid *pgrp, struct task_struct *ignored_task)
{
	struct task_struct *p;

	do_each_pid_task(pgrp, PIDTYPE_PGID, p) { //              
		if ((p == ignored_task) ||  //  p pgrp    ??
		    (p->exit_state && thread_group_empty(p)) || //  pgrp  p                      
		    is_global_init(p->real_parent)) //  pgrp p       init  ,                 ( list_empty(&p->thread_group) == 1;)。
			continue;

		if (task_pgrp(p->real_parent) != pgrp && //  p     p       
		    task_session(p->real_parent) == task_session(p)) //  p     p         
			return 0; //  0,      
	} while_each_pid_task(pgrp, PIDTYPE_PGID, p);

	return 1; //  1,     。
}

이제 우리는 먼저 고아의 진행 과정에 관한 것을 정리하고 상세한 해석은 을 볼 수 있다.
프로세스의 생부(p opptr) 프로세스와 양부(p pptr) 프로세스는 일반적으로 일치하지만 프로세스가 추적될 경우 ppptr는 추적 프로세스를 가리키고 popptr의 지향은 변하지 않습니다.프로세스가 하위 프로세스에 앞서 죽었을 때, 하위 프로세스를 특정한 프로세스에 맡겨야 한다.현재 프로세스가 하나의 라인이라면 같은 라인 그룹의 다음 라인에 맡깁니다. 하위 프로세스의 popptr는 이 라인을 가리킨다.그렇지 않으면 init 프로세스에 맡길 수밖에 없습니다.현재 프로세스와 생부 프로세스가 다른session에 속하고 다른 그룹에 속하며 그 그룹이 있는 그룹과 그 아버지 프로세스 사이의 유일한 유대라면 현재 프로세스가 존재하지 않으면 이 그룹은 고아가 된다.

좋은 웹페이지 즐겨찾기