1:代码版本
Linux-0.11
exit.c
2:销毁流程
销毁函数也是系统调用
die()-> do_exit()
具体过程:
- 首先会释放进程的代码段和数据段占用的内存进行释放
- 关闭进程打开的文件,对当前的目录和i节点进行同步
- 如果当前要销毁的进程有子进程,就让1号进程作为子进程的父进程 (init进程)
- 如果当前进程是一个会话头进程,则会终止会话中的所有进程
- 改变当前进程的运行状态,变成TASK_ZOMBIE僵死状态,并向父进程发送信号SIGCHLD
- 父进程再运行子进程的时候会运行wait(), wait_pid()用来等待某个子进程终止,收到子程序的 SIGCHLD信号的时候,就会来终止僵死状态的子进程
- 父进程会把子进程的运行时间累加到自己的变量进程中
- 把对应的子进程的进程描述符结构体释放,置空任务数组中的空槽
#define SIGCHLD 17
3:相关的销毁函数
3.1 release
清空对应进程数组,释放对应内存页
void release(struct task_struct * p)
{
int i;
if (!p)
return;
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {//找到对应的p
task[i]=NULL;//清空进程数据对应的位置
free_page((long)p); //释放p的进程结构体空间
schedule();
return;
}
panic("trying to release non-existent task");
}
3.2 send_sig
向指定任务发送信号
static inline int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32) //(p为0 sig的值小于1或者大于32,返回错误)
return -EINVAL;
if (priv || (current->euid==p->euid) || suser())//(权限唯一 || 当前的有效用户id = 指定的用户id || 超级用户)
p->signal |= (1<<(sig-1)); //修改p进程信号值
else
return -EPERM;
return 0;
}
3.3 终止会话kill_session
终止当前进程的会话,给其发送SIGHUP
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);//设置信号为SIGHUP 终止会话
}
}
3.4 发送信号 sys_kill
向对应的进程号或进程组号发送任何信号
pid > 0 给对应的pid发送 sig
pid = 0 给当前进程的进程组发送sig
pid = -1 给任何进程发送sig
pid <-1 给进程组号为-pid的进程组发送sig
/*
* XXX need to check permissions needed to send signals to process
* groups, etc. etc. kill() permissions semantics are tricky!
*/
int sys_kill(int pid,int sig)
{
struct task_struct **p = NR_TASKS + task; //p执行进程组最后
int err, retval = 0;
if (!pid) while (--p > &FIRST_TASK) { //pid == 0 遍历进程组
if (*p && (*p)->pgrp == current->pid) //找到对应进程组号和当前pid一样的进程结构体
if (err=send_sig(sig,*p,1))
retval = err;
} else if (pid>0) while (--p > &FIRST_TASK) { //pid > 0 遍历进程组
if (*p && (*p)->pid == pid) //找到对应的pid的进程结构体
if (err=send_sig(sig,*p,0))//发送信号
retval = err;
} else if (pid == -1) while (--p > &FIRST_TASK) { //pid == -1 ; 遍历进程组,
if (err = send_sig(sig,*p,0))//每一个进程结构体发送信号
retval = err;
else while (--p > &FIRST_TASK)//pid < -1 ; 遍历进程组,给进程组号为-pid的进程组发送sig
if (*p && (*p)->pgrp == -pid)//找到对应进程组号为|pid|绝对值的所有进程结构体
if (err = send_sig(sig,*p,0))
retval = err;
return retval;
}
3.5 tell_father
子进程向父进程发送SIGCHLD信号
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) //根据pid找到对应的结构体数组中的位置task[i]
continue;
task[i]->signal |= (1<<(SIGCHLD-1));//向task[i]发送信号
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"); //没有找到对应的父进程
release(current);//释放当前进程
}
3.6 do_exit
int do_exit(long code)
{
int i;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); //释放TSS段的内存
free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); //释放LDT段的内存
for (i=0 ; i<NR_TASKS ; i++) //遍历进程数组
if (task[i] && task[i]->father == current->pid) { //task[i]不为NULL && 当前要关闭进程是某个进程task[i]的父进程
task[i]->father = 1; //这个进程task[i]的父进程要改变成 1号进程,防止task[i]没有父进程
if (task[i]->state == TASK_ZOMBIE) //如果这个进程task[i]的状态是僵死状态
/* assumption task[1] is always init */
(void) send_sig(SIGCHLD, task[1], 1); //向父进程(1号进程)发送信号SIGCHLD
}
for (i=0 ; i<NR_OPEN ; i++) //遍历当前进程打开的文件数组(最大20)
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 */
}
3.7 sys_waitpid
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) //option为1这个函数不阻塞,0会阻塞
{
int flag, code;
struct task_struct ** p;
verify_area(stat_addr,4); //验证区域stat_addr是否可以使用
repeat:
flag=0;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { //p从进程数组的最后到第一个进行遍历。开始进行筛选
if (!*p || *p == current) // 如果(p为NULL || p是当前执行的进程 )
continue; //继续回到循环
if ((*p)->father != current->pid)// 如果(p是父进程 )
continue;//继续回到循环
if (pid>0) {// 如果(p > 0)
if ((*p)->pid != pid)//如果不是要找的pid
continue;//继续回到循环
} else if (!pid) {// 如果(p == 0)
if ((*p)->pgrp != current->pgrp)//如果不是要找的组id pgrp
continue;//继续回到循环
} else if (pid != -1) {// 如果(p == 0)
if ((*p)->pgrp != -pid)//如果要找的组id pgrp不是 pid的绝非值
continue;//继续回到循环
}
switch ((*p)->state) {//对应进程p的不同状态
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;//获取子进程pid 到 flag
code = (*p)->exit_code;//获取退出码到 到 code
release(*p);//释放子进程p
put_fs_long(code,stat_addr);
return flag;//返回falg 退出函数
default://子程序其他状态 flag=1;
flag=1;
continue;
}
}
if (flag) { //运行到这说明子程序不需要被释放
if (options & WNOHANG)//options 为1 就不做任何操作就返回0
return 0;
current->state=TASK_INTERRUPTIBLE;//options 为0 就改变当前进程状态INTERRUPTIBLE 为可打断睡眠,进入睡眠
schedule();//调度其他进程运行
if (!(current->signal &= ~(1<<(SIGCHLD-1))))//当收到信号后,并且信号是SIGCHLD
goto repeat;//从repeat再次执行
else
return -EINTR;
}
return -ECHILD;
}