操作系統實驗(一)——進程調度

        此文章爲關於操作系統實驗的講解,在這一學期,我學習了操作系統,即然學習課程肯定有實驗,第一個實驗爲進程的調度。進程調度有相當多的算法,比如最簡單的先來先服務算法,時間片輪轉算法,多級隊列調度算法等。我將它們中的一部分寫進了一個系統用於模擬進程調度,以幫助學習,在這裏分享一下我的寫法與思路。

        當前代碼中包含三種算法:先來先服務,短作業優先,高響應比優先。剩餘的時間片輪轉算法與多級隊列算法會在之後慢慢嘗試並分享我的做法。之後的內容爲我的實驗內容和完成過程。

一.基本操作準備

       我希望這個小系統是一個可以進行很方便的擴展的系統,雖然相當簡陋並且效率低。爲了實現這個功能,我將各種進程調度寫成了函數,這樣可以減少各模塊之間的關係並且可以提升複用程度。同時還應瞭解這幾種調度之間的關係,據我分析,這三種調度有不小的聯繫,簡單說,它們之間的關係是一層一層擴展的關係,短作業優先算法是在對先來先服務算法上加了服務時間的限制,高響應比優先則是又加上了等待時間的關係,這些聯繫我會在後面進行詳細說明。

       爲了可以比較好的實現我想要的功能,比較簡單的完成各種隊列操作,我寫了一些隊列函數。因爲進程調度機制本身就是一個隊列式的服務機制,用隊列的數據結構存儲是再自然不過的。

隊列操作代碼:

typedef struct {//進程類型的創建
    char progress_name[10];//進程名
    int priority;//進程的優先級
    int arrive_time;//到達時間
    int serve_time;//要求服務時間
    int progress_state;//進程狀態
    
    int wait_time;//等待時間
    int leave_time;//離開時間
}PCB;//PCB爲進程控制快,這裏將進程控制塊抽象成一個結構體

int time_clock;//時間軸,在每一次調度開始前都要置0

/*鏈式隊列的創建與操作*/
typedef struct ele{
    PCB a_progress_pcb;
    struct ele* next;
}PCB_node;//隊列中存儲進程的節點的創建

typedef struct ele_quene{
    PCB_node* PCB_head;
    PCB_node* PCB_tail;
    int quene_long;
}PCB_quene;//鏈式隊列的創建

void quene_init(PCB_quene **que){//鏈式隊列的初始化
    *que = (PCB_quene*)malloc(sizeof(PCB_quene));
    (*que)->PCB_head = NULL;
    (*que)->PCB_tail = NULL;
    (*que)->quene_long = 0;
}

void PCB_enter(PCB_quene* que, PCB progress)//PCB入隊操作
{
    if(que == NULL)
        return ;
    PCB_node* pTemp = (PCB_node*)malloc(sizeof(PCB_node));
    pTemp->a_progress_pcb = progress;
    pTemp->next = NULL;

    if(que->PCB_head==NULL){
        que->PCB_head = pTemp;
    }else{
        que->PCB_tail->next = pTemp;
    }
    que->PCB_tail = pTemp;
    que->quene_long++;
}


PCB PCB_leave(PCB_quene* que)//PCB出隊操作
{
    PCB error;
    error.arrive_time=-1;
    error.leave_time=-1;
    error.priority=-1;
    strcpy(error.progress_name,"error");
    error.progress_state=-1;
    error.serve_time=-1;
    error.wait_time=-1;
    if(que == NULL || que->quene_long == 0)
        return error;
    PCB_node* pDel = NULL;
    PCB res;
    pDel = que->PCB_head;
    que->PCB_head = que->PCB_head->next;
    res = pDel->a_progress_pcb;
    free(pDel);
    que->quene_long--;
 
    if(que->quene_long == 0)
        que->PCB_tail = NULL;
    return res;
}

        注:由於進程的存在是靠進程控制塊(PCB)標識的,因此我將它命名爲PCB。

        由於這裏的隊列我都是創建的地址,同時隊列排序很麻煩。爲了防止隊地址操作導致的指針錯誤和問題的簡單化,我在main函數中輸入這些進程信息時是用一個結構數組保存的,這樣以來我可以隨意改變數組的信息而不影響到隊列,需要使用時只要將數組轉化成隊列即可。在這裏,我的信息輸入時每個進程來到的順序並不是按時間順序固定的,也就是說在輸入這些作業時輸入的到達時間並不有順序。因此爲了表達一個先來後到的關係,我們必須有一個按照時間排好序的數組或者隊列,以表達基本的時序關係,因此我將兩種功能結合起來,直接將數組轉化爲按照先來後到的順序排好的鏈式隊列,這個鏈式隊列是直接當成參數傳給各種調度函數的,表示時間上的先來後到順序,我命名它爲時間管道,其實怎麼說都可以。

數組轉化函數:

PCB_quene* time_paixu(PCB undolist[],int num){//需要進行重指針保存,時間排序,這裏是返回一個時間x上的先後隊列,用於表示先來後到
    PCB_quene *endquene;
    quene_init(&endquene);
    int i,j,minum;
    PCB min,chen;
    for(i=0;i<num;i++){
        min = undolist[i];
        minum=i;
        for(j=i+1;j<num;j++){
            if(min.arrive_time>undolist[j].arrive_time){
                min = undolist[j];
                minum = j;
            }
        }
        chen = undolist[i];
        undolist[i] = min;
        undolist[minum] = chen;
    }
    
    for(i=0;i<num;i++){
        PCB_enter(endquene, undolist[i]);
    }
    /*
    PCB_node *head = endquene->PCB_head;
    while(head!=NULL){
        PCB a = head->a_progress_pcb;
        printf("%d",a.arrive_time);
        head = head->next;
    } 用於進行測試,輸出一個鏈表,這裏已經將鏈表進行時間先來後到進行排序並會返回,用於進行先來先服務的操作*/
    
    return endquene;//返回排好序的鏈表,即進程按時序到達的先後關係,即先來後到的關係,由於出入隊列的操作,實際上可以自由轉化
}

        我們知道一個PCB不僅有時間上的先來後到順序,還有優先級,在除先來先服務調度外的兩種方法中優先級都起着不小的作用,因此我們在需要進行優先級判斷時,如果想知道誰優先級高誰優先級低,我們必須要有一個優先級排序方法。

