linux内核之进程管理模块sched.c

linux内核之进程管理模块sched.c

 一 功能描述
       1,sched.c 是内核中有关进程调度管理的程序,其中有关调度的基本函数(sleep_on() , wakeup() ,schedule() 函数等) ,其中比较重要的一个函数是schedule()函数,
             该函数负责选择系统中,下一个将要运行的进程,它首先对所有的任务进行选择,唤醒任何一个已经得到信号的任务。
             具体方法是针对任务数组中的每个任务,检查其报警定时值alarm。如果任务的alarm时间已经过期(alarm < jiffies), 则在它的信号位图中设置SIGALRM信号,
             然后清alarm的值,jiffies是系统是从开机开始算起的滴答数,如果进程的信号位图中除被阻塞的的信号外还有其他的信号,并且读进程处于可中断睡眠状态,
则置进程为就绪状态。
            随后是调度函数的核心处理部分,这部分代码根据进程的时间片和优先权调度机制,来选择随后要执行的任务。它首先循环检查任务数组中的所有任务,根据每个就绪态任务剩余执行时间的值counter,来选取该值最大的一个任务,并利用switch_to()函数切换到该任务。若所有就绪态任务该值都为0,表示此刻所有任务的时间片都
 已经运行完毕,于是就根据任务的优先权值priority,重置每个任务的运行时间片值counter,再重新执行循环检查所有任务的执行时间片值。

     sleep_on()函数的主要功能是当一个进程所请求的资源正忙或不在内存中时暂时切换出去,放在等待队列中等待一段时间, 当切换回来之后再继续运行,放入等待队列的方式利用了函数中的tmp指针作为各个正在等待任务的联系。
 以下是 内核模块中的 sched.c函数代码

#include  < linux / sched.h >
#include 
< linux / kernel.h >
#include 
< linux / sys.h >
#include 
< linux / fdreg.h >
#include 
< asm / system.h >
#include 
< asm / io.h >
#include 
< asm / segment.h >

#include 
< signal.h >

// 读宏取信号nr在信号位图中对应位的二进制数值,信号编号1-32.比如信号5的位图就是1<<(5-1),等于0010000b
#define  _S(nr) (1<<((nr)-1))

// 定义阻塞信号位图
#define  _BLOCKABLE(~(_S(SIGKILL) | _S(SIGSTOP)))

// 内核调试函数。显示任务号nr的进程号,进程状态,和内核堆栈空闲字节数
void  show_task( int  nr ,  struct  task_struct  *  p)
{
    
int i , j = 4096 - sizeof(struct task_struct) ;
    printk(
"%d: pid= %d , state=%d," , nr, p->pid , p->state) ;
    i 
= 0 ;
    
while(i < j && !((char *)(p + 1))[i]) //检测指定任务数据结构以后等于0的字节数
      i++ ;
    
    printk(
"%d(of%d) chars free in kernel stack \n\r" , i , j) ;

}


// 显示所有任务的任务号,进程号,进程状态和内核堆栈空闲字节数
void  show_stat( void )
{
   
int i ;
   
for(i = 0 ; i < NR_TASKS ; i++)
      
if(task[i])
          show_task(i , task[i]) ;
}



/**/ ///设置8253芯片初值
#define  LATCH(1193180/HZ)

extern   void  mem_use( void ) ;

extern   int  timer_interrupt( void ) ;

extern   int  system_call( void ) ;

// 每个任务在内核态运行时,都会有自己的内核态堆栈,这里定义了任务的内核态堆栈结构
union task_union {
  
struct task_struct_task ;  //因为一个任务的数据结构与其内核态堆栈结构在同一个内存页中
  char stack[PAGE_SIZE] ;    //所以从堆栈段寄存器ss可以获得其数据段选择符
}
 ;



static  union task_union init_task  =   {INIT_TASK , }  ;
long   volatile  jiffies  =   0  ;  // volatile 表示要从内存取值,因为CPU会把经常使用的变量放在
                            
// 通用寄存器中,但是若其他的程序修改这些变量之后,寄存器中的值,可能
                            
// 并不发生变化,这就造成了脏数据。 使用volatile 表示每次取值,都会从内存取值

long  start_time  =   0  ;
struct  task_struct  *  current  =   & (init_task.task) ;  // 当前任务指针,默认指向任务0
struct  task_struct  *  last_task_uesd_math  =  NULL ;   // 使用协处理器的任务指针
struct  task_struct  *  task[NR_TASKS]  =   {&(init_task.task) , }  ;  // 定义任务指针数组

