孤儿进程退出分析

如果父进程在子进程之前退出,必须有机制来保证子进程能找到一个新的父亲,否则这些成为孤儿的进程就会在退出时永远处于僵死状态,,白白地耗费内存.僵尸状态是在调用do_exit()函数时,最后会调用exit_notify()函数向父进程发送信号,给子进程重新找养父,养父为线程组中的其他线程或者为init进程,并把进程(存放在task_struct结构的exit_state中)设成EXIT_ZOMBIE。对于这个问题,解决方法是给子进程在当前线程组内找一个线程作为父亲,如果不行,就让init做它们的父进程。在do_exit()中会调用exit_notify(),该函数会调用forget_original_parent(),而后者会调用find_new_reaper()来执行寻父过程:

了解forget_original_parent()需要以下几个基本知识:forget_original_parent()定义在:linux/kernel/exit.c

1    parent == real_parent 的子进程

这是本进程的子进程,并且没有被ptrace,处于本进程的children list上。

2   parent == father && parent != real_parent的子进程

被本进程ptrace的其它进程的子进程,处于本进程的children list上。

3   real_parent == father && parent != real_parent的子进程

本进程的子进程,但正在被其它进程ptrace,处于本进程的ptrace_children list上。

/*
 * 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.
 */
static struct task_struct *find_new_reaper(struct task_struct *father)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(father);
	struct task_struct *thread;

	thread = father;
	while_each_thread(father, thread) {
		//依次遍历该结束的进程所在线程组的下一个进程
		if (thread->flags & PF_EXITING)  <span style="font-family: Verdana;">continue;</span>
<span style="white-space:pre">		</span>//如果得到的下一个进程被标记了 PF_EXITING ,就不符合要求,需要继续遍历
<span style="white-space:pre">		</span>//task_struct结构体有一个flags标志,表示进程的当前状态
<span style="white-space:pre">		</span>//<span style="font-family: Verdana;">PF_STARTING:<span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14.44444465637207px; line-height: 23.99305534362793px;">进程是否正在被创建   <span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14.44444465637207px; line-height: 23.99305534362793px;">PF_EXITING:进程退出  <span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14.44444465637207px; line-height: 23.99305534362793px;">PF_MEMALLOC:<span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14.44444465637207px; line-height: 23.99305534362793px;">当前是否在分配内存</span></span></span></span></span>
		if (unlikely(pid_ns->child_reaper == father))//unlikely如果相同进程返回true
		/*child_reaper 的作用是在当前线程组如果没有找到养父的话,需要通过托管表示进程结束后,需要这个child_reaper指向的进程对这个结束的进程进行托管,其中的一个目的是对孤儿进程进行回收。若该托管进程是该结束进程本身,就需要重新设置托管进程,就是将该托管进程设置为当前进程的养父进程thread。*/
		pid_ns->child_reaper = thread;
		return thread;//在该结束进程所在的线程组中找到符合要求的进程,返回即可
	}

	/*
	如果该结束进程所在的线程组中没有其他的进程,
	函数就返回该结束进程所在命名空间的 child_reaper 指向的托管进程
	(前提是该托管进程不是该结束进程本身)
	*/
	if (unlikely(pid_ns->child_reaper == father)) {
		/*
		如果该结束进程所在命名空间的 child_reaper 指向的托管进程就是该结束进程本身,
		而程序运行至此,说明在该线程组中已经找不到符合要求的进程,
		此时,需要将托管进程设置为 init 进程,供函数返回
		*/
		write_unlock_irq(&tasklist_lock);
		if (unlikely(pid_ns == &init_pid_ns))//如果是init进程需要给出error,init进程不能终止
			panic("Attempted to kill init!");

		zap_pid_ns_processes(pid_ns);
		write_lock_irq(&tasklist_lock);
		/*
		 * We can not clear ->child_reaper or leave it alone.
		 * There may by stealth EXIT_DEAD tasks on ->children,
		 * forget_original_parent() must move them somewhere.
		 */
		pid_ns->child_reaper = init_pid_ns.child_reaper;//把当前进程空间的托管进程设置为init进程的托管进程
	}

	return pid_ns->child_reaper;//返回找到的托管进程也就是养父进程
}
reaper = find_new_reaper(father);//在该函数中找到养父进程,要么是线程组内的其他进程,要么是init进程,最后该进程的描述符删除操作就由养父进程进行处理,如果父进程先退出,该进程已经成为孤儿进程的话

