您的位置:首页 > 运维架构 > Linux

linux0.11源码研究---exit.c

2017-03-07 15:45 155 查看
exit.c程序中实现的大致实现的是进程终止和退出等方面如何处理的相关函数,包含
kill()
函数系统调用的内部实现、
exit()
函数系统调用的内部实现、
waitpid()
函数的系统调用的实现,以及一些更底层的函数的实现。

要想看懂这些函数,需要对会话以及进程、进程组有一定的了解,并且对pcb里面的一些成员的了解。

主要包括有这八个函数:

void release(struct task_struct * p)    //用来释放指定进程占用的内存以及删除其在任务数组的指针,并让内核重新调度。
static inline int send_sig(long sig, struct task_struct * p, int priv)  //向指定进程发送信号,权限为priv
static void kill_session(void)  //终止会话
int sys_kill(int pid, int sig)  //向指定进程或进程组发送指定的进程组
static void tell_father(int pid)    //子进程通知父进程(即向进程pid发送信号SIGCHLD)
int do_exit(long code)  //程序退出处理程序
int sys_exit(int error_code)    //系统调用exit(),终止进程
int sys_waitpid(pid_t pid, unsigned long * stat_addr, int options)  //系统调用waitpid(),回收指定的子进程。


1.
void release(struct task_struct * p)


参数:p代表要回收的任务数据结构中的指针

void release(struct task_struct * p)

{

int i;

if (!p)     //如果p为NULL,直接返回
return;
for (i=1 ; i<NR_TASKS ; i++)    //NR_TASKS代表进程并发的最大数值,即当前最多有多少个进程
if (task[i]==p) {           //找到指定的任务
task[i]=NULL;           //将该项置为空
free_page((long)p);     //释放相关内存页
schedule();             //重新进行调度
return;
}
panic("trying to release non-existent task");   //指定任务不存在则死机
}


2.
static inline int send_sig(long sig, struct task_struct * p, int priv)


参数:

sig代表发送的信号

p代表要发送的任务数据结构的指针

priv是否强制发送信号,无视特权级

static inline int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32)  //p为NULL或者信号非法则返回
return -EINVAL;
if (priv || (current->euid==p->euid) || suser())    //如果强制发送标志置位或者当前进程的有效用户id等于指定进程的有效用户id或者当前进程是超级用户,则在进程位图中添加该信号,否则出错退出
p->signal |= (1<<(sig-1));      //或操作将sig加入位图中
else
return -EPERM;
return 0;
}


3.
static void kill_session(void)


static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;   //*p先指向任务数组的末端

while (--p > &FIRST_TASK) {     //遍历任务数组并且跳过任务0
if (*p && (*p)->session == current->session)    //找到和当前进程在一个会话组中的其它进程
(*p)->signal |= 1<<(SIGHUP-1);      //发送sighup挂断进程
}
}


4.
int sys_kill(int pid,int sig)


参数:

pid:pid>0,代表要发送信号的进程号

pid=0,信号会被发送给当前进程的进程组中所有的进程

pid=-1,信号会发送给除了第一个进程的其它所有进程(只要有权限)

pid<-1,信号将发送给进程组为-sig内的所有进程

sig:要发送的信号

int sys_kill(int pid,int sig)
{
struct task_struct **p = NR_TASKS + task;   //*p指向任务数组末端
int err, retval = 0;

if (!pid)       //如果pid为0,即发送信号sig给当前进程组的所有进程
while (--p > &FIRST_TASK) {         //遍历任务数组,找到该进程组中所有进程
if (*p && (*p)->pgrp == current->pid)
if (err=send_sig(sig,*p,1)) //调用send_sig发送信号,启用priv,表示强制发送
retval = err;
} else if (pid>0) while (--p > &FIRST_TASK) {   //如果pid>0,即向指定进程发送信号
if (*p && (*p)->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)  //最后一种情况就是发送给gid为-pid的进程组的所有进程
if (*p && (*p)->pgrp == -pid)
if (err = send_sig(sig,*p,0))   //未开启强制发送
retval = err;
return retval;
}


5.
static void tell_father(int pid)


参数: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;
task[i]->signal |= (1<<(SIGCHLD-1));    //向指定的进程的信号位图中加入SIGCHLD
return;
}
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
//如果没找到该父进程,则打印输出并调用release函数自己释放资源
printk("BAD BAD - no father found\n\r");
release(current);
}


