本進程調度算法採用最高優先數優先的調度算法(即把處理機分配給優先數最高的進程)。
通過模擬調度進程的PCB塊來調度進程。進程的PCB塊包含以下四方面的內容:
a) 進程標示符
b) 處理及狀態信息
c) 進程調度信息
d) 進程控制信息
進程在運行中存在三種基本狀態,分別是運行狀態、就緒狀態和阻塞狀態。
主要的功能函數有:
排序函數:對就緒隊列裏面的進程根據優先級進行排序。
阻塞函數:阻塞就緒隊列中(從前往後)優先級相同的連續進程。
喚醒函數:將阻塞隊列中優先數最大且剩餘運行時間最小的進程調入就緒鏈表首位。
運行函數:就是運行!
程序運行截圖:
輸入進程信息
運行的第一次
運行的第14次(就緒隊列與阻塞隊列均存在進程)
程序運行結束
代碼已經加好了很細緻的註釋,在gcc環境下可直接運行:
#include <stdio.h>
#include <stdlib.h>
#define getpch(type) (type*)malloc(sizeof(type))
//#define NULL 0
//定義PCB結構體
struct pcb
{
char name[10]; //進程名稱
int state; //進程狀態
int super; //優先級
int ntime; //總運行時間
int rtime; //已運行時間
struct pcb* link; //下一PCB的地址
} //定義3個pcb類型指針,其中:
*ready=NULL, //……"ready" 代表就緒隊列鏈表首位地址
*p, //……"p" 代表當前正在運行的pcb地址
*bhead=NULL; //……"bhead" 代表阻塞鏈表的首位地址
//把結構體pcb定義成一個數據類型:PCB
typedef struct pcb PCB;
//排序函數:對就緒隊列裏面的進程根據優先級進行排序
void sort()
{
PCB *first,*second; //沒什麼好說的
int insert=0; //該值用於標記在排序過程中是否發生過插隊情況
//如果[就緒鏈表首項爲空]或[當前進程的優先數高於鏈表首項進程的優先數]
if ((ready==NULL)||((p->super)>(ready->super)))
{
//將當前進程置於就緒鏈表首位
p->link=ready;
ready=p;
}
else //否則嘛 ^_^
{
//取出鏈表中的排在最前兩項,分別爲first,second
first=ready;
second=first->link;
//循環,直到second所指向的地址爲空時才停止
while(second!=NULL)
{
//如果p的優先級大於second
if((p->super)>(second->super))
{
//將p插於first和second之間
p->link=second;
first->link=p;
second=NULL; //設置second爲空(這是循環結束的條件)
insert=1; //將的值"insert"設置爲1
}
else //否則 0.0
{
//first和second所指的地址向鏈表後移位
//舉個栗子:循環的第一次,first和second分別指向鏈表的第1位和第2位;那麼第二次循環則分別指向鏈表的第2位和第3位
first=first->link;second=second->link;
}
}
//如果沒有發生過插隊情況
if(insert==0) {
first->link=p; //將p排在first之後(注:此時循環已經結束,再沒有發生過插隊的情況下,first即是隊尾)
}
}
}
//阻塞函數:阻塞就緒隊列中(從前往後)優先級相同的連續進程
void block(){
PCB *pb,*index=ready;
int count=0;
if(ready==NULL)return; //如果就緒隊列中沒有進程,則終止阻塞操作(都沒有進程我還阻塞個啥?)
//循環,遍歷整個就緒鏈表
while((index->link!=NULL))
{
/**
* 這裏需要細緻寫一下:
* 一個進程是否被阻塞,其主要依據是優先數.即有連續多個優先數相等的進程排列在一起時就會發生阻塞.
* 這個循環所進行的工作是循環遍歷就緒鏈表的每一項,通過依次與就序列表的首項進程的優先數比較,判斷出優先數是否發生減小.
* 發生減小則代表連續的序列發生中斷,則跳出循環.
* 此時pcb類型的指針"index"已指向[優先數相等進程的序列]的最後一項.
*/
if((index->link->super)>=ready->super){
index->link->state='B'; //將進程狀態標記爲'B',即"block".
index=index->link; //用以鏈表循環的循環指針向後移一位.
count++; //每檢測到可阻塞進程時自增計數
}else{
break;
}
}
//判斷當就緒鏈表中可阻塞進程大於0時
if(count>0){
/**
* 這裏還是需要詳細註解:
* 通過循環找出了[優先數相等進程的序列]後需要將這條子鏈表從就緒鏈表中取出,(注:這個子鏈表是從就緒鏈表的第二項開始的,如果都阻塞了你運行啥去?)
* 再將這個子鏈表加入阻塞鏈表隊首(這裏之所以加到隊首而不是隊尾,是爲了節省尋找隊尾時產生的循環的開銷)
* 就緒鏈表此時由於被取走一段子鏈表,需要將原先子鏈表首尾部分重新連接.
*/
pb=index->link;
index->link=bhead;
bhead=ready->link;
ready->link=pb;
}
}
//喚醒函數:將阻塞隊列中優先數最大且剩餘運行時間最小的進程調入就緒鏈表首位
void weakup(){
PCB *index,*prior,*t; //"index"和"prior"的作用請參考[sort]排序函數中的"first"和"second"
//"t"的作用是用於兩個指針調換位置時使用的中間變量.
int exchange=0; //該值用於標記是否需要進行位置調換操作
if(bhead==NULL)return; //如果就阻塞列中沒有進程,則終止喚醒操作(都沒有進程我還喚醒個啥?)
//如果阻塞鏈表不止一個進程
if(bhead->link!=NULL)
{
index=bhead->link;
prior=bhead;
//循環,遍歷整個阻塞鏈表
while(index!=NULL)
{
/**
* 雖然寫備註快寫吐了,但這裏還是需要詳細描述一下:
* 喚醒阻塞鏈表中的哪一個進程,其判斷依據有兩點:
* 1.優先數在阻塞鏈表中最判斷大;
* 2.當優先數最大的進程不唯一時,判斷進程的剩餘運行時間,剩餘運行時間少的進程優先執行.
* 這裏採用的是一次循環,所以在循環時每當找到比之前進程更符合條件的進程時都將該進程與阻塞鏈表首尾的進程調換位置.
* 這樣我們再循環結束時就可知道,阻塞鏈表首位的進程是應該喚醒的進程.
*/
if((index->super)>(bhead->super))
{
exchange=1; //標記需要進行位置調換操作
}
else if((index->super)==(bhead->super))
{
if((index->ntime-index->rtime)<(bhead->ntime-bhead->rtime))
{
exchange=1; //標記需要進行位置調換操作
}
}
if(exchange==1){
//與阻塞鏈表首位進程調換位置
prior->link=bhead;
t=bhead->link;
bhead->link=index->link;
index->link=t;
bhead=index;
//將標記變量exchange重新標記爲0
exchange=0;
}
prior=index; //"prior"向後移動一位(這裏之所以不使用"prior=prior->link"是因爲執行交換位置操作時,prior的後繼地址將不爲index)
index=index->link; //"index"向後移動一位
}
}
//將阻塞鏈表首位的進程調到就緒列表首位
t=bhead->link;
bhead->link=ready;
ready=bhead;
bhead=t;
}
//錄入進程信息
void input()
{
int i,num;
printf("\n請輸入進程數量:");
scanf("%d",&num);
//循環錄入每個進程的信息
for (i=0;i<num;i++)
{
//賦予每個進程初始屬性值
printf("\n[請設置 %d號 進程的基本屬性]\n",i+1);
p=getpch(PCB); //getpch(type)是宏定義函數,使用malloc函數根據數據類型,動態開闢內存空間
printf("\n 名稱:");
scanf("%s",p->name);
printf("\n 優先級:");
scanf("%d",&p->super);
printf("\n 所需時間:");
scanf("%d",&p->ntime);
printf("\n");
p->rtime=0;
p->state='W'; //'W'等待狀態,'R'運行狀態,'B'阻塞狀態
p->link=NULL;
sort(); //執行排序
}
}
//獲取鏈表長度(不用看,沒啥o用)
int space()
{
int l=0;PCB*pr=ready;
while(pr!=NULL)
{
l++;
pr=pr->link;
}
return(l);
}
//顯示列名(不用看,沒啥o用)
void show()
{
printf("\n名稱\t狀態\t優先數\t總時長\t已用時\n");
}
//顯示指定進程的信息
void disp(PCB*pr)
{
printf("%s\t",pr->name);
printf("%c\t",pr->state);
printf("%d\t",pr->super);
printf("%d\t",pr->ntime);
printf("%d\t",pr->rtime);
printf("\n");
}
//顯示當前正在運行的進程和就緒隊列內進程的信息
void check()
{
PCB *pr,*pb;
printf("\n====[當前運行中的進程]====\n");
printf("進程名稱:%s\n",p->name);
show(); //顯示列名
disp(p);//顯示當前正在運行的進程信息
pr=ready;
if(pr==NULL){
printf("\n====[當前就緒隊列爲空]====\n");
}
else
{
printf("\n====[當前就緒隊列狀態]====");
show();
while(pr!=NULL)
{
disp(pr);
pr=pr->link;
}
}
pb=bhead;
if(pb==NULL){
printf("\n====[當前阻塞隊列爲空]====\n");
}
else
{
printf("\n====[當前阻塞隊列狀態]====");
show();
while(pb!=NULL)
{
disp(pb);
pb=pb->link;
}
}
}
//銷燬函數:銷燬當前正在運行的進程
void destroy()
{
printf("\n進程[%s]已完成.\n",p->name);
free(p); //free函數用於釋放內存空間
}
//運行函數
void running()
{
(p->rtime)++; //當前進程的已運行時間自增
//如果進程實際運行時間已經等於了預設的總時長
if(p->rtime==p->ntime)
{
destroy(); //辦掉他!
}
else //否則啊 @.@
{
(p->super)--; //進程優先數減一
p->state='W'; //狀態標記爲等待
sort(); //就緒隊列重新排序
//如果阻塞鏈表不爲空則喚醒阻塞進程
if(bhead!=NULL){
weakup();
}
block(); //執行阻塞操作
}
}
//主函數
void main()
{
int len,h=0;
char ch;
input(); //錄入進程信息
len=space(); //獲取鏈表長度(沒啥o用)
//只要鏈表長度不等於0且鏈表首項不爲空就一直循環下去
while((len!=0)&&(ready!=NULL))
{
//system("cls");
ch=getchar();
h++;
printf("\n\n[第%d次運行]\n",h);
//取就緒鏈表的首項進程,轉爲正在進行狀態
p=ready;
ready=p->link;
p->link=NULL;
p->state='R';
check(); //顯示當前運行狀態信息
running(); //運行
printf("\n按任一鍵繼續.....");
ch=getchar();
}
printf("\n\n進程已經完成.\n");
ch=getchar();
}