OS_程式排程:C++實現

QIOHA發表於2020-06-19

實驗二、程式排程模擬實驗
一、實驗目的:
本實驗模擬在單處理機環境下的處理機排程,幫助理解程式排程的概念,深入瞭解程式控制塊的功能,以及程式的建立、撤銷和程式各個狀態間的轉換過程。
二、實驗內容:

  1. 程式排程演算法:採用最高優先數優先的排程演算法、先來先服務演算法、SJF和多級反饋排程演算法。
  2. 每個程式有一個程式控制塊(PCB)表示。程式控制塊可以包含如下資訊:程式名、優先數、到達時間、需要執行時間、已用CPU時間、程式狀態等等。程式的優先數及需要的執行時間可以事先人為輸入(也可以由隨機數產生)。程式的到達時間為程式輸入的時間。 程式的執行時間以時間片為單位進行計算。
  3. 就緒程式獲得CPU後都只能執行一個時間片。用已佔用CPU時間加1來表示。如果執行一個時間片後,程式的已佔用CPU時間已達到所需要的執行時間,則撤消該程式,如果執行一個時間片後程式的已佔用CPU時間還未達所需要的執行時間,也就是程式還需要繼續執行,此時應將程式的優先數減1(即降低一級),然後把它插入就緒佇列等待CPU。
  4. 每個程式的狀態可以是就緒W(Wait)、執行R(Run)、或完成F(Finish)三種狀態之一。
    三、實驗要求:
  5. 每進行一次排程程式都列印一次執行程式、就緒佇列、以及各個程式的PCB,以便進行檢查。
  6. 對同一組程式的各種排程演算法分別計算平均週轉時間和平均帶權週轉時間。

第一步
關於標頭檔案:

    #include<stdio.h>
    #include<cstdlib>
    #include<iostream>
    #define N 3

我們這裡預設最大允許三個程式,您也可以自己改成5,這邊建議用3就好,程式還有一些不為人知的祕密

名稱空間這可以學習一下c++,與本演算法無太大關係所以在此不講述

    using std::cout;
    using std::cin;
    using std::endl;

接下來我們開始我們最初的結構的定義:

程式pcb的定義

因為是程式排程,所以一定涉及到了程式pcb的呼叫,跟書一樣的定義,我們直接給出
    typedef struct pcb{
    	char pname;//程式名
    	int n;//優先順序
    	int atime;//程式到達時間
    	int rtime;//執行時間
    	int utime;//已用時間
    	char status;//程式的狀態,w等待,r執行
    	pcb* next;
    }pcb,*PCB;

	PCB zpcb[N];//裝載著每個pcb的指標陣列
	int ctime;//當前執行時間用於先來先服務
	int num;//處理機要處理的程式的個數
	float time1,time2;//1為平均週轉:完成減去到達,2為帶權週轉:平均除以執行
	PCB head=(PCB)malloc(sizeof(pcb));

zpcb是一個指標陣列你也可以想象成程式佇列,這是根據演算法的各個形式進行增刪的,time1是平均週轉時間,用完成減去到達就可以,time2是平均帶權週轉時間,用time1除以程式執行時間即可,這裡可以看後面各個排程演算法的應用,很容易理解。

假設我們的排程演算法都實現了,首先我們需要定義我們的引數,建立pcb,裝載pcb陣列,程式個數等等####

那麼我們首先定義一個方法,createProcess
在建立之前我們需要一個思考,我們的程式的初始狀態應該都置於wait,我們可以將所有的程式放置在我們之前的head佇列中,方便後續的使用。我們需要輸入我們的程式名等pcb結構中需要的值,最後用一個佇列進行裝載,這裡只給出一些提示,讀者自己創造。