long  user_stack[PAGE_SIZE  >>   2 ] ;
struct   {

   
long * a ;
   
short  b ;
  
}
 stack_start  =   {&user_stack[PAGE_SIZE >> 2] , 0x10}  ; 

void  math_state_restore()
{
  
//如果任务没变,则返回
  if(last_task_used_math == current)
    
return ;

  _asm_(
"fwait") ; 
  
if(last_task_used_math)
  
{
    _asm_(
"fnsave %0" :: "m"(last_task_used_math->tss.i387)) ;
  }


  last_task_used_math 
= current ;
  
if(current->uesd_math){  //已经使用过协处理器
     _asm_("frstor %0"::"m"(current->tss.i387)) ;
  }
 else{   //第一次使用协处理器,需要初始化相关信息
    _asm_("fninit"::) ;
    current
->used_math = 1 ;//设置已经使用过协处理器  
    
  }

   

}


void  schedule( void )
{
  
int i , next , c ;
  
struct task_struct **p ;//任务结构指针的指针
  for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
    
if(*p){
      
//如果设置国任务的定时值alarm,并且已经过期(alarm<jiffies),则在信号位图中置SIGRAM信号,
      
//即向任务发送SIGRAM信号。然后清alarm。 该信号的默认操作是终止进程。
      
//jiffies是从系统开始启动算起的滴答数。
      if((*p)->alarm && (*p)->alarm < jiffies)
       
{
          (
*p)->signal |= (1<<(SIGALRM - 1)) ;
          (
*P)->alarm = 0 ; 
       }

      
       
//如果信号位图中除被阻塞的信号外还有其他的信号,并且任务处于可中断状态,则置任务为就绪状态。
       if(((*p)->signal & ~(_BLOCKABLE&(*p)->blocked))&&(*p)->state == TASK_INTERRUPTIBLE)
         (
*p)->state - TASK_RUNNING ; //置为就绪状态

    }


     
//这是调度程序的主要部分
    while(1)
   
{
     c 
= -1 ;
     next 
= 0 ;
     i 
= NR_TASKS ;
     p 
= &task[NR_TASKS] ;  
     
//这段代码也是从任务数组的最后一个任务开始循环处理,并跳过不含任务的数组槽
     
//比较每个就绪状态任务的counter(任务运行时间的递减滴答数),哪个值最大,运行时间还不长,
     
//next就指向哪个任务号
     while(--i){
       
if(!*--p)
         
continue ;
       
if((*p)->state == TASK_RUNNING && (*p)->counter > c) 
          c 
= (*p)->counter, next = i ;
     }
 

     
//如果比较得出有counter值不等于0的结果,或者系统中没有一个可以运行的程序,那么就跳出最上层的
     
//while循环,执行任务切换程序
     if(c != 0 ) break ;
  
     
//全部的任务的时间片都已经使用完毕,那么就需要重新设置时间片值,重新执行。
     
// counter值最大的先执行
     for(p = &LAST_TASK ; p > &FIRST_TASK ;p++)  
       
if(*p)
         (
*p)->counter = ((*p)->counter >> 1+ (*p)->priority ;

   }



    switch_to(next) ;  
//切换到任务号为next的任务,并运行之

}



   
/**/ /*
     线程中断系统调用
   
*/

   
int  sys_pause( void )
   
{
      current
->state = TASK_INTERRUPTIBLE ;
      schedule() ;  
      
return 0 ;
   }

 
   
// 把任务变为不可中断的等待状态,并让睡眠队列的头指针指向当前的任务
   
// 只有明确的唤醒,才会返回,该函数提供了进程与中断处理程序之间的同步机制
   
// 函数参数P是等待任务队列的头指针,为了修改调用该函数程序中原来的指针变量的值,
   
// 就需要提供(*p)指针的指针。
  


   
void  sleep_on( struct  task_struct  ** p)    // 等待任务队列的头指针
   {
      
struct task_struct * tmp ;
      
if(!p)  
         
return ;
      
//如果进程0将要休眠,则死机 
      if(current == &(init_task.task))   
        panic(
"task[0] trying to sleep");
      
//让tmp指向已经在等待队列之上的任务,并且将等待队列头的等待指针指向当前任务
      
//这样就把当前任务插入到了*p的等待队列中。然后将当前任务变为不可中断的等待状态,并执行重新调度
       
      tmp 
= *p ;
      
*= current ;
      current
->state = TASK_UNINTERRUPTIBLE ;
      schedule() ;
      
      
if(tmp)        //若在其前还有等待任务,则将其变为就绪状态
        tmp->state = 0 ;
        


  }
 
  
// 将当前的任务置为可中断的等待状态,并放入*p指定的等待队列中
   void  interruptible_sleep_on( struct  task_struct  **  p)
  
{
     
struct task_struct * tmp ;
     
if(!p)
        
return ;
     
if(current == &(init_task.task)) 
        panic(
"task[0] trying to sleep") ;
     tmp 
= *p ;
     
*= current ;
 repeat:     
      current
->state = TASK_INTERRUPTIBLE ;
      schedule() ;
//执行调度程序,切换任务
      
//只有当这个等待程序被唤醒的时候,程序才会又回到这里执行,表示进程已被明确地唤醒并执行。 
      if(*&& *p!= current){
        (
**p).state = 0 ;
        
goto repeat ;
      }

      
*= NULL ;
      
if(tmp)
        tmp
->state = 0 ;
  }
    


  
/**/ /*唤醒等待的任务*/
  
void  wake_up( struct  task_struct  ** p)
  
{
    
if(p && *p)
    
{
       (
**p).state = 0 ;//置为就绪状态        
       *= NULL ;
    }


  }

   
   
// 下列进程数组存放的是软驱马达启动到正常转数的进程指针,数组索引0-3对应软驱A-D
    static   struct  task_struct   *  wait_motor[ 4 =   {NULL , NULL , NULL , NULL}  ;
   
// 每个马达启动所需要的时间数
    static   int  mon_timer[ 4 =   {0 , 0 , 0 ,0}  ;
   
// 记录每个马达停转之前维持的时间 
    static   int  moff_timer[ 4 =   {0,0,0,0}  ;
   unsigned 
char  current_DOR  =   0x0C  ; // 这里设置初值,允许DMA和中断请求。启动FDC
   
// 指定软驱启动到正常运转状态所需要的等待时间
    int  ticks_to_floppy_on(unsigned  int  nr)
   
{
     
extern unsigned char selected ;
     unsigned 
char mask = 0x10 << nr ;
     
if(nr > 3)
       panic(
"floppy_on : nr > 3") ; 
     moff_timer[nr] 
= 10000 ; 
     cli() ;
     mask 
|= current_DOR ;
     
if(!selected){
       mask 
&= 0xFC ;
       mask 
|= nr ;
     }

     

     
if(mask != current_DOR)
     
{
         outb(mask , FD_DOR) ;
         
if((mask ^ current_DOR) & 0xf0)
            mon_timer[nr] 
= HZ / 2 ;
         
else if(mon_timer[nr] < 2)
            mon_timer[nr]  
= 2 ; 

         current_DOR 
= mask ; 
     }

       
     sti() ;
     
return mon_timer[nr] ; 
 
  }


   
void  floppy_on(unsigned  int  nr)
   
{
       cli() ;  
       
while(ticks_to_floppy_on(nr))
         sleep_on(nr 
+ wait_motor) ;
       sti() ;
   }


  
void  floppy_off(unsigned  int  nr)
  
{
     moff_timer[nr] 
= 3 * HZ ;
  }
  
  

  
/**/ /*软盘定时处理子程序*/
  
/**/ /*更新马达启动定时值和马达关闭停转定时值*/
  
/**/ /*系统每当经过一个滴答就会被调用一次,随时更新马达开启或者停转的时间*/
  
void  do_floppy_timer( void )
  
{
     
int i ;
     unsigned 
char mask = 0x10 ;
     
for(i = 0 ; i < 4 ; i ++ , mask <<= 1)
     
{
        
if(!(mask & current_DOR))         
         
continue ;

        
if(mon_timer[i]){
           
if(!--mon_timer[i]) 
             wake_up(i 
+ wait_motor) ;
        }
 else if(!moff_timer[i])
         
{
           current_DOR 
&= ~mask ;
           outb(current_DOR , FD_DOR) ;
         }
 else
           moff_timer[i]
-- ;

     }


  }


#define  TIME_REQUEST 64
   
// 下面是定时器的代码。最多可以有64个计时器
   static   struct  timer_list   {
     
long jiffies ; //定时滴答数
     void(* fn)() ; //定时处理程序
     struct timer_list * next ; //链接指向下一个定时器
 }
  timer_list[TIME_REQUEST] ,  *  next_timer  =  NULL ;

/**/ /*添加一个新的定时器*/
 
void  add_timer( long  jiffies ,  void ( * fn)( void ))
 
{
   
struct timer_list * p ;
   
   
if(!fn)
     
return ;
   cli() ;
   
if(jiffies <= 0)//如果定时值为0,则立即执行处理程序,并且该定时器不加入链表
      (fn)() ;
  
  
//否则从定时器数组中,找出一个空的项
   else
     
for(p = timer_list ; p < timer_list + TIMER_REQUEST ; p++)  
      
if(!p->fn)  //找到一个空项,然后退出
        break ;

    
//如果已经用完了定时器数组,则系统崩溃,否则向定时器的数据结构中填入相应的信息,并连入链表头
        
     
if(p >= timer_list + TIME_REQUEST)
       panic(
"no more time request free");

     p
->fn = fn ;
     p
->jiffies = jiffies ;
     p
->next = next_timer ;
     next_timer 
= p ;
     
     
//下面的函数其实是一个 双向队列
     while(p->next && p->next->jiffies < p->jiffies){
       p
->jiffies -= p->next->jiffies ;
       fn 
= p->fn ;
       p
->fn = p->next->fn ;
       p
->next->fn = fn ;
       jiffies 
= p->jiffies ;
       p
->next->jiffies = jiffies ;
       p 
= p->next ;
     }

   }
 

 }


  
void  do_timer( long  cpl)
  
{
    
extern int beepcount ; //扬声器发生时间滴答数
    extern void sysbeepstop (void) ; //关闭扬声器
    if(beepcount)      
      
if(!--beppcount)
         sysbeepstop() ;
    
if(cpl)
      current
->utime++ ;
    
else
      current
->stime++ ;
    
if(next_timer){
      next_timer
->jiffies-- ;
      
while(next_timer && next_timer->jiffies <= 0){
        
void(*fn)(void) ;
        fn 
= next_timer->fn ;
        next_timer
->fn = NULL ;
        next_timer 
= next_timer->next ; 
        (fn)() ;
      }

    }



   
if(current_DOR & 0xf0)
     do_floppy_timer() ;
   
if((--current->counter) > 0)  return ;
   current
->counter = 0;
   
if(!cpl)  return ;
   schedule() ;

  }



  
// 系统调用功能,设置报警定时时间值
    int  sys_alarm( long  seconds)
   
{
     
int old = current->alarm ;
     
if(old)
       old 
= (old - jiffies) / HZ ;
     current
->alarm = (seconds > 0 ) ? (jiffies + HZ * seconds):0 ;
     
return old ;
   }


   
// 取当前的进程号pid

   
int  sys_getpid( void )
  
{

     
return current->pid ;
  }


   
// 取父进程号ppid
    int  sys_getppid( void )
 
{
    
return current->father ;
 }


  
// 取用户号
   int  sys_getuid( void )
  
{
    
return current->uid ;
  }

  
  
// 取有效的用户号euid
   int  sys_geteuid( void )
  
{
    
return current->euid ;
  }


  
// 取组号gid
   int  sys_getgid( void )
  
{
     
return current->gid ;
  }


  
// 取有效的组号
   int  sys_getegid( void )
  
{
    
return current->egid ;
  }

  
// 系统调用功能---降低对CPU的优先使用权
   int  sys_nice( long  increment)
  
{
    
if(current->priority - increment > 0)
       current
->priority -= increment ;
     
return 0 ;
 }

 
  
// 内核调度程序初始化

  
void  sched_init( void )
  
{
     
int  i ;
     
struct desc_struct * p ; //描述符表结构指针  
     set_tss_desc(gdt + FIRST_TSS_ENTRY , &(init_task.tss)) ;
     set_ldt_desc(gdt 
+ FIRST_LDT_ENTRY , &(init_task.task.ldt));
     p 
= gdt + 2 + FIRST_TSS_ENTRY ;
     
for(i = 1 ; i < NR_TASKS ;i++
     
{
       task[i] 
= NULL ;
       p
->= p->= 0 ;        
       p
++ ;
       p
->= p->= 0 ;
       p
++ ;
     }
 

     _asm_(
"pushfl ; andl $0xffffbfff , (%esp) ; popfl" ) ;
 
     ltr(
0) ;
     lldt(
0) ;

     outb_p(
0x36 , 0x43) ;
     outb_p(LATCH 
& 0xff , 0x40) ;
     outb(LATCH 
>> 8 , 0x40) ;

     set_intr_gate(
0x20 , &timer_interrupt) ;
     outb(inb_p(
0x21)&~0x01 , 0x21) ;
     set_system_gate(
0x80 , &system_call) ;

  }

 







你可能感兴趣的:(linux内核之进程管理模块sched.c)