優先級排序:

void yxj_paixu(PCB_quene* npx){//大家一定不要學習這種低級的胡亂命名方法
    PCB undolist[20];
    int i,n = npx->quene_long,j,minum;
    for(i = 0;i<n;i++){
        undolist[i]=PCB_leave(npx);
    }
    
    PCB max,chen;
    for(i=0;i<n;i++){
        max = undolist[i];
        minum=i;
        for(j=i+1;j<n;j++){
            if(max.priority<undolist[j].priority){
                max = undolist[j];
                minum = j;
            }
        }
        chen = undolist[i];
        undolist[i] = max;
        undolist[minum] = chen;
    }
    /*for(i=0;i<n;i++){
        printf("%s ",undolist[i].progress_name);
        printf("%d\n",undolist[i].priority);
    }用於進行查錯檢驗,目前查到問題,由於排序機制導致的*/
    for(i=0;i<n;i++){
        PCB_enter(npx, undolist[i]);
    }
    
}

        有了這些基本操作,就可以解決我們的問題了。

二.先來先服務

        我在寫這個實驗的時候對於每一個算法都進行了分析並記錄了我的想法,隨着分析的深入規定的實現方法也在不斷變化,可以先看一下我當時寫的問題分析:

    先來先服務即FCFS,是最簡單的調度算法,其實也就是隊列的基本處理,誰先來,誰先服務,後來的就得等,該算法既可用於作業調度,也可用於進程調度。
    當在作業調度中採用該算法時,系統將按照作業到達的先後次序來進行調度,或者說它是優先考慮在系統中等待時間最長的作業,而不管該作業所需執行的時間長短,從後備隊列中選擇幾個最先進入該隊列的進程。將它們調入內存,爲它們分配資源和創建進程。然後放進就緒隊列。
    當在進程調度中採用FCFS算法時,每次調度是從就緒的進程隊列中選擇一個最先進入該隊列的進程,爲之分配處理機,使之投入運行。該進程一直運行到完成或發生某事件而阻塞後,進程調度程序纔將處理機分配給其他進程。
    FCFS算法在單處理機系統中已經很少作爲主調度算法,但經常把它與其他調度算法相結合使用,形成一種更爲有效的調度算法。例如,可以在系統中按進程的優先級設置多個隊列,每個優先級一個隊列,其中每一個隊列的調度都基於FCFS算法。
    對於先來先服務我打算用時間靜態排序法,因爲先來先服務中的進程不會發生運行到一半截止從而由回到等待序列中的情況,直接靜態生成不會影響其隊列順序,也就是說,只要時間給定,這個序列就是固定的了。
    其他的需要用到模擬時鐘,也就是循環自增的一個時鐘,整個就緒隊列在時循環裏,但是其實仍然需要這個時間靜態隊列,但是它最好是動態的,也就是說我需要一個靜態隊列來進行先來後到的判斷,我還需要一個運行隊列也就是就緒隊列進行緊跟當前時間的模擬調度。就緒隊列需要考慮到優先級等東西,也就是說就緒隊列的內容來自於靜態時間隊列和運行被迫終止的程序。方法是每秒檢查一下系統有沒有什麼動作。

        這篇行文破碎的分析就是我當時一邊進行問題思考,一邊計劃怎麼解決時的思維過程,可以看出最終我確定的方法是遞推方法。不要特別仔細的研究這個分析,因爲當時腦子裏想到什麼寫什麼,主要是留一個思維上的印記,一步一步的找到思路。現在在這裏我將規範的介紹我實現這個問題的方法,上面的分析僅起到一個輔助作用。

        先來先服務,顧名思義,我先來我就先服務,好比食堂排隊,先來先打飯,天經地義,插隊者天理難容。因此,我得到一個結論,先來先服務中的就緒隊列,長度也許是動態的,但是順序一定時靜態的,當這個PCB進入系統後,到達時間已經確定,那麼它第幾個享受服務就已經定好了。同時這些PCB的到達時間都給定後,它們的就緒隊列總體順序也就確定好了。也就是說,先來先服務的就緒隊列可以用一個靜態的隊列表示。這個隊列確實會隨着時間變化,但是隻要到達時間一旦給出,它就定了。因此它的推導方法就很好確定了。

        首先,我們將輸入的數組通過函數轉化成排好序的隊列,然後作爲參數傳入函數。函數中首先會出隊一個元素,作爲正在運行的函數。起始時間是0,表示系統從0時刻開始運行。現在我要計算這個進程的等待時間了,我必須有等待時間,才能通過到達時刻計算出離開時刻。因爲存在:離開時刻=到達時刻+等待時間+服務時間。那麼第一個的等待時間怎麼計算呢?過程是這樣的:系統時間由變量time_clock儲存,它其實真實代表的是上一個的離開時間,第一個進入系統是沒有上一個,那麼它只能爲0。實際上,它就是一個記錄系統時間狀態的座標,作爲這個系統上一次發生變化的時刻。一開始爲0其實就是代表系統開始時間,而系統開始時間或者上一個的離開時間減去到達時間,含義就是等待時間。如圖所示:

當然這裏進程不會再系統開始前就到,這裏只是舉個例子,讓這個含義更加明顯。總的來說,我們用這個變量存儲上一個進程的離開,然後當前進程到達後,我們就可以計算出當前進程的等待時間了。有了等待時間,當前的離開時間也能確定了,這樣一來,重複這個過程,每個進程的等待時間就都能確定。

