/* * linux/kernel/exit.c * * (C) 1991 Linus Torvalds */
#include <errno.h> #include <signal.h> #include <sys/wait.h>
#include <linux/sched.h> #include <linux/kernel.h> #include <linux/tty.h> #include <asm/segment.h>
int sys_pause(void);//将进程置为睡眠状态,直到收到信号
int sys_close(int fd);//关闭指挥文件的系统调用
void release(struct task_struct * p)//释放指定进程占用的任务槽以及任务结构占用的内存页面。
{ int i;
if (!p) return; for (i=1 ; i<NR_TASKS ; i++)//NR_TASKS=64,扫描任务数组,找到指定任务
if (task[i]==p) { task[i]=NULL;//本任务置空
free_page((long)p);//清内存
schedule();//重新调度,在这里可能没有必要重新调度,不知作者为何要重新调度
return; } panic("trying to release non-existent task"); }
static inline int send_sig(long sig,struct task_struct * p,int priv)//向指定任务*p发送信号sig,置权限为priv,当priv为1时为强制发送
{ if (!p || sig<1 || sig>32) return -EINVAL; if (priv || (current->euid==p->euid) || suser())//强制发送标志位||当前进程 与指定进程相同也就是自己||为超级用户current->euid=0,(当前进程 的有效用户ID为0时,表示为超级用户)
p->signal |= (1<<(sig-1)); else return -EPERM; return 0; }
static void kill_session(void)//终止会话
{ struct task_struct **p = NR_TASKS + task;//将p指向任务的末端
while (--p > &FIRST_TASK) { if (*p && (*p)->session == current->session)//除任务0外,其会话ID等于当前任务的会话ID时,
(*p)->signal |= 1<<(SIGHUP-1);//发送挂断进程信号,同时注意一下发送信号的方法
} }
/* * 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)//向指定进程或进程组发送任何信号,并非杀死进程,(除进程号0外)
{ struct task_struct **p = NR_TASKS + task;//将p指向任务数组的末端,最多64个任务
int err, retval = 0;
if (!pid) while (--p > &FIRST_TASK) {//pid=0时,信号发送于以当前进程为领头的进程 组中的所有进程 。
if (*p && (*p)->pgrp == current->pid) //其进程组号==当前进程 的进程号,(进程组号一定等于这个进程组中的某一个进程的进程号,面这个进程就是这个进程组的领头进程 ,这个进程的进程号就是这个进程组的组号)
if (err=send_sig(sig,*p,1)) retval = err; } else if (pid>0) while (--p > &FIRST_TASK) {//pid>0时,信号发送于pid的进程
if (*p && (*p)->pid == pid) if (err=send_sig(sig,*p,0)) retval = err; } else if (pid == -1) while (--p > &FIRST_TASK)//pid=-1,信号发送于除第一个进程(初始进程init)外的所有进程
if (err = send_sig(sig,*p,0)) retval = err; else while (--p > &FIRST_TASK)//pid<-1,信号发送于进程组为pid的所有进程
if (*p && (*p)->pgrp == -pid) if (err = send_sig(sig,*p,0)) retval = err; return retval; }
static void tell_father(int pid)//通知父进程,向进程pid 发送信号SIGCHLD,默认情况下子进程停止或终止
{ int i;
if (pid) for (i=0;i<NR_TASKS;i++) {//扫描整个任务数组,64个任务
if (!task[i]) continue; if (task[i]->pid != pid) continue; 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"); release(current);//如果没有找到父进程,则释放自己,这样做并不好,应该有进程1来处理
}
int do_exit(long code)//程序退出处理函数
{ int i;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));//释放当前进程代码段的内存页,current->ldt[1]代码段,current->ldt[0]没用
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));//释放当前进程数据和堆栈段的内存页,current->ldt[2]数据堆栈段,current->ldt[0]没用
for (i=0 ; i<NR_TASKS ; i++) if (task[i] && task[i]->father == current->pid) {//如果当前进程有子进程,则将该子进程的父进程置为1
task[i]->father = 1; if (task[i]->state == TASK_ZOMBIE)//如果该子进程处于ZOMBIE状态,则向task[1](init进程)发送SIGCHLD信号
/* assumption task[1] is always init */ (void) send_sig(SIGCHLD, task[1], 1); } for (i=0 ; i<NR_OPEN ; i++)//NR_OPEN=20,关闭当前进程打开的所有文件
if (current->filp[i]) sys_close(i); iput(current->pwd);//对当前进程的pwd root executable进行同步操作,放回并分别置空
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协处理器指针,当前进程上次使用过协处理器,则将last_task_used_math置空
last_task_used_math = NULL; if (current->leader)//当有进程为会话领头时
kill_session();//终止会话
current->state = TASK_ZOMBIE;//将当前进程状态置为TASK_ZOMBIE,表明当前里程已释放了资源
current->exit_code = code;// 任务退出执行程序的退出码,保存有父进程提取的退出码
tell_father(current->father);//通知父进程
schedule();//进程调度
return (-1); /* just to suppress warnings */ }
int sys_exit(int error_code)//系统调用exit,终止进程
{ return do_exit((error_code&0xff)<<8); }
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)//挂起当前进程,直到pid指定的子进程退出终止或者收到要求终止该进程 的信号 ,或者需要调用 一个信号处理函数
{ 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) {//pid>0,等待指定的进程pid
if ((*p)->pid != pid)//不是当前要找的子进程,跳过
continue; } else if (!pid) {//pid==0 等待进程组号等于当前进程组号的任何子进程
if ((*p)->pgrp != current->pgrp)//扫描 的进程组号与当前的进程组号不相等,跳过
continue; } else if (pid != -1) {// pid<-1等待指定进程pid 绝对值的进程
if ((*p)->pgrp != -pid)//不是当前要找的子进程,跳过
continue; } //以下的语名定为要找到的进程
switch ((*p)->state) { case TASK_STOPPED://子进程非正常结束
if (!(options & WUNTRACED))//参数 option 可以为 0 或WNOHANG(如果没有任何已经结束的子进程则马上返回, 不予以等待)或 WUNTRACED (如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会),如果WUNTRACED没有置位,无需产即返回,可继续扫描其他进程
continue; put_fs_long(0x7f,stat_addr);//如果WUNTRACED置位,则写入状态信息,返回此子进程 号
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
flag=1; continue;//这个地方是否可以换成break 语名???
} } if (flag) { if (options & WNOHANG)//如果options==WNOHANG(如果没有任何已经结束的子进程则马上返回, 不予以等待)时,则立即返回
return 0; current->state=TASK_INTERRUPTIBLE;//置当前进程为可中断等待状态
schedule();//重新进程调度,重新调度后,当又轮到本程序进行时,则从下面的语句开始进行
if (!(current->signal &= ~(1<<(SIGCHLD-1))))//如果本程序还没有收到子进程发过来的SIGCHLD信号,则重新开始查找
goto repeat; else return -EINTR; } return -ECHILD; } //说明:LINE 190行的重新任务调度,是很有必要的,这样一任务调度,父进程就
//有可能暂时不运行,转让于子进程加以运行,这样就有可能子进程运行结束,
//当轮到父进程运行时,正好执行if (!(current->signal &= ~(1<<(SIGCHLD-1))))语句,
//也就是说能够收到子进程传过来的SIGCHLD信号,从而返回-EINTR(4),
//也就是等待到了子进程的结束
|