一 功能描述
1,sched.c 是內(nèi)核中有關(guān)進程調(diào)度管理的程序,其中有關(guān)調(diào)度的基本函數(shù)(sleep_on() , wakeup() ,schedule() 函數(shù)等) ,其中比較重要的一個函數(shù)是schedule()函數(shù),
該函數(shù)負責選擇系統(tǒng)中,下一個將要運行的進程,它首先對所有的任務進行選擇,喚醒任何一個已經(jīng)得到信號的任務。
具體方法是針對任務數(shù)組中的每個任務,檢查其報警定時值alarm。如果任務的alarm時間已經(jīng)過期(alarm < jiffies), 則在它的信號位圖中設(shè)置SIGALRM信號,
然后清alarm的值,jiffies是系統(tǒng)是從開機開始算起的滴答數(shù),如果進程的信號位圖中除被阻塞的的信號外還有其他的信號,并且讀進程處于可中斷睡眠狀態(tài),
則置進程為就緒狀態(tài)。
隨后是調(diào)度函數(shù)的核心處理部分,這部分代碼根據(jù)進程的時間片和優(yōu)先權(quán)調(diào)度機制,來選擇隨后要執(zhí)行的任務。它首先循環(huán)檢查任務數(shù)組中的所有任務,根據(jù)每個就緒態(tài)任務剩余執(zhí)行時間的值counter,來選取該值最大的一個任務,并利用switch_to()函數(shù)切換到該任務。若所有就緒態(tài)任務該值都為0,表示此刻所有任務的時間片都
已經(jīng)運行完畢,于是就根據(jù)任務的優(yōu)先權(quán)值priority,重置每個任務的運行時間片值counter,再重新執(zhí)行循環(huán)檢查所有任務的執(zhí)行時間片值。
sleep_on()函數(shù)的主要功能是當一個進程所請求的資源正忙或不在內(nèi)存中時暫時切換出去,放在等待隊列中等待一段時間, 當切換回來之后再繼續(xù)運行,放入等待隊列的方式利用了函數(shù)中的tmp指針作為各個正在等待任務的聯(lián)系。
以下是 內(nèi)核模塊中的 sched.c函數(shù)代碼
#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在信號位圖中對應位的二進制數(shù)值,信號編號1-32.比如信號5的位圖就是1<<(5-1),等于0010000b
#define _S(nr) (1<<((nr)-1))

//定義阻塞信號位圖
#define _BLOCKABLE(~(_S(SIGKILL) | _S(SIGSTOP)))

//內(nèi)核調(diào)試函數(shù)。顯示任務號nr的進程號,進程狀態(tài),和內(nèi)核堆棧空閑字節(jié)數(shù)
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]) //檢測指定任務數(shù)據(jù)結(jié)構(gòu)以后等于0的字節(jié)數(shù)
i++ ;
printk("%d(of%d) chars free in kernel stack \n\r" , i , j) ;

}

//顯示所有任務的任務號,進程號,進程狀態(tài)和內(nèi)核堆棧空閑字節(jié)數(shù)
void show_stat(void)


{
int i ;
for(i = 0 ; i < NR_TASKS ; i++)
if(task[i])
show_task(i , task[i]) ;
}



/**////設(shè)置8253芯片初值
#define LATCH(1193180/HZ)

extern void mem_use(void) ;

extern int timer_interrupt(void) ;

extern int system_call(void) ;

//每個任務在內(nèi)核態(tài)運行時,都會有自己的內(nèi)核態(tài)堆棧,這里定義了任務的內(nèi)核態(tài)堆棧結(jié)構(gòu)

union task_union
{
struct task_struct_task ; //因為一個任務的數(shù)據(jù)結(jié)構(gòu)與其內(nèi)核態(tài)堆棧結(jié)構(gòu)在同一個內(nèi)存頁中
char stack[PAGE_SIZE] ; //所以從堆棧段寄存器ss可以獲得其數(shù)據(jù)段選擇符
} ;




static union task_union init_task =
{INIT_TASK , } ;
long volatile jiffies = 0 ; //volatile 表示要從內(nèi)存取值,因為CPU會把經(jīng)常使用的變量放在
//通用寄存器中,但是若其他的程序修改這些變量之后,寄存器中的值,可能
//并不發(fā)生變化,這就造成了臟數(shù)據(jù)。 使用volatile 表示每次取值,都會從內(nèi)存取值

long start_time = 0 ;
struct task_struct * current = &(init_task.task) ; //當前任務指針,默認指向任務0
struct task_struct * last_task_uesd_math = NULL ; //使用協(xié)處理器的任務指針

struct task_struct * task[NR_TASKS] =
{&(init_task.task) , } ; //定義任務指針數(shù)組

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)
{ //已經(jīng)使用過協(xié)處理器
_asm_("frstor %0"::"m"(current->tss.i387)) ;

} else
{ //第一次使用協(xié)處理器,需要初始化相關(guān)信息
_asm_("fninit"::) ;
current->used_math = 1 ;//設(shè)置已經(jīng)使用過協(xié)處理器
}

}

void schedule(void)


{
int i , next , c ;
struct task_struct **p ;//任務結(jié)構(gòu)指針的指針
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)

if(*p)
{
//如果設(shè)置國任務的定時值alarm,并且已經(jīng)過期(alarm<jiffies),則在信號位圖中置SIGRAM信號,
//即向任務發(fā)送SIGRAM信號。然后清alarm。 該信號的默認操作是終止進程。
//jiffies是從系統(tǒng)開始啟動算起的滴答數(shù)。
if((*p)->alarm && (*p)->alarm < jiffies)

{
(*p)->signal |= (1<<(SIGALRM - 1)) ;
(*P)->alarm = 0 ;
}
//如果信號位圖中除被阻塞的信號外還有其他的信號,并且任務處于可中斷狀態(tài),則置任務為就緒狀態(tài)。
if(((*p)->signal & ~(_BLOCKABLE&(*p)->blocked))&&(*p)->state == TASK_INTERRUPTIBLE)
(*p)->state - TASK_RUNNING ; //置為就緒狀態(tài)

}

//這是調(diào)度程序的主要部分
while(1)

{
c = -1 ;
next = 0 ;
i = NR_TASKS ;
p = &task[NR_TASKS] ;
//這段代碼也是從任務數(shù)組的最后一個任務開始循環(huán)處理,并跳過不含任務的數(shù)組槽
//比較每個就緒狀態(tài)任務的counter(任務運行時間的遞減滴答數(shù)),哪個值最大,運行時間還不長,
//next就指向哪個任務號

while(--i)
{
if(!*--p)
continue ;
if((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i ;
}

//如果比較得出有counter值不等于0的結(jié)果,或者系統(tǒng)中沒有一個可以運行的程序,那么就跳出最上層的
//while循環(huán),執(zhí)行任務切換程序
if(c != 0 ) break ;
//全部的任務的時間片都已經(jīng)使用完畢,那么就需要重新設(shè)置時間片值,重新執(zhí)行。
// counter值最大的先執(zhí)行
for(p = &LAST_TASK ; p > &FIRST_TASK ;p++)
if(*p)
(*p)->counter = ((*p)->counter >> 1) + (*p)->priority ;

}


switch_to(next) ; //切換到任務號為next的任務,并運行之

}



/**//*
線程中斷系統(tǒng)調(diào)用
*/
int sys_pause(void)

{
current->state = TASK_INTERRUPTIBLE ;
schedule() ;
return 0 ;
}
//把任務變?yōu)椴豢芍袛嗟牡却隣顟B(tài),并讓睡眠隊列的頭指針指向當前的任務
//只有明確的喚醒,才會返回,該函數(shù)提供了進程與中斷處理程序之間的同步機制
//函數(shù)參數(shù)P是等待任務隊列的頭指針,為了修改調(diào)用該函數(shù)程序中原來的指針變量的值,
//就需要提供(*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指向已經(jīng)在等待隊列之上的任務,并且將等待隊列頭的等待指針指向當前任務
//這樣就把當前任務插入到了*p的等待隊列中。然后將當前任務變?yōu)椴豢芍袛嗟牡却隣顟B(tài),并執(zhí)行重新調(diào)度
tmp = *p ;
*p = current ;
current->state = TASK_UNINTERRUPTIBLE ;
schedule() ;
if(tmp) //若在其前還有等待任務,則將其變?yōu)榫途w狀態(tài)
tmp->state = 0 ;


}
//將當前的任務置為可中斷的等待狀態(tài),并放入*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() ;//執(zhí)行調(diào)度程序,切換任務
//只有當這個等待程序被喚醒的時候,程序才會又回到這里執(zhí)行,表示進程已被明確地喚醒并執(zhí)行。

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 ;//置為就緒狀態(tài)
*p = NULL ;
}

}
//下列進程數(shù)組存放的是軟驅(qū)馬達啟動到正常轉(zhuǎn)數(shù)的進程指針,數(shù)組索引0-3對應軟驅(qū)A-D

static struct task_struct * wait_motor[4] =
{NULL , NULL , NULL , NULL} ;
//每個馬達啟動所需要的時間數(shù)

static int mon_timer[4] =
{0 , 0 , 0 ,0} ;
//記錄每個馬達停轉(zhuǎn)之前維持的時間

static int moff_timer[4] =
{0,0,0,0} ;
unsigned char current_DOR = 0x0C ;//這里設(shè)置初值,允許DMA和中斷請求。啟動FDC
//指定軟驅(qū)啟動到正常運轉(zhuǎn)狀態(tài)所需要的等待時間
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 ;
}


/**//*軟盤定時處理子程序*/

/**//*更新馬達啟動定時值和馬達關(guān)閉停轉(zhuǎn)定時值*/

/**//*系統(tǒng)每當經(jīng)過一個滴答就會被調(diào)用一次,隨時更新馬達開啟或者停轉(zhuǎn)的時間*/
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 ; //定時滴答數(shù)
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,則立即執(zhí)行處理程序,并且該定時器不加入鏈表
(fn)() ;
//否則從定時器數(shù)組中,找出一個空的項

else
{
for(p = timer_list ; p < timer_list + TIMER_REQUEST ; p++)
if(!p->fn) //找到一個空項,然后退出
break ;

//如果已經(jīng)用完了定時器數(shù)組,則系統(tǒng)崩潰,否則向定時器的數(shù)據(jù)結(jié)構(gòu)中填入相應的信息,并連入鏈表頭
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 ;
//下面的函數(shù)其實是一個 雙向隊列

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 ; //揚聲器發(fā)生時間滴答數(shù)
extern void sysbeepstop (void) ; //關(guān)閉揚聲器
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() ;

}


//系統(tǒng)調(diào)用功能,設(shè)置報警定時時間值
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 ;
}
//系統(tǒng)調(diào)用功能---降低對CPU的優(yōu)先使用權(quán)
int sys_nice(long increment)

{
if(current->priority - increment > 0)
current->priority -= increment ;
return 0 ;
}
//內(nèi)核調(diào)度程序初始化

void sched_init(void)

{
int i ;
struct desc_struct * p ; //描述符表結(jié)構(gòu)指針
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) ;

}