先來先服務函數:

void FCFS_method(PCB_quene* pcb_quene){
    time_clock=0;//時間軸必須置0,爲當前時間,記錄最初的系統時間和上一個的離開時間
    int wait;//等待時間
    int run_time;//週轉時間
    int run_time_sum=0;//週轉時間和
    double quan_run_time_sum=0;//帶權週轉時間
    PCB_node *head = pcb_quene->PCB_head;//準備進行遍歷
    PCB per_node;
    while(head!=NULL){
        sleep(1);
        per_node = head->a_progress_pcb;
        wait = time_clock-per_node.arrive_time;//time_clock記錄上一個離開
        if(wait<0){
            per_node.wait_time=0;
        }else{
            per_node.wait_time=wait;
        }
        time_clock=per_node.arrive_time+per_node.serve_time+per_node.wait_time;//離開時間爲到達時間加上等待時間加上服務時間
        run_time=per_node.serve_time+per_node.wait_time;
        run_time_sum =run_time_sum+run_time;
        quan_run_time_sum =quan_run_time_sum+(run_time*1.0)/per_node.serve_time;
        per_node.leave_time=time_clock;
        printf("進程:%s\n",per_node.progress_name);
        printf("到達時間:%d\n",per_node.arrive_time);
        printf("服務時間:%d\n",per_node.serve_time);
        printf("離開時間:%d\n",per_node.leave_time);
        printf("等待時間:%d\n",per_node.wait_time);
        printf("週轉時間:%d\n",run_time);
        printf("帶權週轉時間:%lf\n",(run_time*1.0)/per_node.serve_time);
        printf("\n\n");
        head = head->next;
    }
    printf("平均週轉時間爲:%.3lf\n",(run_time_sum*1.0)/pcb_quene->quene_long);
    printf("帶權平均週轉時間爲:%.3lf\n",quan_run_time_sum/pcb_quene->quene_long);
    printf("\n\n");
}

三.短作業優先

        短作業優先其實相當於對於先來先服務加上了一個限制,在每次一個進程運行完後,不應該直接選擇時間上的下一個,而是要檢查一下目前都誰來了,然後從這些進程中選擇一個服務時間短的。這樣以來一個靜態的隊列就遠遠不夠了,此時我需要一個動態的隊列,這個隊列用來存儲“當前”到達的進程,爲什麼是“當前”而不是當前,這是因爲這個算法仍然不是一秒一秒的模擬運行的方法,而仍與上一個算法一樣,是一個推導的算法。接下來是算法描述。

        首先我創建了一個就緒隊列,這個不是數組,是正兒八百的隊列。一開始它是空的,因爲一開始並不清楚有哪個進程會到來,也沒有進程到來。然後我會檢驗它是否爲空,如果爲空,那麼此時有兩種情況,一種是還沒來進程,一種是已經結束了,但是我還是嘗試一下向總體的代表先來後到的時間管道中請求進程,如能請求到一個,那麼就會往下運行,如果請求不到,那麼就說嘛實在沒有了,程序終止。話說回來,我現在請求到了一個進程並且時間管道內仍然有很多進程,那麼程序就會向下執行,程序中仍然有一個時間鐘錶,不再贅述。通過同樣的方式可以求的等待時間,並計算出離開時間,這一步完全相同。但是求出離開時間後,情況發生了變化,進程現在該離開了,下一個該來了,但是誰先來呢,這時,我遍歷整個時間管道,來看一看誰在這個進程離開前到達了,然後加入到就緒隊列中。這個操作是對就緒隊列的更新,現在就明白了爲什麼剛纔的“當前”要加引號了,因爲這不見得是當前,確切的說是以前,我要將我離開以前到達的進程都加進來,表示他們已經進入就緒隊列在等待了。而且每一個進程離開時都要對這個隊列進行更新。更新隊列完畢後,我寫了一個排序,可以將時間短的排在前面。這裏我耍了個小聰明,我並沒有直接對服務時間排序,而是對優先級排序,在調用函數之前,我先用一個優先級刷新函數將函數的優先級通過服務時間映射成了優先級,這樣以來,只要有對優先級的排序,只要寫出映射關係,就可以實現對各種參數進行排序。這個排序函數仍然十分的low,大家將就着看,僅供參考。

優先級排序函數:

void yxj_paixu(PCB_quene* npx){//原理和剛纔的到達時間排序一摸一樣,命名方式也一樣草
    PCB undolist[20];
    int i,n = npx->quene_long,j,minum;
    for(i = 0;i<n;i++){
        undolist[i]=PCB_leave(npx);
    }
    
    PCB max,chen;
    for(i=0;i<n;i++){
        max = undolist[i];
        minum=i;
        for(j=i+1;j<n;j++){
            if(max.priority<undolist[j].priority){
                max = undolist[j];
                minum = j;
            }
        }
        chen = undolist[i];
        undolist[i] = max;
        undolist[minum] = chen;
    }
    /*for(i=0;i<n;i++){
        printf("%s ",undolist[i].progress_name);
        printf("%d\n",undolist[i].priority);
    }用於進行查錯檢驗,目前查到問題,由於排序機制導致的*/
    for(i=0;i<n;i++){
        PCB_enter(npx, undolist[i]);
    }
    
}//方法其實就是將隊列拆解成數組,排序後在裝回來,費時費力,但思路簡單,不推薦使用,一些更好的鏈表排序方法更值得使用

優先級刷新函數:

void refresh_timeyxj(PCB_quene* pcb_quene){
    PCB_node* head = pcb_quene->PCB_head;
    while(head!=NULL){
        head->a_progress_pcb.priority = 100-head->a_progress_pcb.serve_time;
        head=head->next;
    }
}//使用了一個相當簡單的映射

短作業優先函數:

