--------------------------------------------------------
exit & wait
--------------------------------------------------------
·fs_exit 函数
PRIVATE int fs_exit()
{
int i;
struct proc *p = &proc_table[fs_msg.PID]; /* 请求退出的那个进程 */
for (i = 0; i < NR_FILES; i++)
{
if (p->filp[i])
{
/* release the inode */
p->filp[i]->fd_inode->i_cnt--;
/* release the file desc slot */
if (--p->filp[i]->fd_cnt == 0) /* 说明此进程是最后一个引用此文件的进程了 */
p->filp[i]->fd_inode = 0;
p->filp[i] = 0;
}
}
return 0;
}
——fs_exit 做的工作和 fs_open 相同!
·do_exit 函数
PUBLIC void do_exit(int status)
{
int i;
int pid = mm_msg.source; /* PID of caller */
int parent_pid = proc_table[pid].p_parent; /* 获得发 exit 消息进程的 parent */
struct proc *p = &proc_table[pid];
/* tell FS, see fs_exit() */
MESSAGE msg2fs;
msg2fs.type = EXIT;
msg2fs.PID = pid;
send_recv(BOTH, TASK_FS, &msg2fs);
// 进程表对应的就是这段内存,如果进程表设置为了空闲
// 那么这段内存就可以被覆盖
free_mem(pid);
p->exit_status = status;
if (proc_table[parent_pid].p_flags & WAITING) /* 父进程在等 */
{
proc_table[parent_pid].p_flags &= ~WAITING;
cleanup(&proc_table[pid]);
}
else
proc_table[pid].p_flags |= HANGING; /* 父进程没有 wait ,就将自己设置为 hanging */
/* 要退出的进程还有子进程 */
for (i = 0; i < NR_TASKS + NR_PROCS; i++)
{
if (proc_table[i].p_parent == pid)
{ /* is a child */
proc_table[i].p_parent = INIT;
if ((proc_table[INIT].p_flags & WAITING) &&
(proc_table[i].p_flags & HANGING))
{
proc_table[INIT].p_flags &= ~WAITING;
cleanup(&proc_table[i]); /* 这里是唤醒 INIT 进程 */
}
}
}
}
——函数流程:
首先判断父进程是否在等自己,如果父进程在等自己,那么唤醒父进程,清除自己,很到位 ... 如果父进程没有在等,那么将自己设置为 hanging ,此时父进程的子进程中多了一个 hanging 的进程,当下次父进程调用 wait 的时候,会直接清除此 hanging 的进程并对那次 wait 直接返回!如果父进程不再调用 wait,那么会在父进程调用 exit 的时候将此 hanging 进程过继给 init 进程,init 在循环等待,所以遇见 hanging 进程直接清除并 init 进程继续循环 wait!
这是关于有没有父进程在等自己的问题,接着来看看如果要退出的此进程有孩子进程的情况,如果此进程有孩子,并且孩子在 hanging(这就是我们上面刚刚分析的因为这个孩子在 exit 的时候此进程并没有在 wait,所以孩子就将自己设置为了 hanging),那么就会将该孩子交给 INIT 进程,并且立即展开一次 【INIT 返回 & 清除此 hanging 进程】 的工作(因为在 INIT wait 的时候,遇见自己的孩子是 hanging,INIT 也会执行这一步)!如果此进程的孩子没有 hanging,那么将此孩子过继给 INIT 就行了,因为孩子没有 hanging,所以孩子是能够将来退出的,在孩子退出的时候,它唤醒的将是 INIT 父进程!
·do_wait 函数
PUBLIC void do_wait()
{
int pid = mm_msg.source;
int i;
int children = 0;
struct proc *p_proc = proc_table;
/* 要进入等待的进程有 Hanging 的孩子 */
for (i = 0; i < NR_TASKS + NR_PROCS; i++, p_proc++)
{
if (p_proc->p_parent == pid)
{
children++;
if (p_proc->p_flags & HANGING) /* 调用 wait 的进程有孩子正在 hanging */
{ /* 则直接唤醒它并将它的孩子清除掉 */
cleanup(p_proc);
return;
}
}
}
if (children) /* 有孩子但是孩子没有 hanging,进入wait */
proc_table[pid].p_flags |= WAITING;
else
{
/* 没有孩子,直接返回· */
MESSAGE msg;
msg.type = SYSCALL_RET;
msg.PID = NO_TASK;
send_recv(SEND, pid, &msg);
}
}
wait 函数就是分为三种情况:
1)如果有孩子在 hanging ,那么清除孩子并且此次 wait 返回
2)如果有孩子但是孩子正常运行,那么进入 wait 状态
3)如果没有孩子,直接返回!
·clean_up 函数
PRIVATE void cleanup(struct proc *proc)
{
MESSAGE msg2parent;
msg2parent.type = SYSCALL_RET;
msg2parent.PID = proc2pid(proc); /* 子进程的 ID 号 */
msg2parent.STATUS = proc->exit_status; /* 子进程的状态 */
send_recv(SEND, proc->p_parent, &msg2parent);
proc->p_flags = FREE_SLOT; /* ** */
}
cleanup 函数唤醒父进程并清除自己!
init 进程创建第一个进程之后就进入循环 wait 的状态:
while (1)
{
int s;
int child = wait(&s);
printf("child (%d) exited with status: %d.\n", child, s);
}
运行
OK,关于 wait 和 exit 的问题我们就分析到这里!