jake1036

linux0.11信号处理之 exit.c

                                                            exit.c 程序解析

 1 功能描述
   

       该程序主要的作用是终止和退出的有关事宜。主要包括进程释放、会话终止和程序退出处理函数以及杀死进程,终止进程,挂起进程调用函数。还包括进程信号发送函数,以及通知父进程子进程终止的函数tell_father()。
   释放进程的函数release() 主要根据制定的任务数据结构指针,在任务数组中删除指定的进程指针、释放内存页,并立刻让内核重新调度任务的运行。
 

   kill_session() 函数通过向会话号与当前进程相同的进程发送挂断进程的信号。

   sys_kill()用于向进程发送任何指定的信号。根据pid的不同参数值,该系统会向不同的进程发送任何指定的信号。

   do_exit() 函数是在exit系统调用的中断处理程序中被调用。它首先会释放当前进程的内存页面。如果当前进程有子进程,就将子进程的father置为1 ,即把子进程的父进程变为进程1(init进程)。如果该子进程已经处于僵死的状态,那么向进程1发送子进程终止信号SIGCHLD 。接着关闭当前进程打开的所有文件,释放试验的中断设备。
   协处理器设备,若当前进程是进程组的领头进程,则还需要终止所有相关进程。 随后把当前进程置位僵死状态,设置退出码,并向父进程发送子进程终止信号SIGCHLD。最后让内核重新调度任务运行。

2  代码示例
  
#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(!|| sig < 1 || sig > 32)
       
return -EINVAL ;
    
if(priv || (current->euid == p->euid)|| suser())  //euid表示当前进程的权限
      p->signal |= (1<<(sig - 1)) ;
    
else
      
return -EPERM ;
    
return 0 ;
 }


 
//终止会话session
 static void kill_session(void)
 
{
   
struct task_struct **= NR_TASKS + task ;  //*p首先指向最后一个任务
   
//扫描任务指针数组,如果所有的任务的会话号等于当前进程的会话号,那么就向它发送终止信号
   for(;p >= &FIRST_TASK ; p--
    
{
       
if(*&& (*p)->session == current->session)
         (
*p)->signal |= 1 <<(SIGHUP - 1) ;
    }



 }

  
//向进程组发送信号
//这个函数用来向任何进程发送信号
int sys_kill(int pid , int sig)
{
  
struct task_struct **= NR_TASKS + task ;
  
int err , retval = 0 ;
  
if(!pid) while(--> &FIRST_TASK){   //如果当前进程pid==0。那么就会把信号发送给与当前进程在同一组中的进程
    if(*&& (*p)->pgrp == current->pid)  
      
if(err = send_sig(sig , *p , 1))  //强制发送
         retval = err ; 
  }
 else if(pid > 0while(--> &FIRST_TASK){
     
if(*&& (*p)->pid == current->pid)  
      
if(err = send_sig(sig , *p , 0))  //强制发送
         retval = err ; 
   }

 
else if(pid == -1while(--> &FIRST_TASK){  
      
if(err = send_sig(sig , *p , 0))  //强制发送
         retval = err ; 
   }
 
 
else while(--> &FIRST_TASK)  
    
if(*&& (*p)->pgrp == -pid)
       
if(err = send_sig(sig , *p , 0))  //强制发送
         retval = err ; 
}

  
 
//通知父进程--向进程pid发送信号SIGCHLD ;默认情况下子进程将停止或者终止
 
//如果没有找到父进程,则自己释放。

 
static void tell_father(int pid)
 
{
   
int i ;
   
if(pid)
     
//扫描进程数组表寻找指定进程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\b\r") ;
      release(current) ;
 }

//程序退出处理函数。在下面的sys_exit()函数中被调用
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)) ; 
  
//如果当前进程有子进程,将子进程的father置为1。
  
//若该子进程已经处于僵死状态,则向进程1发送子进程终止信号SIGCHLD
  
//如果该子进程已经处于僵死状态,则向进程1发送子进程终止信号
  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) ;
   
//对当前进程的工作目录pwd,跟目录root以及执行文件的i节点进行同步操作,放回各个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 ;
    
//如果当前进程上次使用过协处理器,则将last_task_used_math 置空
    if(last_task_used_math == current)
      last_task_used_math 
= NULL ;
    
//如果当前进程是leader进程,则终止该会话的所有相关进程
    if(current->leader)
      kill_session() ;

    
//把当前进程的状态变为僵死状态,表明当前进程已经释放了资源。并保存由父进程读取的退出码
    current->state = TASK_ZOMBIE ;
    current
->exit_code = code ;
    tell_father(current
->father) ;//通知父进程,子进程将结束
    schedule() ; //重新调度进程运行
    return (-1) ;
}
 
 

 
//系统调用exit()。终止进程
  int sys_exit(int error_code)
 
{
   
return do_exit((error_code & 0xff<< 8) ;   
 }

  

  
//挂起当前进程,等待pid指定的子进程退出或者收到终止该进程的信号,或者是需要一个信号句柄
 int sys_waitpid(pid_t pid , unsigned long * stat_addr , int options)
 

    
int flag , code ;  //flag标志作用于后面表示所选出的子进程处于就绪或者睡眠状态
    struct task_struct ** p;
    verify_area(stat_addr , 
4) ;
repeat:
    flag 
= 0 ;
    
//从任务数组末端开始扫描所有的任务,跳过空项,本进程项以及非当前进程的子进程项
    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
     
{  
       
if(!*|| *== current)  //跳过空项以及当前进程项
         continue ;
       
if((*p)->father != current->pid)  //跳过非当前进程的子进程项
         continue ;

       
//此时选择到的进程一定是当前进程的子进程
       
//如果当前的pid>0,但是不等于参数pid
       
//就说明是当前进程其他的子进程
       if(pid > 0)
       
{
          
if((*p)->pid != pid) 
          
{
             
continue ; 
          }

        }
 else if(!pid){  //如果pid==0,则表示正在等待组号等于当前进程的所有进程
          if((*p)->pgrp != current->pgrp)
            
continue ; 
        }
else  if(pid != -1)//如果pid<-1
          if((*p)->pgrp != -pid)
            
continue ;
        }


       
//如果前3个对pid的判断不符合标准,则表示当前进程正在等待其他任何子进程,即pid=-1的情况
       
//接下来根据子进程的状态来处理
       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 ;

 }



posted on 2010-11-07 15:43 kahn 阅读(1067) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理