void SJE_method(PCB_quene* pcb_quene){
    int n = pcb_quene->quene_long;
    int run_time;
    int run_time_sum=0;
    double quan_run_time_sum=0;
    time_clock=0;//時間z軸置0
    PCB_node *head;
    PCB_quene* ready_quene;//就緒隊列
    quene_init(&ready_quene);
    PCB runing_pcb,lve;
    int wait;
    while(1){
        sleep(1);
        if(ready_quene->quene_long==0){
            PCB_enter(ready_quene, PCB_leave(pcb_quene));//如果就緒隊列沒有PCB,那麼將從時間管道內請求一個
        }//否則的話要向就緒隊列中請求一個來運行
        runing_pcb = PCB_leave(ready_quene);//向隊頭請求一個,獲得其進程,將先求等待時間等一系例時間
        runing_pcb.progress_state=2;
        wait = time_clock-runing_pcb.arrive_time;//time_clock記錄上一個離開
        if(wait<0){
            runing_pcb.wait_time=0;
        }else{
            runing_pcb.wait_time=wait;
        }
        time_clock=runing_pcb.arrive_time+runing_pcb.serve_time+runing_pcb.wait_time;//計算出當前進程的離開時間
        
        run_time=runing_pcb.serve_time+runing_pcb.wait_time;
        run_time_sum =run_time_sum+run_time;
        quan_run_time_sum =quan_run_time_sum+(run_time*1.0)/runing_pcb.serve_time;
        runing_pcb.leave_time=time_clock;
        //printf("%d",time_clock);
        head=pcb_quene->PCB_head;
        while(head!=NULL){
            if(head->a_progress_pcb.arrive_time<=time_clock){
                lve =head->a_progress_pcb;
                lve.progress_state=1;
                //printf("%s",lve.progress_name);
                PCB_enter(ready_quene, lve);
                PCB_leave(pcb_quene);
                head=pcb_quene->PCB_head;
            }else
                head = head->next;
        }printf("\n");
        //更新隊列
        yxj_paixu(ready_quene);//按照優先級排序
        //printf("%s %d %d\n",runing_pcb.progress_name,runing_pcb.arrive_time,runing_pcb.priority);
        printf("進程:%s\n",runing_pcb.progress_name);
        printf("優先級:%d\n",runing_pcb.priority);
        printf("到達時間:%d\n",runing_pcb.arrive_time);
        printf("服務時間:%d\n",runing_pcb.serve_time);
        printf("離開時間:%d\n",runing_pcb.leave_time);
        printf("等待時間:%d\n",runing_pcb.wait_time);
        printf("週轉時間:%d\n",run_time);
        printf("帶權週轉時間:%lf\n",(run_time*1.0)/runing_pcb.serve_time);
        printf("\n\n");
        runing_pcb.progress_state=3;
        if(ready_quene->quene_long==0&&pcb_quene->quene_long==0){
            break;
        }
    }
    printf("平均週轉時間爲:%.3lf\n",(run_time_sum*1.0)/n);
    printf("帶權平均週轉時間爲:%.3lf\n",quan_run_time_sum/n);
    printf("\n\n");
}

四.高響應比優先

        高響應比優先算法其實大體上和短作業優先非常相似,我的這個函數也是直接拿短作業優先改的,但是它相比短作業優先算法又多了一個動態因素,那就是等待時間,等待時間一直在變,一個進程可能一直在就緒隊列中等待,那麼等待時間也就會一直在升高,等待時間升高後,根據響應比公式,優先級也會提高,那麼它可能就會被先執行。下面是響應比公式:

                            優先級   =(服務時間+等待時間)/   服務時間

        可見,這裏的優先級變得更加合理科學了,但也複雜了。與服務時間和等待時間都有關係,在這裏我一度有些懵,考慮是不是應該一秒一秒的遍歷推進,但是後來發現仍然不需要,只要結合短作業優先算法再進行一些添加就可以了。我發現,只要控制好當一個進程離開時,系統應該發生什麼這個問題,所有的問題就迎刃而解了。這裏,當一個進程離開時,首先我要向就緒隊列裏邊加入新來的進程,也就是說當前進程正在運行時過來的進程,然後要爲他們求出等待了多久,最後求出優先級。這樣,問題就解決了。不過這只是求出了新來的進程,因爲在上一個進程離開時,也會有一些進程加進來並也會進行這個更新操作,但是他們沒有輪到執行,於是當目前正在運行的進程離開時,他們的優先級已經舊了,或說,他們的優先級更新是上一個進程離開時執行的,而非這一個進程離開時執行的,他們在系統中又等待了一些時間,因此這些等待時間要重新求解,然後再更新一次優先級即可。我在這裏直接又遍歷了一次就緒隊列,可以完全更新一次所有進程的優先級和等待時間。注意,這裏存在可以優化的成分,因爲我在更新就緒隊列時已經爲新來的計算過一次新的優先級了,在這裏又全面更新了一次,因此雖然數據不會出錯但是操作出現了容餘。在這裏可以進行優化,將在更新就緒隊列時的等待時間和優先級的更新刪去。這個優化大家可以在看懂算法後自行操作,在此我附上優化前的代碼。

高響應比函數:

