linux内核分析之exit.c

#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++)
  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)
{
  先判断给定进程指针是否为空,信号是否越界
 if (!p || sig<1 || sig>32)
  return -EINVAL;
 更新指定进程的信号位图:
 1. 用户是超级用户
 2. 当前进程的有效用户id和指定进程的有效用户id相同
 3. 如果有特殊特权级,priv != 0
 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);
 }
}

向指定进程发送信号
int sys_kill(int pid,int sig)
{
 struct task_struct **p = NR_TASKS + task;
 int err, retval = 0;

  如果进程id为0,从最后一个进程槽开始遍历进程数组,表示把信号发送给与当前进程同组的所有进程中去
 if (!pid) while (--p > &FIRST_TASK) {
   如果当前进程id和某进程的进程组id相等,则向这个进程发送该信号
  if (*p && (*p)->pgrp == current->pid)
   if (err=send_sig(sig,*p,1))
    retval = err;
 }
 如果进程id大于0,从最后一个进程槽开始遍历数组
 else if (pid>0) while (--p > &FIRST_TASK) {
   如果进程id和遍历中的某个进程id相等,则把该信号发送给这个进程
  if (*p && (*p)->pid == pid)
   if (err=send_sig(sig,*p,0))
    retval = err; 
 }
 如果进程id为-1,表示向所有进程发送该信号,除了进程0.
 else if (pid == -1) while (--p > &FIRST_TASK)
  if (err = send_sig(sig,*p,0))
   retval = err;
  如果进程id小于0且不等于0,则将信号发送到同pid绝对值相等的进程组的所有进程。
 else while (--p > &FIRST_TASK)
  if (*p && (*p)->pgrp == -pid)
   if (err = send_sig(sig,*p,0))
    retval = err;
 return retval;
}
发送给父进程信号的函数,SIGCHILD
static void tell_father(int pid)
{
 int i;
  如果pid有效,即大于0
 if (pid)
   遍历任务数组,向与pid相同的进程递送SIGCHILD信号
  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);
}

被系统调用函数调用
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));
 遍历任务数组,查找当前任务的子任务,如果该任务的状态是僵死,那么将向进程1发送SIGCHILD信号。
 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);
 将当前进程运行目录归还给i节点
 iput(current->pwd);
 current->pwd=NULL;
 将根目录归还给i节点
 iput(current->root);
 current->root=NULL;
 将执行文件归还给i节点
 iput(current->executable);
 current->executable=NULL;
 如果当前进程是会话期的首进程,并且有终端和其相连,释放终端
 if (current->leader && current->tty >= 0)
   将与终端联系的进程组设置为0,表示回收
  tty_table[current->tty].pgrp = 0;
 如果该进程上次使用了协处理器,将协处理器指针置空
 if (last_task_used_math == current)
  last_task_used_math = NULL;
 如果当前进程是首进程,需要向该会话期所有进程发送SIGHUP信号
 if (current->leader)
  kill_session();
 将当前进程状态置为僵死,表示等着父进程回收退出状态
 current->state = TASK_ZOMBIE;
 current->exit_code = code;
 向当前进程的父进程发送SIGCHILD信号
 tell_father(current->father);
 进行任务调度
 schedule();
 不会到达此处,因为该进程的状态在调度之前已经置为TASK_ZOMBIE。
 return (-1); 
}

退出进程的系统调用
int sys_exit(int error_code)
{
 return do_exit((error_code&0xff)<<8);
}

取回子进程的退出状态
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;
  如果pid大于0,并且该进程不是pid所制定的进程,不处理进程id为pid的进程
  if (pid>0) {
   if ((*p)->pid != pid)
    continue;
  }
  如果pid为0,不处理和当前进程组同一组的进程
  else if (!pid) {
   if ((*p)->pgrp != current->pgrp)
    continue;
  }
  如果pid小于0但是不为-1.不处理同pid绝对值同一组的进程
  else if (pid != -1) {
   if ((*p)->pgrp != -pid)
    continue;
  }
  
  到了这个地方,要处理的进程为:
  1. 当前进程的子进程,该子进程的id等于pid
  2. 当前进程的子进程,且该子进程和当前进程不在同一组中
  3. 当前进程的子进程,且该子进程同pid的绝对值所指代的进程在不同组中
  switch ((*p)->state) {
    如果该子进程为停止状态,且有置WUNTRACED位,将0x7f作为状态返回给父进程。
   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;
  }
 }
 如果该子进程不是僵死状态,也不是停滞状态,或者是停滞状态但是没有设置WUNTRACED位,需要进一步处理
 if (flag) {
   如果设置了WNOHANG位,立即返回,不需要阻塞
  if (options & WNOHANG)
   return 0;
  否则,父进程阻塞在这里,并且被处理器重新调度
  current->state=TASK_INTERRUPTIBLE;
  schedule();
  下次被调度的时候,如果除SIGCHILD之外,没有其他信号需要处理,就继续从头开始执行,否则
  被信号中断,返回EINTR错误
  if (!(current->signal &= ~(1<<(SIGCHLD-1))))
   goto repeat;
  else
   return -EINTR;
 }
 如果在这里返回,表示没有找到子进程,子进程不存在。
 return -ECHILD;
}


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值