这一期的内容比较轻松,主要是搞懂 exit.c 的代码,其中涉及到有关内存管理的函数也不深入,等到了 mm 模块再做分析,一些函数如果没有给出定义,那就是现在只需要知道它们的功能就好了。另外,下一篇文章将会阅读 fork.c,结合 sched.c,exit.c,fork.c 再回味一遍,就能构建起一个进程创建、调度、终止的框架
exit.c 主要实现了终止进程与进程退出的相关函数,即系统调用 exit 的处理流程。当调用 exit 函数时(参数为用户程序提供的退出码),实际调用 sys_exit。sys_exit 又调用 do_exit ,并保留退出码低字节,左移 8 位后当作 do_exit 的参数,空出来的这 8 位将用于保存 wait 函数的状态信息
1 2 3 4 5
| int sys_exit(int error_code) { return do_exit((error_code&0xff)<<8); }
|
do_exit 函数根据当前进程的特性对其进行处理,并把当前进程状态设置为僵死态,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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) (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); }
|
然后是 do_exit 中调用的一些函数的实现(按从上到下的顺序):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
static inline int send_sig(long sig,struct task_struct * p,int priv) { if (!p || sig<1 || sig>32) return -EINVAL; if (priv || (current->euid==p->euid) || suser()) p->signal |= (1<<(sig-1)); else return -EPERM; return 0; }
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); } }
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; task[i]->signal |= (1<<(SIGCHLD-1)); return; } printk("BAD BAD - no father found\n\r"); release(current); }
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"); }
|
exit.c 中还剩下两个函数,sys_kill 是系统调用 kill 的处理函数,用于向给定的 pid 对应的进程发送一个信号;sys_waitpid 是系统调用 waitpid 的处理函数,作用是挂起当前进程直到 pid 指定的子程序终止等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| int sys_kill(int pid,int sig) { struct task_struct **p = NR_TASKS + task; int err, retval = 0;
if (!pid) while (--p > &FIRST_TASK) { if (*p && (*p)->pgrp == current->pid) if (err=send_sig(sig,*p,1)) retval = err; } else if (pid>0) while (--p > &FIRST_TASK) { if (*p && (*p)->pid == pid) if (err=send_sig(sig,*p,0)) retval = err; } else if (pid == -1) while (--p > &FIRST_TASK) if (err = send_sig(sig,*p,0)) retval = err; else while (--p > &FIRST_TASK) if (*p && (*p)->pgrp == -pid) if (err = send_sig(sig,*p,0)) retval = err; return retval; }
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; }
|
我们平常在程序中调用的 wait 函数定义在 lib/wait.c 中,wait 实际就是通过调用 sys_waitpid 实现的:
1 2 3 4 5 6 7
| _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
pid_t wait(int * wait_stat) { return waitpid(-1,wait_stat,0); }
|