void HRRN_method(PCB_quene* pcb_quene){
    int n = pcb_quene->quene_long;
    int run_time;
    int run_time_sum=0;
    double quan_run_time_sum=0;
    time_clock=0;//時間z軸置0
    PCB_node *head,*head1;
    PCB_quene* ready_quene;
    quene_init(&ready_quene);
    PCB runing_pcb,lve,ls;
    int wait;
    while(1){
        sleep(1);
        if(ready_quene->quene_long==0){
            ls=PCB_leave(pcb_quene);
            ls.priority=1;
            PCB_enter(ready_quene, ls);//如果就緒隊列沒有PCB,那麼將從時間管道內請求一個
        }//否則的話要向就緒隊列中請求一個來運行
        runing_pcb = PCB_leave(ready_quene);//向隊頭請求一個,獲得其進程,將先求等待時間等一系例時間
        runing_pcb.progress_state=2;
        wait = time_clock-runing_pcb.arrive_time;//time_clock記錄上一個離開
        if(wait<0){
            runing_pcb.wait_time=0;
        }else{
            runing_pcb.wait_time=wait;
        }//實際等待時間刷新
        time_clock=runing_pcb.arrive_time+runing_pcb.serve_time+runing_pcb.wait_time;//計算出當前進程的離開時間
        
        run_time=runing_pcb.serve_time+runing_pcb.wait_time;
        run_time_sum =run_time_sum+run_time;
        quan_run_time_sum =quan_run_time_sum+(run_time*1.0)/runing_pcb.serve_time;
        runing_pcb.leave_time=time_clock;
        //printf("%d",time_clock);
        head=pcb_quene->PCB_head;
        while(head!=NULL){
            if(head->a_progress_pcb.arrive_time<=time_clock){
                lve =head->a_progress_pcb;
                lve.progress_state=1;
                lve.wait_time=time_clock-lve.arrive_time;
                lve.priority=(lve.wait_time+lve.serve_time)/lve.serve_time;//等待時間和優先級在加入時就更新,這裏與下面的全面更新形成冗餘,可以刪去
                //printf("%s",lve.progress_name);
                PCB_enter(ready_quene, lve);
                PCB_leave(pcb_quene);
                head=pcb_quene->PCB_head;
            }else
                head = head->next;
        }//更新就緒隊列
        head1=ready_quene->PCB_head;
        while(head1!=NULL){
            
            head1->a_progress_pcb.wait_time=time_clock-head1->a_progress_pcb.arrive_time;
            head1->a_progress_pcb.priority=(head1->a_progress_pcb.wait_time+head1->a_progress_pcb.serve_time)/head1->a_progress_pcb.serve_time;
            head1=head1->next;
        }
        //全面數據更新
        yxj_paixu(ready_quene);//按照優先級排序
        //printf("%s %d %d\n",runing_pcb.progress_name,runing_pcb.arrive_time,runing_pcb.priority);
        printf("進程:%s\n",runing_pcb.progress_name);
        printf("優先級:%d\n",runing_pcb.priority);
        printf("到達時間:%d\n",runing_pcb.arrive_time);
        printf("服務時間:%d\n",runing_pcb.serve_time);
        printf("離開時間:%d\n",runing_pcb.leave_time);
        printf("等待時間:%d\n",runing_pcb.wait_time);
        printf("週轉時間:%d\n",run_time);
        printf("帶權週轉時間:%lf\n",(run_time*1.0)/runing_pcb.serve_time);
        printf("\n\n");
        runing_pcb.progress_state=3;
        if(ready_quene->quene_long==0&&pcb_quene->quene_long==0){
            break;
        }
    }
    printf("平均週轉時間爲:%.3lf\n",(run_time_sum*1.0)/n);
    printf("帶權平均週轉時間爲:%.3lf\n",quan_run_time_sum/n);
    printf("\n\n");
}

 

全部代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef struct {
    char progress_name[10];//進程名
    int priority;//進程的優先級
    int arrive_time;//到達時間
    int serve_time;//要求服務時間
    int progress_state;//進程狀態
    
    int wait_time;//等待時間
    int leave_time;//離開時間
}PCB;//PCB爲進程控制快,這裏將進程控制塊抽象成一個結構體

int time_clock;//時間軸,在每一次調度開始前都要置0

/*鏈式隊列的創建與操作*/
typedef struct ele{
    PCB a_progress_pcb;
    struct ele* next;
}PCB_node;

typedef struct ele_quene{
    PCB_node* PCB_head;
    PCB_node* PCB_tail;
    int quene_long;
}PCB_quene;

void quene_init(PCB_quene **que){
    *que = (PCB_quene*)malloc(sizeof(PCB_quene));
    (*que)->PCB_head = NULL;
    (*que)->PCB_tail = NULL;
    (*que)->quene_long = 0;
}//初始化鏈表,我發現我已經不知道怎麼寫了,網上找的,至於爲何用二級指針,有可能爲了操作統一,還需加深理解

void PCB_enter(PCB_quene* que, PCB progress)
{
    if(que == NULL)
        return ;
    PCB_node* pTemp = (PCB_node*)malloc(sizeof(PCB_node));
    pTemp->a_progress_pcb = progress;
    pTemp->next = NULL;

    if(que->PCB_head==NULL){
        que->PCB_head = pTemp;
    }else{
        que->PCB_tail->next = pTemp;
    }
    que->PCB_tail = pTemp;
    que->quene_long++;
}


PCB PCB_leave(PCB_quene* que)
{
    PCB error;
    error.arrive_time=-1;
    error.leave_time=-1;
    error.priority=-1;
    strcpy(error.progress_name,"error");
    error.progress_state=-1;
    error.serve_time=-1;
    error.wait_time=-1;
    if(que == NULL || que->quene_long == 0)
        return error;
    PCB_node* pDel = NULL;
    PCB res;
    pDel = que->PCB_head;
    que->PCB_head = que->PCB_head->next;
    res = pDel->a_progress_pcb;
    free(pDel);
    que->quene_long--;
 
    if(que->quene_long == 0)
        que->PCB_tail = NULL;
    return res;
}

