对linux 0.11版本内核中的进程退出和回收的理解

常常看到调用exit结束进程,那结束进程到底做了哪些事情叫结束进程呢?目前在看的linux 0.11版本的内核里面的exit()系统调用里面就是调用了do_exit(),它的源码如下:

int do_exit(long code)
{
	int i;
	
	free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
	free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
	for (i=0 ; i<NR_TASKS ; i++)
		if (task[i] && task[i]->father == current->pid) {
			task[i]->father = 1;
			if (task[i]->state == TASK_ZOMBIE)
				/* assumption task[1] is always init */
				(void) send_sig(SIGCHLD, task[1], 1);
		}
	for (i=0 ; i<NR_OPEN ; i++)
		if (current->filp[i])
			sys_close(i);
	iput(current->pwd);
	current->pwd=NULL;
	iput(current->root);
	current->root=NULL;
	iput(current->executable);
	current->executable=NULL;
	if (current->leader && current->tty >= 0)
		tty_table[current->tty].pgrp = 0;
	if (last_task_used_math == current)
		last_task_used_math = NULL;
	if (current->leader)
		kill_session();
	current->state = TASK_ZOMBIE;
	current->exit_code = code;
	tell_father(current->father);
	schedule();
	return (-1);	/* just to suppress warnings */
}

        首先,调用free_page_tables去释放代码段和数据段,get_base通过ldt[1]和ldt[2]这两个描述符来找到代码段和数据段的起始位,熟悉LDT的话应该知道这点。get_limit通过两个段选择符来找到描述符进而确定代码段和数据段的段界限。free_page_tables()即要去释放页表指向的物理内存空间,也要去释放页表本身占据的内存空间,这里的释放跟内存管理有关,先不去管。
        紧接着,进入一个循环,如果某个进程的父进程是当前进程,则将当前进程的父进程设置为init进程,在设置完之后,再去查看子进程的状态是否在僵死状态,如果是,则向该子进程的父进程发送信号,这里的父进程必定是init进程了,这样就可以通过init进程去回收该僵死进程。
        之后又进入一个循环,来关闭所有该进程所打开的文件。
        之后放回各个i结点并分别置空。
        紧接着去判断该进程是否是回话头领并且占据着终端控制权,若果是的话要将终端释放。还要去终止该回话中的其他相关进程。最后要把自己设置为僵死状态,设置退出号并通向其父进程发送信号表明自己已经结束。之后运行调度程序进行进程切换。
        在这里可以发现进程调用do_exit()之后释放了绝大多数占用的资源,但是进程描述符表和内核堆栈共用的那块地址空间怎么没有释放?其实这两个过程应该是分开的,我的理解是假如子进程直接把所有的东西的释放完了,那父进程无从去得到子进程的相关信息。这就要关注另一段代码,waitpid()系统调用,其源码如下:

int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
	int flag, code;
	struct task_struct ** p;

	verify_area(stat_addr,4);
repeat:
	flag=0;
	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
		if (!*p || *p == current)
			continue;
		if ((*p)->father != current->pid)
			continue;
		if (pid>0) {
			if ((*p)->pid != pid)
				continue;
		} else if (!pid) {
			if ((*p)->pgrp != current->pgrp)
				continue;
		} else if (pid != -1) {
			if ((*p)->pgrp != -pid)
				continue;
		}
		switch ((*p)->state) {
			case TASK_STOPPED:
				if (!(options & WUNTRACED))
					continue;
				put_fs_long(0x7f,stat_addr);
				return (*p)->pid;
			case TASK_ZOMBIE:
				current->cutime += (*p)->utime;
				current->cstime += (*p)->stime;
				flag = (*p)->pid;
				code = (*p)->exit_code;
				release(*p);
				put_fs_long(code,stat_addr);
				return flag;
			default:
				flag=1;
				continue;
		}
	}
	if (flag) {
		if (options & WNOHANG)
			return 0;
		current->state=TASK_INTERRUPTIBLE;
		schedule();
		if (!(current->signal &= ~(1<<(SIGCHLD-1))))
			goto repeat;
		else
			return -EINTR;
	}
	return -ECHILD;
}

        分析:首先要明确参数对这个系统调用的影响!当pid>0表示等待回收该pid的子进程;pid=0时,回收进程组号等于当前进程号的子进程;pid<-1时回收进程号为-pid的子进程;pid=-1,回收任何子进程。option可以使该系统调用直接返回或者陷入阻塞等待;stat_addr用来保存状态信息。
        下面看分析下代码,首先遍历所有进程排除不符合条件的进程,包括空的,进程号等于自己的,不是当前进程子进程的进程,接下来的进程就都是当前进程的子进程了(也就是经过上述筛选后的进程都符合pid=-1时的调用了,所以后面不需要排除pid=-1里面的不符合条件的进程了)。接下来分析pid。
       ①pid>0时,即要等待指定的进程,所以排除掉进程号不等于该pid的子进程
       ②pid=0,即要回收进程组号等于该进程号的子进程,所以排除掉进程组号不符合的子进程
       ③pid<-1,排除掉进程号不等于-pid的子进程
做完这一切选择之后,都是符合条件的子进程了。

       case1:如果WUNTRACE置位了,也就是说不追踪,这样的话对暂停状态的信号就直接返回pid退出,否则继续扫描。
       case2:子进程处于僵死状态,把它在用户态和内核态运行的时间累加到当前进程中,取得子进程的pid和退出码,这里也可以看出为什么子进程调用do_exit()的时候为什么不直接释放掉进程描述符这个结构,因为父进程还要获取子进程描述符中上述信息。之后就可以调用release()去释放描述符(这个函数里面会调用schedule()函数,书上写着觉得没必要,好像是没什么必要,每回收一次描述符就要调度一次),并把调用put_fs_long()将状态信息也就是退出码写到用户态堆栈上面,之后返回flag也就是子进程pid。
       default:找到过子进程,但它不出在以上两个状态,就置为1。
       执行到if(flag)时,说明该子进程并不符合僵死或暂停,也就是执行了默认操作,假如设置了WNOHANG,也就是不符合条件直接返回,否则,则是阻塞调用,它将自己设置为可中断等待状态,然后执行任务切换。等下次切换回来,如果还没收到子进程结束的信号,则继续遍历!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值