孤儿进程退出分析

如果父进程在子进程之前退出,必须有机制来保证子进程能找到一个新的父亲,否则这些成为孤儿的进程就会在退出时永远处于僵死状态,,白白地耗费内存.僵尸状态是在调用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)  continue;//<span style="font-size: 14px; line-height: 24px;">如果得到的下一个进程被标记了 PF_EXITING ,就不符合要求,需要继续遍历</span>
               <span style="font-size: 14px; line-height: 24px;">if (unlikely(pid_ns->child_reaper == father))//unlikely如果相同进程返回true</span>
		/*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进程,最后该进程的描述符删除操作就由养父进程进行处理,如果父进程先退出,该进程已经成为孤儿进程的话
</pre><pre name="code" class="cpp" style="font-family: Verdana; font-size: 14px; line-height: 24px;">在给子进程找到合适的新父进程之后,需要对该进程的所有子进程设置新的父进程
list_for_each_entry_safe(p, n, &father->children, sibling) {
//当前退出的线程不是其真正的父亲,那么必然是被跟踪的进程.否则在系统日志打印错误
<span class="keyword" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 102, 153); font-weight: bold; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 11.818181991577148px; line-height: 17.27272605895996px;">if</span><span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 11.818181991577148px; line-height: 17.27272605895996px;"> (p->parent == father) {  </span>

BUG_ON(task_ptrace(p));

    p->parent = p->real_parent;  //将当前进程的父进程设置为真正的父进程

 
}
<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 11.818181991577148px; line-height: 17.27272605895996px;">reparent_thread(father, p, &dead_children);  </span>

最后调用exit_ptrace()函数对ptrace的子进程寻找父亲

















  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Linux 中,僵尸进程孤儿进程都是指与父进程不再有联系的进程,它们通常是由于进程管理不当或程序错误导致的。 **僵尸进程**是已经完成执行任务,但其父进程还没有来得及处理其退出状态的进程。当进程完成执行后,它的退出状态(也称为退出码或终止状态)会被保存在系统中,直到父进程通过 `wait` 或 `waitpid` 等函数来获取该状态。如果父进程没有处理该状态,那么该进程就会成为僵尸进程,占用系统资源。要清理僵尸进程,可以使用 `kill` 命令向其父进程发送 `SIGCHLD` 信号,或者重新编写程序,使其正确处理子进程退出状态。 **孤儿进程**是指其父进程已经退出或被终止,但其自身仍在运行的进程孤儿进程会被 `init` 进程进程号为 `1`)接管,`init` 进程会定期检查系统中是否有孤儿进程,并且将其的父进程设置为 `init` 进程。要避免孤儿进程的产生,可以在父进程退出之前,等待子进程退出,或者将子进程的父进程设置为 `init` 进程。 可以使用 `ps` 命令来查看系统中的僵尸进程孤儿进程。使用以下命令可以查看所有僵尸进程: ``` ps aux | grep 'Z' ``` 其中,`aux` 选项用于显示所有进程,`grep 'Z'` 用于查找所有状态为 `Z` 的进程,即僵尸进程。 使用以下命令可以查看所有孤儿进程: ``` ps -ejH ``` 其中,`-e` 选项用于显示所有进程,`-j` 选项用于以层次结构的形式显示进程,`-H` 选项用于显示所有孤儿进程

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值