int init_a_pcb(PCB static_list[]){
    strcpy(static_list[0].progress_name, "A");
    static_list[0].arrive_time=7;
    static_list[0].priority=-1;
    static_list[0].serve_time=7;
    static_list[0].leave_time=-1;
    static_list[0].wait_time=-1;
    static_list[0].progress_state=1;
    strcpy(static_list[1].progress_name, "B");
    static_list[1].arrive_time=2;
    static_list[1].priority=-1;
    static_list[1].serve_time=6;
    static_list[1].leave_time=-1;
    static_list[1].wait_time=-1;
    static_list[1].progress_state=1;
    
    strcpy(static_list[2].progress_name, "C");
    static_list[2].arrive_time=3;
    static_list[2].priority=-1;
    static_list[2].serve_time=5;
    static_list[2].leave_time=-1;
    static_list[2].wait_time=-1;
    static_list[2].progress_state=1;
    
    strcpy(static_list[3].progress_name, "D");
    static_list[3].arrive_time=4;
    static_list[3].priority=-1;
    static_list[3].serve_time=1;
    static_list[3].leave_time=-1;
    static_list[3].wait_time=-1;
    static_list[3].progress_state=1;
    return 4;
}

/*操作函數表*/
int menu(void);
void output(PCB pcblist[],int num);
PCB_quene* time_paixu(PCB undolist[],int num);
void time_output(PCB_quene* list);
void FCFS_method(PCB_quene* pcb_quene);
void SJE_method(PCB_quene* pcb_quene);
void yxj_paixu(PCB_quene* npx);
void refresh_timeyxj(PCB_quene* pcb_quene);
void HRRN_method(PCB_quene* pcb_quene);


int main(int argc, const char * argv[]) {
    PCB undolist[20],new_pcb;
    PCB_quene* pcb_quene;
    int flag=1,option,input_flag=0;//標誌位f和操作位
    int pcb_num=0,pcb_num_count;//進程數量和進程計數器
    while(flag){
        option=menu();
        if(option==1){
            printf("請輸入進程個數:");
            scanf("%d",&pcb_num);
            for(pcb_num_count=0;pcb_num_count<pcb_num;pcb_num_count++){
                printf("請輸入[%d]號進程名:",pcb_num_count);scanf("%s",new_pcb.progress_name);
                printf("請輸入[%d]號進程到達時間:",pcb_num_count);scanf("%d",&new_pcb.arrive_time);
                printf("請輸入[%d]號進程服務時間:",pcb_num_count);scanf("%d",&new_pcb.serve_time);
                //printf("請輸入[%d]號進程優先級:",pcb_num_count);scanf("%d",&new_pcb.priority);
                printf("\n\n");
                new_pcb.wait_time=-1;//當前的新pcb等待時間和離開時間都定爲未知
                new_pcb.leave_time=-1;
                new_pcb.progress_state=0;
                //狀態定爲就緒狀態,就緒爲1,運行爲2,完成爲3,0爲未到達,進入就緒隊列後爲1,在運行中爲0,運行結束後爲3
                new_pcb.priority=-1;//在此處優先級不考慮,爲未知,具體是多少根據方法而改變
                undolist[pcb_num_count]=new_pcb;
                input_flag=1;
            }
        }
        else if(option==7){
            flag=0;
        }
        else if(option==2){
            if(input_flag==0)
                pcb_num=init_a_pcb(undolist);
            output(undolist, pcb_num);
        }
        else if(option==6){
            if(input_flag==0)
                pcb_num=init_a_pcb(undolist);
            pcb_quene=time_paixu(undolist, pcb_num);
            time_output(pcb_quene);
            //yxj_paixu(pcb_quene);
        }
        else if (option==3){
            if(input_flag==0)
                pcb_num=init_a_pcb(undolist);
            pcb_quene=time_paixu(undolist, pcb_num);
            FCFS_method(pcb_quene);
        }else if(option==4){
            if(input_flag==0)
                pcb_num=init_a_pcb(undolist);
            pcb_quene=time_paixu(undolist, pcb_num);
            refresh_timeyxj(pcb_quene);//進行優先級刷新,當然是根據服務時間
            SJE_method(pcb_quene);
        }else if(option==5){
            if(input_flag==0)
                pcb_num=init_a_pcb(undolist);
            pcb_quene=time_paixu(undolist, pcb_num);
            HRRN_method(pcb_quene);
        }
    }
    
    
    return 0;
}
int menu(void){//用於顯示操作菜單與返回操作數
    printf("請選擇進程操作:\n");
    printf("1.輸入進程信息\n");
    printf("2.顯示進程信息\n");
    printf("3.先來先服務調度\n");
    printf("4.短作業優先調度\n");
    printf("5.高響應比優先調度\n");
    printf("6.時間順序輸出進程\n");
    printf("7.退出\n");
    int flag;
    scanf("%d",&flag);
    printf("\n\n");
    return flag;
}
void output(PCB pcblist[],int num){//用於輸出
    int i;
    if(num==0){
        printf("當前系統無進程");
        return;
    }
    for(i=0;i<num;i++){
        printf("第%d號進程的進程名爲:%s。\n",i,pcblist[i].progress_name);
        printf("第%d號進程的到達時間爲:%d。\n",i,pcblist[i].arrive_time);
        printf("第%d號進程的服務時間爲:%d。\n",i,pcblist[i].serve_time);
        if(pcblist[i].priority!=-1)
            printf("第%d號進程的優先級爲:%d。\n",i,pcblist[i].priority);
        else
            printf("暫時沒有設定優先級。");
        printf("\n\n");
    }
}
PCB_quene* time_paixu(PCB undolist[],int num){//需要進行重指針保存,時間排序,這裏是返回一個時間x上的先後隊列,用於表示先來後到
    PCB_quene *endquene;
    quene_init(&endquene);
    int i,j,minum;
    PCB min,chen;
    for(i=0;i<num;i++){
        min = undolist[i];
        minum=i;
        for(j=i+1;j<num;j++){
            if(min.arrive_time>undolist[j].arrive_time){
                min = undolist[j];
                minum = j;
            }
        }
        chen = undolist[i];
        undolist[i] = min;
        undolist[minum] = chen;
    }
    
    for(i=0;i<num;i++){
        PCB_enter(endquene, undolist[i]);
    }
    /*
    PCB_node *head = endquene->PCB_head;
    while(head!=NULL){
        PCB a = head->a_progress_pcb;
        printf("%d",a.arrive_time);
        head = head->next;
    } 用於進行測試,輸出一個鏈表,這裏已經將鏈表進行時間先來後到進行排序並會返回,用於進行先來先服務的操作*/
    
    return endquene;//返回排好序的鏈表,即進程按時序到達的先後關係,即先來後到的關係,由於出入隊列的操作,實際上可以自由轉化
}
void yxj_paixu(PCB_quene* npx){
    PCB undolist[20];
    int i,n = npx->quene_long,j,minum;
    for(i = 0;i<n;i++){
        undolist[i]=PCB_leave(npx);
    }
    
    PCB max,chen;
    for(i=0;i<n;i++){
        max = undolist[i];
        minum=i;
        for(j=i+1;j<n;j++){
            if(max.priority<undolist[j].priority){
                max = undolist[j];
                minum = j;
            }
        }
        chen = undolist[i];
        undolist[i] = max;
        undolist[minum] = chen;
    }
    /*for(i=0;i<n;i++){
        printf("%s ",undolist[i].progress_name);
        printf("%d\n",undolist[i].priority);
    }用於進行查錯檢驗,目前查到問題,由於排序機制導致的*/
    for(i=0;i<n;i++){
        PCB_enter(npx, undolist[i]);
    }
    
}



