jake1036

linux0.11---fork.c函数分析

1 功能描述 
  fork()系统调用用于创建子进程,Linux中所有的进程都是进程0的子进程。

    copy_process()函数用于创建并复制进程代码段和数据段以及环境,在进程复制的过程中,工作主要牵涉到进程数据结构中信息的设置。
    系统首先为新建进程在主内存区申请一页内存来存放任务数据结构信息,并复制当前进程任务数据结构中的所有内容作为新进程任务数据结构的模板。
    随后对已经复制的任务数据结构内容进行修改。把当前进程设置为新进程的父进程,清除信号位图并复位新进程的各个统计值。接着根据当前进程环境设置新进程任务状态段TSS中各个寄存器的值,新建进程内核态堆栈指针tss.esp0被设置成为任务数据结构所在内存页面的顶端,而tss.ss0被设置为内核数据段选择符。
  每当任务进入内核态运行的时候,其内核态堆栈指针初始位置不变,均为任务数据所在页面的顶端。

 

 


  2源代码分析

   
 #include <errno.h>
#include 
<linux/sched.h>
#include 
<linux/kernel.h>
#include 
<asm/segment.h>
#include 
<asm/system.h>

extern void write_verify(unsigned long address) ;
long last_pid = 0//最新进程号,其值会由get_empty_process生成
void verify_area(void * addr ,  int size)
{
   unsigned 
long start ;
   start 
= (unsigned long) addr ;
   size 
+= start & 0xfff ;
   start 
&= 0xfffff000 ;
   start 
+= get_base(current->ldt[2]) ;
   
while(size > 0){
     size 
-= 4096 ;  
     write_verify(start) ;
     start 
+= 4096 ;
   }

}


//该函数为新的进程在线性地址空间中设置新的代码段和数据段基地址和限长
int copy_mem(int nr , struct task_struct * p)
{
   unsigned 
long old_data_base , new_data_base , data_limit ;
   unsigned 
long old_code_base , new_code_base , code_limit ;
   code_limit 
= get_limit(0x0f) ;
   data_limit 
= get_limit(0x17) ;
   old_code_base 
= get_base(current->ldt[1]) ;
   old_data_base 
= get_base(current->ldt[2]) ;
   
if(old_code_base != old_data_base)
     panic(
"We donot support seperate ID") ;
   
if(data_limit < code_limit) 
     panic(
"Bad data_limit") ;

   new_data_base 
= new_code_base = nr * 0x4000000 ; //nr * 64MB
   p->start_code = new_code_base ;
   set_base(p
->ldt[1] , new_code_base) ;
   set_base(p
->ldt[2] , new_data_base) ;
   
if(copy_page_tables(old_data_base , new_data_base , data_limit))
    
{
      free_page_tables(new_data_base , data_limit) ;
      
return -ENOMEM ;

    }

    
return 0 ;   
}


//下面是主要的fork程序,负责复制系统进程信息
//并且设置必要的寄存器,还整个地复制数据段

int copy_process(int nr , long ebp , long edi , long esi , long gs , long none ,
 
long ebx , long ecx , long edx , long fs , long es , long ds , long eip ,
long cs , long eflags , long esp , long ss)
{
  
struct task_struct * p ;
  
int i ;
  
struct file * f ;
  p 
= (struct task_struct *) get_free_page() ;
  
if(!p)
    
return -EAGAIN ;
  task[nr] 
= p ;       
  
*= * current ; //并不是指针的赋值 ,而是直接申请了一个空间 
  
//下面开始修改任务数据结构的值
  p->state = TASK_UNINTERRUPTIBLE ; 
  p
->pid = last_pid ;
  p
->father = current->pid ;
  p
->counter = p->priority ;//设置时间片
  p->signal = 0 ;   //信号位图置0
  p->alarm = 0 ; //报警定时器值
  p->leader = 0 ;//进程的领导权
  p->utime = p->stime = 0 ; //用户态号和心态的运行时间
  p->cutime = p->cstime = 0 ;//子进程用户态和和核心态的运行时间  
  p->start_time = jiffies ; //进程当前的运行时间
  

  p
->tss.back_link = 0  ;
  p
->tss.esp0 = PAGE_SIZE + (long) p ; //任务内核态栈指针
  p->tss.ss0 = 0x10 ;       //内核态的段选择符,与数据段选择符相同
  p->tss.eip = eip  ;
  p
->tss.eflags = eflags ; 
  p
->tss.eax = 0 ; //这是当fork()调用返回时 新进程会返回0的原因
  p->tss.ecx = ecx ;
  p
->tss.edx = edx ;
  p
->tss.ebx = ebx ; 
  p
->tss.esp = esp ;
  p
->tss.ebp = ebp ;
  p
->tss.esi = esi ;
  p
->tss.edi = edi ;
  p
->tss.es = es & 0xffff ;
  p
->tss.cs = cs & 0xffff ;
  p
->tss.ss = ss & 0xffff ;
  p
->tss.ds = ds & 0xffff ;
  p
->tss.fs = fs & 0xffff ;
  p
->tss.gs = gs & 0xffff ;
  p
->tss.ldt = _LDT(nr) ;  
  p
->tss.trace_bitmap = 0x80000000 ;
  

  
if(last_task_used_math == current)
   __asm__(
"clts ; fnsave %0" ::"m"(p->tss.i387)) ;
  
//接下来复制进程页表,即在线性地址空间设置新任务代码段和数据段描述符中的基地址和限长,并复制页表。  
  if(copy_mem(nr , p)){
    task[nr] 
= NULL ;
    free_page((
long)p) ;
    
return -EAGAIN ;
  }


  
//如果父进程中有些文件是打开的,则将对应文件的打开次数加1 ,因为子进程会共享父进程打开的这些文件
  for(i = 0 ; i < NR_OPEN ; i++)  
    
if(f = p->filp[i])
     f
->f_count++ ;
  
if(current->pwd)
     current
->pwd->i_count++ ;
  
if(current->root)
    current
->root->i_count++ ;
  
if(current->executable)
    current
->executable->i_count++ ;

  
//最后在GDT表中设置新任务TSS段和LDT段描述符
  set_tss_desc(gdt + (nr<<1+ FIRST_TSS_ENTRY , &(p->tss)) ;
  set_ldt_desc(gdt 
+ (nr<<1+ FIRST_LDT_ENTRY , &(p->ldt)) ;
  p
->state = TASK_RUNNING ;  
   
  
return last_pid ;
}


//为新进程取得不重复的进程号last_pid ,函数返回在任务数组中的任务号
 int find_empty_process(void)
{
  
int i ; 
  repeat:
    
if((++last_pid) < 0) last_pid = 1 ;
    
for(i = 0 ; i < NR_TASKS ; i++)
       
if(task[i] && task[i]->pid == last_pid)
           
goto repeat ;

    
for(i = 1 ; i < NR_TASKS ; i++)  
       
if(!task[i])
          
return i ;

    
return -EAGAIN ; 
}










  

posted on 2010-11-08 19:48 kahn 阅读(1000) 评论(0)  编辑 收藏 引用


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