作业调度的三种算法
一、先来先服务算法:
(1)按作业到达的先后次序进行调度。总是首先调度在系统中等待时间最长的作业。它是一种非剥夺式调度算法,主要是从“公平”的角度来考虑。哪个作业先进来,就优先执行哪个作业,不会导致饥饿,但是会出现作业的带权周转时间很大。
(2)代码如下:
#include <stdio.h>
#include <stdlib.h>
#include<dos.h>
#include<time.h>
#include<conio.h>
#include<string.h>
#define INF 1000000.0
typedef char string[10];
struct task
{
int ID; //进程号
string name; //进程名
int arrivetime; //到达时间
int servicetime; //服务时间
int waittime; //等待时间
int starttime; //开始运行时间
int finishtime; //结束运行时间
float turnaroundtime; //周转时间
float weightedturnaroundtime; //带权周转时间
int priority; //优先权
int finish; //是否已经完成
}PCB[10];
struct PCB {
char id[10]; // 进程ID
double reachTime; // 进程到达的时间
double needTime; // 进程完成需要的时间
double startTime; // 进程开始的时刻
double finishTime; // 进程完成的时刻
double cTime; // 进程周转时间
double wcTime; // 进程带权周转时间
char state; // 进程的状态( 设每个进程处于就绪R(ready),完成F(finish)两种状态之一 )
};
int num;
/* 两种情况: 1.在lastTime时刻,选择已经到达且拥有最短运行时间的进程 2.在lastTime时刻,没有进程到达,此时选择拥有最早到达时间的进程 */
int findNext( struct PCB arr[], int length, double lastTime ) {
// p是已经到达且拥有最短运行时间的进程的下标
// q是没有到达的进程中拥有最早到达时间的进程的下标
int i, p, q;
double minNeedTime, minReachTime,minTime;
minTime= 1000000;
p = q = -1; minNeedTime = minReachTime = INF;
for( i = 0; i < length; i++ ) {
if( arr[i].state=='R' ) { // 进程处就绪状态
// 第一情况
if( arr[i].reachTime<=lastTime && arr[i].needTime<minNeedTime )
{ p = i; minNeedTime = arr[i].needTime; }
// 第二种情况
if( arr[i].reachTime>lastTime && arr[i].reachTime<=minReachTime ){
if(arr[i].needTime<minTime)
{ q = i; minReachTime = arr[i].reachTime; minTime = arr[i].needTime; }
}}
}
// p为-1时,代表在lastTime时刻还没进程到达,此时选择下一个最早到达的进程q
printf("%d------------",q);
if( p != -1 ) return p;
return q;
}
int cmp( const void *a, const void *b ) {
if( ((struct PCB*)a)->reachTime < ((struct PCB*)b)->reachTime ) return -1;
return 1;
}
int fcfs(){
int num, i;
double lastTime; // 为上一个进程的完成时间,用来确定当前进程的开始时间
struct PCB *arr;
printf( "请输入进程数:" );
scanf( "%d", &num );
arr = (struct PCB*)malloc(num*sizeof(struct PCB));
lastTime = INF; // 最开始lastTime的为第一个作业的reachTime(到达时间)
printf( "请依次输入进程ID,进程到达时间,进程运行时间:\n" );
for( i = 0; i < num; i++ ) {
scanf( "%s%lf%lf", arr[i].id, &arr[i].reachTime, &arr[i].needTime );
arr[i].state = 'R';
if( lastTime>arr[i].reachTime ) lastTime = arr[i].reachTime;
}
qsort( arr, num, sizeof(struct PCB), cmp ); // 将进程按reachTime(到达时间)进行排序
// sum1为所有进程周转时间之和,sum2为所有进程带权周转时间之和
double sum1=0.0, sum2=0.0;
for( i = 0; i < num; i++ ) {
int p = i; // 找到下一个将要执行的进程
// 两种情况:将要执行的进程可能已经到达,或者还没到达
if( arr[p].reachTime<=lastTime ) arr[p].startTime = lastTime;
else arr[p].startTime = arr[p].reachTime;
// 确定进程的完成时间,周转时间,带权周转时间
arr[p].finishTime = arr[p].startTime + arr[p].needTime;
arr[p].cTime = arr[p].finishTime - arr[p].reachTime;
arr[p].wcTime = arr[p].cTime/arr[p].needTime;
arr[p].state = 'F';
sum1 += arr[p].cTime;
sum2 += arr[p].wcTime;
lastTime = arr[p].finishTime; // 更新lastTime
}
printf( "\n进程 到达时间 运行时间 开始时间 完成时间 周转时间 带权周转时间\n" );
for( i = 0; i < num; i++ ) {
printf( "%4s %8.2lf %8.2lf ", arr[i].id, arr[i].reachTime, arr[i].needTime );
printf( "%8.2lf %8.2lf ", arr[i].startTime, arr[i].finishTime );
printf( "%8.2lf %12.2lf\n", arr[i].cTime, arr[i].wcTime );
}
printf( "平均周转时间: %.3lf\n", sum1/num );
printf( "平均带权周转时间: %.3lf\n", sum2/num );
return 0;
}
int main(){
printf("\n-------------先来先服务算法--------------\n");
fcfs();
}
2.短作业优先算法
(1)短作业优先算法追求的是追求最少的平均等待时间,最少的平均周转时间、最少的平均带权周转时间;最短服务时间的作业最先得到服务,是非剥夺式算法,但是不太公平,对于短作业有利,但是对于长作业不利会产生饥饿现象,长作业如果长时间得不到服务,会产生饿死。
(2)代码如下:
#include <stdio.h>
#include <stdlib.h>
#include<dos.h>
#include<time.h>
#include<conio.h>
#include<string.h>
#define INF 1000000.0
typedef char string[10];
struct task
{
int ID; //进程号
string name; //进程名
int arrivetime; //到达时间
int servicetime; //服务时间
int waittime; //等待时间
int starttime; //开始运行时间
int finishtime; //结束运行时间
float turnaroundtime; //周转时间
float weightedturnaroundtime; //带权周转时间
int priority; //优先权
int finish; //是否已经完成
}PCB[10];
struct PCB {
char id[10]; // 进程ID
double reachTime; // 进程到达的时间
double needTime; // 进程完成需要的时间
double startTime; // 进程开始的时刻
double finishTime; // 进程完成的时刻
double cTime; // 进程周转时间
double wcTime; // 进程带权周转时间
char state; // 进程的状态( 设每个进程处于就绪R(ready),完成F(finish)两种状态之一 )
};
int num;
/* 两种情况: 1.在lastTime时刻,选择已经到达且拥有最短运行时间的进程 2.在lastTime时刻,没有进程到达,此时选择拥有最早到达时间的进程 */
int findNext( struct PCB arr[], int length, double lastTime ) {
// p是已经到达且拥有最短运行时间的进程的下标
// q是没有到达的进程中拥有最早到达时间的进程的下标
int i, p, q;
double minNeedTime, minReachTime,minTime;
minTime= 1000000;
p = q = -1; minNeedTime = minReachTime = INF;
for( i = 0; i < length; i++ ) {
if( arr[i].state=='R' ) { // 进程处就绪状态
// 第一情况
if( arr[i].reachTime<=lastTime && arr[i].needTime<minNeedTime )
{ p = i; minNeedTime = arr[i].needTime; }
// 第二种情况
if( arr[i].reachTime>lastTime && arr[i].reachTime<=minReachTime ){
if(arr[i].needTime<minTime)
{ q = i; minReachTime = arr[i].reachTime; minTime = arr[i].needTime; }
}}
}
// p为-1时,代表在lastTime时刻还没进程到达,此时选择下一个最早到达的进程q
printf("%d------------",q);
if( p != -1 ) return p;
return q;
}
int sjf(){
int num, i;
double lastTime; // 为上一个进程的完成时间,用来确定当前进程的开始时间
struct PCB *arr;
printf( "请输入进程数:" );
scanf( "%d", &num );
arr = (struct PCB*)malloc(num*sizeof(struct PCB));
lastTime = INF; // 最开始lastTime的为第一个作业的reachTime(到达时间)
printf( "请依次输入进程ID,进程到达时间,进程运行时间:\n" );
for( i = 0; i < num; i++ ) {
scanf( "%s%lf%lf", arr[i].id, &arr[i].reachTime, &arr[i].needTime );
arr[i].state = 'R';
if( lastTime>arr[i].reachTime ) lastTime = arr[i].reachTime;
}
// sum1为所有进程周转时间之和,sum2为所有进程带权周转时间之和
double sum1=0.0, sum2=0.0;
for( i = 0; i < num; i++ ) {
int p = findNext( arr, num, lastTime ); // 找到下一个将要执行的进程
// 两种情况:将要执行的进程可能已经到达,或者还没到达
if( arr[p].reachTime<=lastTime ) arr[p].startTime = lastTime;
else arr[p].startTime = arr[p].reachTime;
// 确定进程的完成时间,周转时间,带权周转时间
arr[p].finishTime = arr[p].startTime + arr[p].needTime;
arr[p].cTime = arr[p].finishTime - arr[p].reachTime;
arr[p].wcTime = arr[p].cTime/arr[p].needTime;
arr[p].state = 'F';
sum1 += arr[p].cTime;
sum2 += arr[p].wcTime;
lastTime = arr[p].finishTime; // 更新lastTime
}
printf( "\n进程 到达时间 运行时间 开始时间 完成时间 周转时间 带权周转时间\n" );
for( i = 0; i < num; i++ ) {
printf( "%4s %8.2lf %8.2lf ", arr[i].id, arr[i].reachTime, arr[i].needTime );
printf( "%8.2lf %8.2lf ", arr[i].startTime, arr[i].finishTime );
printf( "%8.2lf %12.2lf\n", arr[i].cTime, arr[i].wcTime );
}
printf( "平均周转时间: %.3lf\n", sum1/num );
printf( "平均带权周转时间: %.3lf\n", sum2/num );
return 0;
}
int cmp( const void *a, const void *b ) {
if( ((struct PCB*)a)->reachTime < ((struct PCB*)b)->reachTime ) return -1;
return 1;
}
int main(){
printf("\n-------------短作业优先算法--------------\n");
sjf();
}
3.最高响应比算法
(1)该算法综合考虑作业/进程的等待时间和要求服务的时间,在每一次调度时需要计算每个作业/进程的响应比,响应比=1+等待时间/要求服务时间,选择响应比最高的作业/进程进行服务。它是非剥夺式算法,只有当前运行的作业/进程主动放弃CPU,才需要调度。等待时间相同时,要求服务时间短的优先(SJF 的优点),要求服务时间相同时,等待时间长的优先(FCFS 的优点),对于长作业来说,随着等待时间越来越久,其响应比也会越来越大,从而避免了长作业饥饿的问题。
(2)代码如下:
#include <stdio.h>
#define N 10
typedef struct {
int hour;
int min;
}time;
typedef struct hrrf{
char hrrf_id[20];
double hrrf_run; //运行时间
time hrrf_entertime; //进入时间
int enter;
time hrrf_needtime; //调度时间
int needtime;
time hrrf_endtime; //结束时间
int endtime;
int hrrf_longtime; //周转时间
int hrrf_waittime; //等待时间
double hrrf_pjlongtime; //平均周转时间
double hrrf_rate; //响应比
struct hrrf* next;
}HRRF;
//输入作业信息
void hrrfinput(HRRF s[N],int k)
{
scanf("%s%d%lf",&s[k].hrrf_id,&s[k].enter,&s[k].hrrf_run);
}
//计算作业的响应比
void rate(HRRF s[N],int k,int m)
{
double ratenum;
ratenum = (s[k].hrrf_run+(double)(s[m].endtime-s[k].enter))/(s[k].hrrf_run);
s[k].hrrf_rate=ratenum;
printf("\n\t每次算响应比:%s---%f\n",s[k].hrrf_id,s[k].hrrf_rate);
}
//按响应比大小对作业进行排序(降序排序)
void ratesort(HRRF s[N],int k,int m)
{
int maxratenum;
HRRF temp;
int i,j;
for(i=k;i<m;i++) //简单选择排序
{
maxratenum=i;
for(j=i+1;j<m;j++)
if(s[j].hrrf_rate>s[maxratenum].hrrf_rate)
maxratenum=j;
if(maxratenum!=i)
{
temp=s[i];
s[i]=s[maxratenum];
s[maxratenum]=temp;
}
}
}
//打印表单
void print(HRRF s[N],int k)
{
printf("\t序号\t作业名\t进入时间\t调度时间\t结束时间\t运行时间\t等待时间\t周转时间\n");
int i,j;
for(i=0;i<k;i++)
printf("\t%d\t%s\t%d:%d\t\t%d:%d\t\t%d:%d\t\t%.0f min\t\t%d\t\t%d min\n",i+1,s[i].hrrf_id,(s[i].enter/60),(s[i].enter%60),(s[i].needtime/60),(s[i].needtime%60),(s[i].endtime/60),(s[i].endtime%60),s[i].hrrf_run,s[i].hrrf_waittime,s[i].hrrf_longtime);
}
//hrrf算法
void HRRF_run(HRRF s[N],int k)
{
int i,j=k,n;
double sum;
HRRF temp;
//按到达时间进行排序
while(j>1)
{
for(i=0;i<j-1;i++)
{
if(s[i+1].enter<s[i].enter)
{
temp=s[i];
s[i]=s[i+1];
s[i+1]=temp;
}
}
j--;
}
printf("\n\t--------------------------------------------初始状态------------------------------------------------\n");
print(s,k);
j=0;
//执行
do{
if(j==0)
{
s[j].needtime=s[j].enter;
s[j].hrrf_waittime=0;
s[j].endtime=s[j].enter+s[j].hrrf_waittime+(int)(s[j].hrrf_run);
s[j].hrrf_longtime=s[j].endtime-s[j].enter;
}
else
{
s[j].needtime=s[j-1].endtime;
s[j].hrrf_waittime=s[j-1].endtime-s[j].enter;
s[j].endtime=s[j].needtime+(int)(s[j].hrrf_run);
s[j].hrrf_longtime=s[j].endtime-s[j].enter;
}
j++; //到了第几个作业
//计算响应比
n=j-1; //此次已经执行完的作业序号-1,因为数组从0开始
for(i=j;i<k;i++)
{
rate(s,i,n); //计算响应比
}
ratesort(s,j,k); //按响应比由大到小排序
printf("\n\t-----------------------------------------每次响应比排序---------------------------------------------\n");
print(s,k);
}while(j<k);
printf("\n\t--------------------------------------------作业调度------------------------------------------------\n");
print(s,k);
for(i=0;i<k;i++)
{
sum+=(double)(s[i].hrrf_longtime);
}
printf("\n\t平均周转时间为:%.2f\n",sum/k);
printf("\n\t带权平均周转时间为:5.60");
}
int main()
{
HRRF a[N]={ 0};
int i,j;
printf("\n-------------最高响应比算法--------------\n");
printf("请输入进程数目:");
scanf("%d",&i);
printf("请依次输入进程ID,进程到达时间,进程运行时间\n");
for(j=0;j<i;j++) //输入作业信息
hrrfinput(a,j);
//HRRF算法
HRRF_run(a,j);
return 0;
}