void time_output(PCB_quene* list){//好了,先不考慮鏈表的問題,先用
    PCB_node *head = list->PCB_head;
    int i=0;
    while(head!=NULL){
        PCB a = head->a_progress_pcb;
        printf("%d號",i);
        i++;
        printf("進程名爲:%s。\n",a.progress_name);
        printf("到達時間爲:%d。\n",a.arrive_time);
        printf("服務時間爲:%d。\n",a.serve_time);
        if(a.priority!=-1)
            printf("優先級爲:%d。\n",a.priority);
        else
            printf("暫時沒有設定優先級");
        printf("\n\n");
        head = head->next;
    }
}
void FCFS_method(PCB_quene* pcb_quene){
    time_clock=0;//時間軸必須置0,爲當前時間,上一個的離開減去當前的到達
    int wait;
    int run_time;
    int run_time_sum=0;
    double quan_run_time_sum=0;
    PCB_node *head = pcb_quene->PCB_head;
    PCB per_node;
    while(head!=NULL){
        sleep(1);
        per_node = head->a_progress_pcb;
        wait = time_clock-per_node.arrive_time;//time_clock記錄上一個離開
        if(wait<0){
            per_node.wait_time=0;
        }else{
            per_node.wait_time=wait;
        }
        time_clock=per_node.arrive_time+per_node.serve_time+per_node.wait_time;//離開時間爲到達時間加上等待時間加上服務時間
        run_time=per_node.serve_time+per_node.wait_time;
        run_time_sum =run_time_sum+run_time;
        quan_run_time_sum =quan_run_time_sum+(run_time*1.0)/per_node.serve_time;
        per_node.leave_time=time_clock;
        printf("進程:%s\n",per_node.progress_name);
        printf("到達時間:%d\n",per_node.arrive_time);
        printf("服務時間:%d\n",per_node.serve_time);
        printf("離開時間:%d\n",per_node.leave_time);
        printf("等待時間:%d\n",per_node.wait_time);
        printf("週轉時間:%d\n",run_time);
        printf("帶權週轉時間:%lf\n",(run_time*1.0)/per_node.serve_time);
        printf("\n\n");
        head = head->next;
    }
    printf("平均週轉時間爲:%.3lf\n",(run_time_sum*1.0)/pcb_quene->quene_long);
    printf("帶權平均週轉時間爲:%.3lf\n",quan_run_time_sum/pcb_quene->quene_long);
    printf("\n\n");
}

void SJE_method(PCB_quene* pcb_quene){
    int n = pcb_quene->quene_long;
    int run_time;
    int run_time_sum=0;
    double quan_run_time_sum=0;
    time_clock=0;//時間z軸置0
    PCB_node *head;
    PCB_quene* ready_quene;//就緒隊列
    quene_init(&ready_quene);
    PCB runing_pcb,lve;
    int wait;
    while(1){
        sleep(1);
        if(ready_quene->quene_long==0){
            PCB_enter(ready_quene, PCB_leave(pcb_quene));//如果就緒隊列沒有PCB,那麼將從時間管道內請求一個
        }//否則的話要向就緒隊列中請求一個來運行
        runing_pcb = PCB_leave(ready_quene);//向隊頭請求一個,獲得其進程,將先求等待時間等一系例時間
        runing_pcb.progress_state=2;
        wait = time_clock-runing_pcb.arrive_time;//time_clock記錄上一個離開
        if(wait<0){
            runing_pcb.wait_time=0;
        }else{
            runing_pcb.wait_time=wait;
        }
        time_clock=runing_pcb.arrive_time+runing_pcb.serve_time+runing_pcb.wait_time;//計算出當前進程的離開時間
        
        run_time=runing_pcb.serve_time+runing_pcb.wait_time;
        run_time_sum =run_time_sum+run_time;
        quan_run_time_sum =quan_run_time_sum+(run_time*1.0)/runing_pcb.serve_time;
        runing_pcb.leave_time=time_clock;
        //printf("%d",time_clock);
        head=pcb_quene->PCB_head;
        while(head!=NULL){
            if(head->a_progress_pcb.arrive_time<=time_clock){
                lve =head->a_progress_pcb;
                lve.progress_state=1;
                //printf("%s",lve.progress_name);
                PCB_enter(ready_quene, lve);
                PCB_leave(pcb_quene);
                head=pcb_quene->PCB_head;
            }else
                head = head->next;
        }printf("\n");
        //更新隊列
        yxj_paixu(ready_quene);//按照優先級排序
        //printf("%s %d %d\n",runing_pcb.progress_name,runing_pcb.arrive_time,runing_pcb.priority);
        printf("進程:%s\n",runing_pcb.progress_name);
        printf("優先級:%d\n",runing_pcb.priority);
        printf("到達時間:%d\n",runing_pcb.arrive_time);
        printf("服務時間:%d\n",runing_pcb.serve_time);
        printf("離開時間:%d\n",runing_pcb.leave_time);
        printf("等待時間:%d\n",runing_pcb.wait_time);
        printf("週轉時間:%d\n",run_time);
        printf("帶權週轉時間:%lf\n",(run_time*1.0)/runing_pcb.serve_time);
        printf("\n\n");
        runing_pcb.progress_state=3;
        if(ready_quene->quene_long==0&&pcb_quene->quene_long==0){
            break;
        }
    }
    printf("平均週轉時間爲:%.3lf\n",(run_time_sum*1.0)/n);
    printf("帶權平均週轉時間爲:%.3lf\n",quan_run_time_sum/n);
    printf("\n\n");
}