6.
int do_exit(long code)


参数:code代表退出码

int do_exit(long code)
{
int i;

free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));    //释放当前进程的代码段所占的内存页(ldt是局部描述符,在另外一篇博文中有介绍)
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;        //将该子进程的父进程设置为init进程(1号进程
if (task[i]->state == TASK_ZOMBIE)  //如果该子进程的状态为僵尸进程
/* assumption task[1] is always init */
(void) send_sig(SIGCHLD, task[1], 1);   //则向进程1发送子进程终止信号SIGCHLD
}
for (i=0 ; i<NR_OPEN ; i++)     //关闭当前进程打开着的所有文件
if (current->filp[i])
sys_close(i);
//将当前进程的工作目录pwd、根目录root以及程序的可执行文件节点进行同步,并全部释放
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 置空
last_task_used_math = NULL;
if (current->leader)    //如果当前进程是leader进程,则终止该会话组中全部进程
kill_session();
current->state = TASK_ZOMBIE;   //将当前进程的状态设置为僵尸状态
current->exit_code = code;      //并将退出码设置好
tell_father(current->father);   //通知当前进程的父进程(即发送SIGCHLD信号给父进程)
schedule();                     //重新调度
return (-1);    /* just to suppress warnings */
}


7.
int sys_exit(int error_code)


参数:error_code代表退出码

int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}


8.
int sys_waitpid(pid_t pid, unsigned long * stat_addr, int options)


参数:

pid:

pid>0,表示等待当前进程的进程号等于pid的子进程

pid=0,表示等待当前进程所在的进程组的全部子进程

pid=-1,表示等待当前进程的任何子进程

pid<-1,表示等待进程组id等于-pid内的所有的子进程

stat_addr:传出参数,存储退出的状态信息

options:值为WUNTRACED时,表示如果子进程是停止的,也马上返回(无须跟踪);值为WNOHANG,表示如果没有子进程处于退出或者终止状态则马上返回,不阻塞等待

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;
//这里得到的进程p肯定是当前进程的子进程

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) {
//子进程 p 处于停止状态时,如果此时 WUNTRACED 标志没有置位,表示程序无须立刻返回,于是继续扫描处理其它进程。如果 WUNTRACED 置位,则把状态信息 0x7f 放入*stat_addr,并立刻返回子进程号 pid。这里 0x7f 表示的返回状态使 WIFSTOPPED()宏为真。
case TASK_STOPPED:
if (!(options & WUNTRACED))
continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
//如果子进程 p 处于僵死状态,则首先把它在用户态和内核态运行的时间分别累计到当前进程(父进程)中,然后取出子进程的 pid 和退出码,并释放该子进程。最后返回子进程的退出码和 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;
//如果这个子进程 p 的状态既不是停止也不是僵死,那么就置 flag=1。
default:
flag=1;
continue;
}
}
//如果flag被置位,就代表有符合要求的子进程,但是没有处于退出态或者僵死态
if (flag) {
if (options & WNOHANG)  //如果设置了不阻塞,则退出
return 0;
current->state=TASK_INTERRUPTIBLE;  //置当前进程为可中断等待状态
schedule();     //调度其它进程
if (!(current->signal &= ~(1<<(SIGCHLD-1))))   //当又开始执行本进程时,如果本进程没有收到除 SIGCHLD 以外的信号,则还是重复处理。否则,返回出错码并退出。
goto repeat;
else
return -EINTR;
}
//若没有找到符合要求的子进程,则返回错误码
return -ECHILD;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: