作業系統實驗二 作業排程
實驗目的
1) 加深作業概念的理解;
2) 掌握選擇作業排程演算法的準則;
3) 掌握作業排程演算法。
實驗要求
1) 編寫程式完成實驗內容;
2) 對測試資料進行分析;
3) 撰寫實驗報告。
實驗內容
1) 設計可用於該實驗的作業控制塊;
2) 動態或靜態建立多個作業;
3) 模擬先來先服務排程演算法和短作業優先排程演算法。
4) 排程所建立的作業並顯示排程結果(要求至少顯示出各作業的到達時間,服務時間,開始時間,完成時間,週轉時間和帶權週轉時間);
5)比較兩種排程演算法的優劣。
實驗原理
1.作業
作業(Job)是一個比程式更為廣泛的概念,它不僅包含了通常的程式和資料,而且還應配有一份作業說明書,系統根據該說明書來對程式的執行進行控制。在批處理系統中,是以作業為基本單位從外存調入記憶體的。
2.作業控制塊JCB(Job Control Block)
為了管理和排程作業,在多道批處理系統中為每個作業設定了一個作業控制塊,如同程序控制塊是程序在系統中存在的標誌一樣,它是作業在系統中存在的標誌,其中儲存了系統對作業進行管理和排程所需的全部資訊。在JCB中所包含的內容因系統而異,通常應包含的內容有:作業標識、使用者名稱稱、使用者帳戶、作業型別(CPU 繁忙型、I/O 繁忙型、批量型、終端型)、作業狀態、排程資訊(優先順序、作業已執行時間)、資源需求(預計執行時間、要求記憶體大小、要求I/O裝置的型別和數量等)、進入系統時間、開始處理時間、作業完成時間、作業退出時間、資源使用情況等。
3.
作業排程的主要功能是根據作業控制塊中的資訊,審查系統能否滿足使用者作業的資源需求,以及按照一定的演算法,從外存的後備佇列中選取某些作業調入記憶體,併為它們建立程序、分配必要的資源。然後再將新建立的程序插入就緒佇列,準備執行。
4.選擇排程演算法的準則
1.面向使用者的準則
(1) 週轉時間短。通常把週轉時間的長短作為評價批處理系統的效能、選擇作業排程方式與演算法的重要準則之一。所謂週轉時間,是指從作業被提交給系統開始,到作業完成為止的這段時間間隔(稱為作業週轉時間)。它包括四部分時間:作業在外存後備佇列上等待(作業)排程的時間,程序在就緒佇列上等待程序排程的時間,程序在CPU上執行的時間,以及程序等待I/O
對每個使用者而言,都希望自己作業的週轉時間最短。但作為計算機系統的管理者,則總是希望能使平均週轉時間最短,這不僅會有效地提高系統資源的利用率,而且還可使大多數使用者都感到滿意。可把平均週轉時間描述為:
作業的週轉時間T與系統為它提供服務的時間Ts之比,即W = T/Ts,稱為帶權週轉時間,而平均帶權週轉時間則可表示為:
(2) 響應時間快。常把響應時間的長短用來評價分時系統的效能,這是選擇分時系統中程序排程演算法的重要準則之一。所謂響應時間,是從使用者通過鍵盤提交一個請求開始,直至系統首次產生響應為止的時間,或者說,直到螢幕上顯示出結果為止的一段時間間隔。它包括三部分時間:從鍵盤輸入的請求資訊傳送到處理機的時間,處理機對請求資訊進行處理的時間,以及將所形成的響應資訊回送到終端顯示器的時間。
(3) 截止時間的保證。這是評價實時系統性能的重要指標,因而是選擇實時排程演算法的重要準則。所謂截止時間,是指某任務必須開始執行的最遲時間,或必須完成的最遲時間。對於嚴格的實時系統,其排程方式和排程演算法必須能保證這一點,否則將可能造成難以預料的後果。
(4) 優先權準則。在批處理、分時和實時系統中選擇排程演算法時,都可遵循優先權準則,以便讓某些緊急的作業能得到及時處理。在要求較嚴格的場合,往往還須選擇搶佔式排程方式,才能保證緊急作業得到及時處理。
2.面向系統的準則
這是為了滿足系統要求而應遵循的一些準則。其中,較重要的有以下幾點:
(1) 系統吞吐量高。這是用於評價批處理系統性能的另一個重要指標,因而是選擇批處理作業排程的重要準則。由於吞吐量是指在單位時間內系統所完成的作業數,因而它與批處理作業的平均長度具有密切關係。對於大型作業,一般吞吐量約為每小時一道作業;對於中、小型作業,其吞吐量則可能達到數十道作業之多。作業排程的方式和演算法對吞吐量的大小也將產生較大影響。事實上,對於同一批作業,若採用了較好的排程方式和演算法,則可顯著地提高系統的吞吐量
(2) 處理機利用率好。對於大、中型多使用者系統,由於CPU價格十分昂貴,致使處理機的利用率成為衡量系統性能的十分重要的指標;而排程方式和演算法對處理機的利用率起著十分重要的作用。在實際系統中,CPU的利用率一般在40%(系統負荷較輕)到90%之間。在大、中型系統中,在選擇排程方式和演算法時,應考慮到這一準則。但對於單使用者微機或某些實時系統,則此準則就不那麼重要了。
(3) 各類資源的平衡利用。在大、中型系統中,不僅要使處理機的利用率高,而且還應能有效地利用其它各類資源,如記憶體、外存和I/O裝置等。選擇適當的排程方式和演算法可以保持系統中各類資源都處於忙碌狀態。但對於微型機和某些實時系統而言,該準則並不重要。
5.作業排程演算法
1).先來先服務排程演算法
先來先服務(FCFS)排程演算法是一種最簡單的排程演算法,每次排程都從後備作業佇列中選擇一個或多個最先進入該佇列的作業,將它們調入記憶體,為它們分配資源、建立程序,然後放入就緒佇列。
2).短作業優先排程演算法
短作業優先排程演算法SJF,是指對短作業優先排程的演算法。短作業優先(SJF)的排程演算法是從後備佇列中選擇一個或若干個估計執行時間最短的作業,將它們調入記憶體執行。
3).最高優先權優先排程演算法
為了照顧緊迫型作業,使之在進入系統後便獲得優先處理,引入了最高優先權優先(FPF)排程演算法。系統從後備佇列中選擇若干個優先權最高的作業裝入記憶體。
以下程式碼在VC++6.0執行通過:
#include "stdio.h"
#define getjcb(type) (type*)malloc(sizeof(type))
#define NULL 0
int n=0,time=0;float eti,ewi;
struct jcb{ char name[10]; /* 作業名 */
char state; /* 作業狀態 */
int ts; /* 提交時間 */
int tb; /* 開始執行時間 */
int tc; /* 完成時間 */
float ti; /* 週轉時間 */
float wi; /* 帶權週轉時間 */
int ntime; /* 作業所需執行時間 */
char resource[10]; /* 所需資源 */
struct jcb *link; /* 結構體指標 */
} *p,*q,*head=NULL;
typedef struct jcb JCB;
initial(){
int i;
printf("\nInput jcb num\n");
scanf("%d",&n);
printf("Input\nname\tts\tntime\tresource\n");
for(i=0;i<n;i++){
p=getjcb(JCB);
scanf("%s\t%d\t%d\t%s",&p->name,&p->ts,&p->ntime,&p->resource);
p->state='W';
p->link=NULL;
if(head==NULL) head=q=p;
else{
q->link=p;
q=p;
}
}
}
void print(JCB *pr){
JCB *p;
printf("\ntime=%d",time);
printf("\nname\tstate\tts\tntime\tsource\ttb\ttc\tti\twi\n");
printf("%s\t%c\t%d\t%d\t%s\t%d\t%d\t%4.2f\t%4.2f\n",
pr->name,pr->state,pr->ts,pr->ntime,pr->resource,pr->tb,pr->tc,pr->ti,pr->wi);
p=head;
do{
if(p->state=='W')
printf("%s\t%c\t%d\t%d\t%s\n",
p->name,p->state,p->ts,p->ntime,p->resource);
p=p->link;
}while(p!=NULL);
p=head;
do{
if(p->state=='F')
printf("%s\t%c\t%d\t%d\t%s\t%d\t%d\t%4.2f\t%4.2f\n",
p->name,p->state,p->ts,p->ntime,p->resource,p->tb,p->tc,p->ti,p->wi);
p=p->link;
}while(p!=NULL);
}
void last(){
eti/=n;ewi/=n;
printf("\neti=%7.3f\tewi=%7.3f\n",eti,ewi);
}
void sjf(){
JCB *min;
int i,iden;
for(i=0;i<n;i++){
p=min=head;iden=1;
do{
if(p->state=='W'&&p->ts<=time)
if(iden){
min=p;iden=0;
}
else if(p->ntime<min->ntime) min=p;
p=p->link;
}while(p!=NULL) ;
if(iden) {
i--;printf("\ntime=%d:\tno JCB submib...wait...",time);time++;
if(time>100){printf("\nruntime is too long...error");getch();}
}
else{
running(min);
}
}
}
fcfs(){
JCB *min;
int i,iden;
for(i=0;i<n;i++){
p=min=head;iden=1;
do{
if(p->state=='W'&&p->ts<=time)
if(iden){
min=p;iden=0;
}
else if(p->ts<min->ts) min=p;
p=p->link;
}while(p!=NULL) ;
if(iden) {
i--;printf("\ntime=%d:\tno JCB submib...wait...",time);time++;
if(time>100){printf("\nruntime is too long...error");getch();}
}
else{
running(min);
}
}
}
running(JCB *p){
p->tb=time;p->state='R';
p->tc=p->tb+p->ntime;
p->ti=(float)(p->tc-p->ts);
p->wi=(float)(p->ti/p->ntime);
eti+=p->ti;
ewi+=p->wi;
print(p);
time+=p->ntime;
p->state='F';
printf("\n%s has been finished!\npress any key to continue...\n",p->name);
getch();
}
void runjcb(int m){
printf("\n\nstart running jcb use algorithm %d.",m);
switch(m){
case 1:fcfs();break;
case 2:sjf();break;
default:printf("\nrunjcb error...\n");exit();
}
}
start(){
int m;
char str[100]="\nselect algorithm\n1.FCFS\n2.SJF\n" ;
printf("%s",str);
m=getch()-48;
initial();
if(1<=m&&m<=2) runjcb(m);
else {
printf("\nselect error!try again...\n");
start();
}
last();
}
main(){
start();
printf("\nfinished!");
getch();
}