一 功能描述
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的進程號,進程狀態,和內核堆??臻e字節數
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) ;

}

//顯示所有任務的任務號,進程號,進程狀態和內核堆??臻e字節數
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 ;
*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 ;
*p = current ;
repeat:
current->state = TASK_INTERRUPTIBLE ;
schedule() ;//執行調度程序,切換任務
//只有當這個等待程序被喚醒的時候,程序才會又回到這里執行,表示進程已被明確地喚醒并執行。

if(*p && *p!= current)
{
(**p).state = 0 ;
goto repeat ;
}
*p = NULL ;
if(tmp)
tmp->state = 0 ;
}



/**//*喚醒等待的任務*/
void wake_up(struct task_struct **p)

{
if(p && *p)

{
(**p).state = 0 ;//置為就緒狀態
*p = 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->a = p->b = 0 ;
p++ ;
p->a = p->b = 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) ;

}

