int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}
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) {
// 子进程的新父进程是进程id为1的进程
task[i]->father = 1;
/*
如果子进程刚把自己的状态改成TASK_ZOMBIE,执行到tell_father里的代码时,时间片到了,
然后调度父进程执行,这时候父进程退出了,再切换到子进程执行的时候,
子进程给父进程发信号就丢失了,所以这里补充一下这个逻辑,给新的父进程发信号
*/
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);
// 回写inode到硬盘
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);
// 重新调度进程(tell_father里已经调度过了)
schedule();
return (-1); /* just to suppress warnings */
}
释放地址
// from是线性地址。释放from开始,连续的n个大小为4MB的页面对应的物理地址。最后释放页表、页目录项
int free_page_tables(unsigned long from,unsigned long size)
{
unsigned long *pg_table;
unsigned long * dir, nr;
// 判断是否按4MB对齐
if (from & 0x3fffff)
panic("free_page_tables called with wrong alignment");
if (!from)
panic("Trying to free up swapper memory space");
// 算出size包含多少个MB,比如size是0 - 1>>22,则计算机后是1
size = (size + 0x3fffff) >> 22;
/*
页目录在地址0开始的地方,首先右移得到页目录索引,
根据索引得到页目录项内容,因为页目录项的内容占4个字节,
其中高20位是页表地址,低12位是标记位,,所以要乘以4得到
from对应的页目录项的地址。即dir = from >> 22 << 2 = from >> 20,
但是代码里是直接右移20位,所以需要和0xffc与,把低两位置0,最后得到from
对应的页目录项的地址
*/
dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */
for ( ; size-->0 ; dir++) {
// 低位是1说明该页目录项有效
if (!(1 & *dir))
continue;
// *dir为页表首地址,与0xfffff000是因为高二十位是有效地址,低12位是标记位
pg_table = (unsigned long *) (0xfffff000 & *dir);
// 释放每个页表指向的物理地址
for (nr=0 ; nr<1024 ; nr++) {
// 页表是否有效,有效则释放*pg_table指向物理地址,以4kb对齐
if (1 & *pg_table)
// 与0xfffff000是因为高二十位是有效地址,低12位是标记位
free_page(0xfffff000 & *pg_table);
// 置页表无效
*pg_table = 0;
// 下一个页表
pg_table++;
}
// 释放页表占据的物理地址
free_page(0xfffff000 & *dir);
// 置页目录项为无效
*dir = 0;
}
invalidate();
return 0;
}
结束会话
// 结束会话,给该会话的所有进程发SIGHUP信号,因为子进程会继承父进程的sessionid,所以if可能会多次成立
static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;
while (--p > &FIRST_TASK) {
if (*p && (*p)->session == current->session)
(*p)->signal |= 1<<(SIGHUP-1);
}
}
通知父进程
// 子进程退出,通知进程id是pid的父进程
static void tell_father(int pid)
{
int i;
if (pid)
for (i=0;i<NR_TASKS;i++) {
if (!task[i])
continue;
if (task[i]->pid != pid)
continue;
// 根据pid找到父进程,设置子进程退出的信号
task[i]->signal |= (1<<(SIGCHLD-1));
return;
}
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
printk("BAD BAD - no father found\n\r");
// 释放pcb结构
release(current);
}
// 释放pcb的一页内存,重新调度进程
void release(struct task_struct * p)
{
int i;
if (!p)
return;
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
free_page((long)p);
schedule();
return;
}
panic("trying to release non-existent task");
}