int createProcess(){
  	cout<<"請輸入您需要執行的程式數目(max:3)";
  	cin>>num;
  	cout<<"輸入 "<<num<<"個的程式名、優先順序、到達時間、執行時間(空格隔開,程式直接回車隔開)\n注意:程式的到達時間必須遞增,首個的到達時間為0"<<endl;
  	PCB p = head;
  		for(int i=0;i<num;i++){

希望讀者可以自己去實現,最後我們需要告訴我們的排程演算法使用者一共建立了幾個程式,所以num應該作為返回值進行一個返回,這裡其實有一個bug,我的到達時間是我自己控制的,這是一種純粹的理想狀態。希望有人後來居上。

既然已經建立好了我們的程式,接下來是一種關於使用程式排程演算法了,我們可以設定一個while迴圈,讓使用者去選擇,while中可以搭配swtich語句這種簡單的留給讀者####

在這裡面你肯定會這樣想,我們是不會想呼叫一個演算法就退出,我們是想再排程幾個,用原來的輸入,所以我們如果已經動了我們的head,那麼這裡面的值都要一個歸零,就會很麻煩,這裡你會想起來我們的zpcb陣列,它可以代替我們的head,這裡我們需要一個clean函式,讓使用時間歸零,讓狀態至為w,這個clean叫程式狀態初始化。在此給出程式碼

  void clean(){//清除狀態,使用其他的排程演算法
   	cout<<"程式資訊初始化:"<<endl;
   	PCB pc=head->next;
   	int i;
   	 while(pc)
   {
   i++;
   pc->utime=0;
   pc->status='W';
   pc=pc->next;
   }
   }

第一個排程演算法:PSA高優先數###

在每次傳參我們只需要傳入我們main中的num,其餘的為全域性變數,
這裡直接給出定義

    void PSA(int num){
    	//int n=num;
     	PCB pc=head->next;
    	int time,pr[N];//pr陣列記錄了各個程式的優先順序
    	time1=0;
    	time2=0;

首先我們將n暫替我們的num,pc指標指向我們的首程式,pr陣列用於記錄優先順序,time在前期可以充當一個臨時的變數,後期可以充當我們的系統時間,兩個平均**時間我們可以讓它們進行一個清零。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

第一步我們得讓我們的pr陣列和我們的zpcb陣列記錄下程式的優先順序,和clone整個程式,這就是我們上面說的作用,關於zpcb陣列,一個參與排程的程式集合,ok話不多說我們給出程式碼:

    for(time=0; time<N; time++)
	if(pc)
    {
    zpcb[time]=pc;
    pr[time]=pc->n;
    pc=pc->next;
    }

接下來我們找一找有沒有與我們程式同時到達的程式,又要使用我們的工具變數time,定義一個for,這裡我們的zpcb已經有了我們所有的程式,所以我們直接zpcb[0]->atime==zpcb[time]->atime,看看我們的for迴圈執行與否,如果我們的time大於了1,說明在此刻已經有程式同時到達了,這時候我們要去比較它們的優先順序決定誰位於對首,後面的執行中我們肯定是不止一次要進行一個比較優先順序的操作,我們為什麼不將這個封裝成函式呢,既然如此,幹就完事了。

sort函式:比較優先順序,在同時為第一時間到達的程式中將優先順序高的放置與首,程式優先順序n越大就說明優先順序越高
void sort(int time)
{
    int i=1;
    while(zpcb[i]&&time>=zpcb[i]->atime)i++;
    for(int j=0; j<i-1; j++)
        for(int k=j+1; k<i; k++)if(zpcb[j]->n<zpcb[k]->n)
            {
                PCB c=zpcb[j];
                zpcb[j]=zpcb[k];
                zpcb[k]=c;
            }
}

這裡直接給出程式碼,因為太簡單,就用了一個排序預設你們都可以看懂

接下來我們回到我們的psa排程演算法,這裡給出了之後我們的工具變數time可以做回本職工作了,將第一個的到達時間設定為time,也就是zpcb[0]—>atime;
接下來開始我們的排程,我們首先用一個大while,條件是num>0,

我們需要考慮幾種情況,第一種情況,//如果第二個程式還未到達,第一個程式就開始執行//優先順序減一,執行時間和總的時間加1,狀態置為run
第二種情況,第二個程式到達了

這裡是第一種情況

大致思想:首先我們讓第一個程式的utime++ 系統時間++ 在這個同時讓我們的優先順序-- 因為已經進行了一次對cpu的使用,狀態置為r 這時候我們找一找有沒有到達的程式,比較優先順序,輸出正在執行的程式,在這個同時我們也要將優先順序最高且到達的程式至於第一個,輸出一下我們的就緒佇列,也就是已經到達的程式,已經就緒的程式,看看有沒有完成的程式,如果有就狀態置為f計算兩個時間,將zpc中的第一個程式覆蓋掉因為已經排程完了,如果沒有完成,我們就讓它置為等待,進行新的判斷。

這裡我們還有一個問題就是我們的程式重新的進行了排序輸出了就緒佇列,我們同時也需要輸出當前所有程式的狀態,因為並不是所有的程式都是就緒了,因此這裡需要輸出,在後面我們進行了換位也好,排程完成了都是需要使用,這裡就需要一個show,展示當前所有程式的狀態,這裡直接給出

void show(bool n)
{
    for(PCB p=head->next;p!=NULL;p=p->next)
{
		if(n)
		cout<<"程式名:"<<p->pname<<"  優先順序數:"<<p->n<<"到達時間:"<<p->atime<<"  執行時間:"<<p->rtime<<"  已用執行時間:"<<p->utime<<"  狀態:"<<p->status<<endl;
		else  cout<<"程式名:"<<p->pname<<"  到達時間:"<<p->atime<<"  執行時間:"<<p->rtime<<"  已用執行時間:"<<p->utime<<"  狀態:"<<p->status<<endl;
    }
    cout<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
}

因為大致思想描述可能需要程式碼參考,也在這附上我的程式碼

if(zpcb[1]&&time<zpcb[1]->atime||!zpcb[1])//如果第二個程式還未到達,第一個程式就開始執行
           //優先順序減一,執行時間和總的時間加1,狀態置為run
       {
           zpcb[0]->utime++;
           time++;
           if(zpcb[0]->n!=0)zpcb[0]->n--;
           zpcb[0]->status='R';
           int i;
           for(i=1; zpcb[i]; i++)if(time>=zpcb[i]->atime);
               else break;//找看有沒有比第二個程式先到達的程式
           for(int j=1; j<i-1; j++)//如果優先順序比第二個的高就換位
               for(int k=j+1; k<i; k++)if(zpcb[j]->n<zpcb[k]->n)
                   {
                       PCB t=zpcb[j];
                       zpcb[j]=zpcb[k];
                       zpcb[k]=t;
                   }
           printf("正在執行的程式:%c\t",zpcb[0]->pname);
           printf("就緒佇列:");
           for(int i=1; zpcb[i]; i++)
   		if(time>=zpcb[i]->atime)   printf("%c ",zpcb[i]->pname);//輸出到達時間到了系統時間的程式,為就緒佇列
           printf("\n");
           show(true);
           if(zpcb[0]->utime==zpcb[0]->rtime)//如果當前程式執行完了狀態為F,平均和帶權根據公式,將後一個覆蓋前一個
           {//再去檢視有與系統時間time相同的到達時間或者在之前到達,比較優先順序
               zpcb[0]->status='F';
               time1+=time-zpcb[0]->atime;
               time2+=(double)(time-zpcb[0]->atime)/zpcb[0]->utime;
               int i;
               for( i=1; zpcb[i]; i++)zpcb[i-1]=zpcb[i];
               zpcb[i-1]=NULL;
               num--;
               sort(time);
           }
           else//如果沒執行完就等待,根據優先順序再度確定下一個要執行的程式
           {
                zpcb[0]->status='W';
               if( zpcb[1]!=NULL) sort(time);
           }
     }

希望讀者能夠自主的編碼,
第二個情況是第二個程式到達了,比較到達了之後的優先順序分為大於和小於第一個程式,後面與前面一樣

 else//第二個程式到達了
        {
            if( zpcb[1]&& zpcb[0]->n>= zpcb[1]->n||! zpcb[1])//如果第二個程式也到達了,且優先順序小於第一個程式
            {
                 zpcb[0]->utime++;
                time++;
                if( zpcb[0]->n!=0) zpcb[0]->n--;
                 zpcb[0]->status='R';
            }
            else//如果優先順序比第一個程式高,那麼第一個程式等待,將其換位置,執行
            {
                zpcb[0]->status='W';
                sort(time);
                zpcb[0]->utime++;
                time++;
                if( zpcb[0]->n!=0) zpcb[0]->n--;
                 zpcb[0]->status='R';
            }
            int i;
            for(i=1;  zpcb[i]; i++)if(time>= zpcb[i]->atime);
                else break;
            for(int j=1; j<i-1; j++)
                for(int k=j+1; k<i; k++)if( zpcb[j]->n< zpcb[k]->n)
                    {
                        PCB t= zpcb[j];
                         zpcb[j]= zpcb[k];
                         zpcb[k]=t;
                    }//與上一個一樣,下一個程式到達了,比較優先順序
            printf("正在執行的程式:%c\t", zpcb[0]->pname);
            printf("就緒佇列:");//輸出就緒佇列,判斷是否完成
            for(int i=1;  zpcb[i]; i++)if(time>= zpcb[i]->atime)printf("%c ", zpcb[i]->pname);
            printf("\n");
            show(true);
            if( zpcb[0]->utime== zpcb[0]->rtime)
            {
                 zpcb[0]->status='F';
                time1+=time- zpcb[0]->atime;
                time2+=(double)(time- zpcb[0]->atime)/ zpcb[0]->utime;
                int i;
                for( i=1;  zpcb[i]; i++) zpcb[i-1]= zpcb[i];
                 zpcb[i-1]=NULL;
                num--;
                sort(time);
            }
            else
            {
                 zpcb[0]->status='W';
                if( zpcb[1]!=NULL)sort(time);
            }
        }
    }

最後大while結束,輸出最終的程式的資訊和兩個時間time1 time2 清楚狀態,輸出程式佇列的初始佇列,等待下一輪新的選擇。PSA到此結束

接下來是FCFS先來先服務

基本思想:在這已經沒有優先順序的概念了,定義好pc指標和time,(這裡我們沒有改變其他的,可以直接用head佇列,也就是pc。)記得初始化兩個時間,我們直接一個大while進行使用,讓系統時間和utime進行++就好,每進行一次輸出就緒佇列,發現有完成了也是覆蓋,最後輸出最終情況,清零,展示原始狀態,這裡比較的簡單我就不貼程式碼了,讀者自行編碼。

短程式優先

//短程式優先
//當做了幾個之後,其實是差不多的,這裡的優先順序是作業的使用時間
//即utime,剛開始第一個我們判斷是否存在且第二個是否到達
//然後分情況進行處理,跟前面的程式碼有重合,其實可以封裝一下算時間的等等,
//
基本思想:這裡跟前面一樣進行初始化,用zpcb陣列,一個while,接下來想想會有幾種情況,第一種後面的程式還沒來,第二種後面的程式來了。
假如沒來,直接讓它完成,不用++直接+rtime。輸出各個佇列,覆蓋,狀態置為f,兩個time,num--
如果來了,比較誰的rtime小,置於第一個,接下來是一樣的操作,就不敘述了,這裡附上程式碼,留給讀者進行參考,很簡單希望自己編碼。

void SJF(int num){
	int n = num;
	PCB pc=head->next;
	int time=0;
	time1=time2=0;
		for(time=0;time<N;time++)if(pc){
			zpcb[time]=pc;
			pc=pc->next;
		}
	time = zpcb[0]->atime;
	while(num>0){
		if(zpcb[1]&&time<zpcb[1]->atime||!zpcb[1]){
			zpcb[0]->utime+=zpcb[0]->rtime;
			time+=zpcb[0]->rtime;
			zpcb[0]->status='R';
			printf("正在執行的程式:%c\t",zpcb[0]->pname);
			printf("就緒佇列:");
			for(int i = 1;zpcb[i]&&time>zpcb[i]->atime;i++)
					cout<<zpcb[i]->pname;
			cout<<endl;
			show(false);
			if(zpcb[0]->utime==zpcb[0]->rtime){
				zpcb[0]->status='F';
				time1+=time-zpcb[0]->atime;
				time2+=(float)(time-zpcb[0]->atime)/zpcb[0]->utime;
				int i;
				for(i = 1; zpcb[i];i++)zpcb[i-1]=zpcb[i];
				zpcb[i-1]=NULL;
				num--;
			}
		}
		else{
			int i;
			for(i=1;zpcb[i];i++)if(time>=zpcb[i]->atime);
				else break;
			for(int j=0;j<i-1;j++)
				for(int k = j+1;k<i;k++)if(zpcb[j]->rtime>zpcb[k]->rtime){
					PCB t=zpcb[j];
					zpcb[j]=zpcb[k];
					zpcb[k]=t;
				}
			zpcb[0]->utime+=zpcb[0]->rtime;
			time+=zpcb[0]->rtime;
			zpcb[0]->status='R';
			cout<<"正在執行的程式:"<<'\t'<<zpcb[0]->pname;
			printf("就緒佇列:");
			for(i= 1;zpcb[i]&&time>zpcb[i]->atime;i++)
				cout<<zpcb[i]->pname;
			cout<<endl;
			show(false);
			if(zpcb[0]->utime==zpcb[0]->rtime){
				zpcb[0]->status='F';
				time1+=time-zpcb[0]->atime;
				time2+=(float)(time-zpcb[0]->atime)/zpcb[0]->utime;
				num--;
			}
		}
	}

	cout<<"最終的程式資訊:"<<endl;
	show(false);
	clean();
	cout<<"平均週轉時間:"<<(float)(time1/n)<<"      平均帶權週轉時間:"<<(float)(time2/n)<<endl<<endl;
    show(false);
}

多級反饋MFQ 這個演算法我的程式碼有問題在此只敘述思想

關於初始化這裡可以給出:

void MFQ(int num){
	int n=num;
	 PCB head1[N],head2[N],head3[N],pc=head->next;     //三個優先順序佇列
    int time=pc->atime;
	time1=0;
    time2=0;
    for(int i=0; i<N; i++)head1[i]=head2[i]=head3[i]=NULL;//每個就緒清空
    head1[0]=pc;//放入第一個
    pc=pc->next;

接下來就是while分情況,後續沒到達,後續到達。
後續沒到達也需要分情況,首先就是看看優先順序最高到最低,從h0(head0)到h3中哪一個是有程式的,讓它進行一個使用,並輸出其他的兩個佇列的程式,如果三個都沒有,我們將pc指向的程式進行一個放入h1中,看看情況如何,最後展示當前程式的一個情況。。。看當前的程式佇列0-2中是否有已經完成程式,有就狀態置為f,覆蓋,num--,計算時間12,沒有就投放下一級佇列如果head3中就將其放到最後。
第二種情況中,我們需要判斷pc是不是結束標誌,如果不是加入head1後,繼續執行一個時間片,輸出情況,判斷是否執行完,如果沒有後續程式,將每個佇列中的程式由0-2的優先順序進行執行,判斷是否完成,丟入下級。
最後輸出情況
基本的思想我都已經實現,但是會有一些問題,希望寫出程式碼的同志評論區走一走,最後我們的程式碼就編完了,希望多級反饋能夠有一個更好的程式碼,我的程式碼就不附上了。。。
完結撒花!!!

相關文章