<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px; text-align: justify;">在给子进程找到合适的新父进程之后,需要对该进程的所有子进程设置新的父进程</span><span style="color:#111111;">
list_for_each_entry_safe(p, n, &father->children, sibling) {
</span><span style="color: rgb(17, 17, 17); white-space: pre;">		</span><span style="color:#111111;">p->real_parent = reaper;
</span><span style="color: rgb(17, 17, 17); white-space: pre;">		</span><span style="color:#111111;">if (p->parent == father) {</span><pre name="code" class="cpp" style="font-size: 14.44444465637207px; line-height: 23.99305534362793px;"><span style="color:#111111;">//</span><span style="font-family: 黑体;font-size:12px; line-height: 26px; background-color: rgb(255, 255, 255);">当前退出的线程不是其真正的父亲,那么必然是被跟踪的进程.否则在系统日志打印错误</span>

BUG_ON(task_ptrace(p));


<span style="white-space: pre;">		</span>   <span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;">p->parent =</span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;"> p->real_parent;</span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;">	</span>//将当前进程的父进程设置为真正的父进程
<span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14px; line-height: 24px; white-space: pre;">		</span><span style="font-family:Verdana;color:#111111;"><span style="font-size: 14px; line-height: 24px;">}
</span></span><span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14px; line-height: 24px; white-space: pre;">		</span><span style="font-family:Verdana;color:#111111;"><span style="font-size: 14px; line-height: 24px;">reparent_thread(father, p, &dead_children);
</span></span><span style="color: rgb(17, 17, 17); font-family: Verdana; font-size: 14px; line-height: 24px; white-space: pre;">	</span><span style="font-family:Verdana;color:#111111;"><span style="font-size: 14px; line-height: 24px;">}
</span></span>
最后调用exit_ptrace()函数对ptrace的子进程寻找父亲
时间: 2024-10-26 06:13:16

孤儿进程退出分析的相关文章

孤儿进程

父进程运行结束,但子进程还在运行(未运行结束)的子进程就称为孤儿进程(Orphan Process).孤儿进程最终会被 init 进程(进程号为 1 )所收养,并由 init 进程对它们完成状态收集工作. 孤儿进程是没有父进程的进程,为避免孤儿进程退出时无法释放所占用的资源而变为僵尸进程(什么是僵尸进程,请看<僵尸进程>),进程号为 1 的 init 进程将会接受这些孤儿进程,这一过程也被称为"收养".init 进程就好像是一个孤儿院,专门负责处理孤儿进程的善后工作.每当出

孤儿进程与僵死进程

孤儿进程: 因父亲进程先退出而导致一个子进程被 init 进程收养的进程为孤儿进程,即孤儿进程的父亲更改为 init 进程,该进程在孤儿进程退出后回收它的内核空间资源. 僵死进程: 进程已经退出,但它的父亲进程还没有回收内核资源的进程为僵死进程,即该进程在内核空间的 PCB(进程控制块) 没有释放. 1.以下是一个孤儿进程的示例程序,在此程序中,让父进程先退出,然后子进程再次输出自己的父亲进程号: #include <stdio.h> #include <unistd.h> int

erlang监控进程在启动进程退出后异常退出原因分析

一.问题引出 erlang监控进程在启动时设置了trap_exit为true,即会捕获到退出信号,会将退出信号转换为{'EXIT',Pid,Reason}存入自己的邮箱中,因此与监控进程link关系的进程退出后,监控进程能够很坦然的截获退出信号,自身不退出.启动erlang监控进程的进程,会和监控进程建立link关系,然而当启动进程退出时,监控进程没有象正常的情况,发生了异常退出,为什么设置了trap_exit为true,还会退出呢? 二.原因分析 查看supervisor的源码,supervi

主进程被杀死时,如何保证子进程同时退出,而不变为孤儿进程(一)

在Python中,由于全局解释器锁GIL的存在,使得Python中的多线程并不能大大提高程序的运行效率(这里单指CPU密集型),那么在处理CPU密集型计算时,多用多进程模型来处理,而Python标准库中提供了multiprocessing库来支持多进程模型的编程.multiprocessing中提供了的Process类用于开发人员编写创建子进程,接口类似于标准库提供的threading.Thread类,还提供了进程池Pool类,减少进程创建和销毁带来开销,用以提高复用(见前文). 在多线程模型中

apue第九章 孤儿进程组例子

1. 为什么会有孤儿进程组的概念,APUE没写清楚,但是GNU有规定: 孤儿进程组不可以获得终端,这是为了保证控制进程死掉后他的终端可以安全分配给新session.posix要求向新孤儿进程组中停止状态的进程(也有说是孤儿进程组里所有进程)发送SIGHUP(挂起)信号和SIGCONT(继续)信号.首先处理SIGHUP信号,系统默认处理是终止进程,然而也可以另行处理这样进程会继续执行,但任不可以再获得终端. 2. 书本代码示例apue.3e/relation/orphan3.c较费解 #inclu

Linux中的僵尸进程和孤儿进程

在UNIX里,除了进程0(即PID=0的交换进程,Swapper Process)以外的所有进程都是由其他进程使用系统调用fork创建的,这里调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程,因而除了进程0以外的进程都只有一个父进程,但一个进程可以有多个子进程.        操作系统内核以进程标识符(Process Identifier,即PID)来识别进程.进程0是系统引导时创建的一个特殊进程,在其调用fork创建出一个子进程(即PID=1的进程1,又称init)

孤儿进程 &amp;&amp; 僵尸进程

background: unix: 每个子进程退出,内核释放该进程所有资源,打开的文件,占用的内存 保留的信息:the process ID,the termination status of the process,the amount of CPU time taken by the process 父进程用wait()/waitpid()释放子进程的保留信息 父进程不调用wait()/waitpid()进程号一直被占用,系统所能提供的进程号有限,没有可用的进程号导致系统不能产生新的进程 Z

孤儿进程和僵尸进程

孤儿进程和僵尸进程 一.定义:什么是孤儿进程和僵尸进程 僵尸进程:一个子进程在其父进程还没有调用wait()或waitpid()的情况下退出.这个子进程就是僵尸进程. 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程.孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作. 僵尸进程将会导致资源浪费,而孤儿则不会. 子进程持续10秒钟的僵尸状态(EXIT_ZOMBIE)------------------------------

孤儿进程与僵尸进程[总结]

http://www.cnblogs.com/Anker/p/3271773.htm 1.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊.今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎么解决,我只停留在概念上面,没有深入,倍感惭愧.晚上回来google了一下,再次参考APUE,认真总结一下,加深理解. 2.基本概念 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程.子进程的结束和父进程