void refresh_timeyxj(PCB_quene* pcb_quene){
    PCB_node* head = pcb_quene->PCB_head;
    while(head!=NULL){
        head->a_progress_pcb.priority = 100-head->a_progress_pcb.serve_time;
        head=head->next;
    }
}
void HRRN_method(PCB_quene* pcb_quene){
    int n = pcb_quene->quene_long;
    int run_time;
    int run_time_sum=0;
    double quan_run_time_sum=0;
    time_clock=0;//時間z軸置0
    PCB_node *head,*head1;
    PCB_quene* ready_quene;
    quene_init(&ready_quene);
    PCB runing_pcb,lve,ls;
    int wait;
    while(1){
        sleep(1);
        if(ready_quene->quene_long==0){
            ls=PCB_leave(pcb_quene);
            ls.priority=1;
            PCB_enter(ready_quene, ls);//如果就緒隊列沒有PCB,那麼將從時間管道內請求一個
        }//否則的話要向就緒隊列中請求一個來運行
        runing_pcb = PCB_leave(ready_quene);//向隊頭請求一個,獲得其進程,將先求等待時間等一系例時間
        runing_pcb.progress_state=2;
        wait = time_clock-runing_pcb.arrive_time;//time_clock記錄上一個離開
        if(wait<0){
            runing_pcb.wait_time=0;
        }else{
            runing_pcb.wait_time=wait;
        }//實際等待時間刷新
        time_clock=runing_pcb.arrive_time+runing_pcb.serve_time+runing_pcb.wait_time;//計算出當前進程的離開時間
        
        run_time=runing_pcb.serve_time+runing_pcb.wait_time;
        run_time_sum =run_time_sum+run_time;
        quan_run_time_sum =quan_run_time_sum+(run_time*1.0)/runing_pcb.serve_time;
        runing_pcb.leave_time=time_clock;
        //printf("%d",time_clock);
        head=pcb_quene->PCB_head;
        while(head!=NULL){
            if(head->a_progress_pcb.arrive_time<=time_clock){
                lve =head->a_progress_pcb;
                lve.progress_state=1;
                lve.wait_time=time_clock-lve.arrive_time;
                lve.priority=(lve.wait_time+lve.serve_time)/lve.serve_time;
                //printf("%s",lve.progress_name);
                PCB_enter(ready_quene, lve);
                PCB_leave(pcb_quene);
                head=pcb_quene->PCB_head;
            }else
                head = head->next;
        }
        head1=ready_quene->PCB_head;
        while(head1!=NULL){
            
            head1->a_progress_pcb.wait_time=time_clock-head1->a_progress_pcb.arrive_time;
            head1->a_progress_pcb.priority=(head1->a_progress_pcb.wait_time+head1->a_progress_pcb.serve_time)/head1->a_progress_pcb.serve_time;
            head1=head1->next;
        }
        //更新隊列
        yxj_paixu(ready_quene);//按照優先級排序
        //printf("%s %d %d\n",runing_pcb.progress_name,runing_pcb.arrive_time,runing_pcb.priority);
        printf("進程:%s\n",runing_pcb.progress_name);
        printf("優先級:%d\n",runing_pcb.priority);
        printf("到達時間:%d\n",runing_pcb.arrive_time);
        printf("服務時間:%d\n",runing_pcb.serve_time);
        printf("離開時間:%d\n",runing_pcb.leave_time);
        printf("等待時間:%d\n",runing_pcb.wait_time);
        printf("週轉時間:%d\n",run_time);
        printf("帶權週轉時間:%lf\n",(run_time*1.0)/runing_pcb.serve_time);
        printf("\n\n");
        runing_pcb.progress_state=3;
        if(ready_quene->quene_long==0&&pcb_quene->quene_long==0){
            break;
        }
    }
    printf("平均週轉時間爲:%.3lf\n",(run_time_sum*1.0)/n);
    printf("帶權平均週轉時間爲:%.3lf\n",quan_run_time_sum/n);
    printf("\n\n");
}

 

 

五.總結

        說白了這個實驗很簡單,比去年的編譯原理簡單很多,起碼我知道怎麼寫。雖然原來很簡單但是我仍然想給大家分享一下,講一講,因爲這樣不僅能幫助的一些和我一樣在苦逼學習的同學,還能讓我自己進行一次梳理與分析,比如這次我就分析出了很多內在的問題:碼風不好,胡亂命名;思維不縝密,邏輯錯誤經常發生;代碼不簡潔,耗時長;基礎不紮實,鏈表操作等基礎知識忘得快,以及其他問題。這些問題都是相當嚴重的,一定要進行改正。並且我發現我相當長的時間沒有寫博客了,這也不太好。總之意識到應該及時改正,我發現很多東西都要學,越早越好,大家一起努力吧。聯繫信息在下面,歡迎指正我的錯誤並一起探討。

 

 

郵箱:[email protected]

QQ:894274006

雲杉木屋

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章