【操作系统 - 1】先来先服务FCFS和短作业优先SJF进程调度算法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Houchaoqun_XMU/article/details/55539362

操作系统系列

  学习至此,发现很多学了但很久没用的知识,久而久之,慢慢遗忘。等哪天还需要的话,却发现已经忘得差不多了,即使整理了文档(word等),还是得从头再学一遍。读研第一学期,发现很多东西都可以从博客上学习到,也有不少博主呕心沥血整理了挺多有用的博文。于是,本人借此契机,也慢慢开始整理一些博文,不断改进完善中。整理博文(IT)有如下目的:

  • 首要目的:记录“求学生涯”的所学所悟,不断修改,不断更新!(有读者的互动)
  • 其次目的在这“开源”的时代,整理并分享所学所悟是一种互利的行为!

博文系列:操作系统课程的相关实验

6个实验相关的代码下载地址:http://download.csdn.net/detail/houchaoqun_xmu/9865648

-------------------------------

先来先服务FCFS和短作业优先SJF进程调度算法

一、概念介绍和案例解析

  FCFS调度算法

  先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用FCFS算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。

  • 案例剖析:
  FCFS算法比较有利于长作业(进程),而不利于短作业(进程)。下表列出了A、B、C、D四个作业分别到达系统的时间、要求服务的时间、开始执行的时间及各自的完成时间,并计算出各自的周转时间和带权周转时间。

  从表上可以看出,其中短作业C的带权周转时间竞高达100,这是不能容忍的;而长作业D的带权周转时间仅为1.99。据此可知,FCFS调度算法有利于CPU繁忙型的作业,而不利于I/O繁忙型的作业(进程)。
  CPU繁忙型作业是指该类作业需要大量的CPU时间进行计算,而很少请求I/O。通常的科学计算便属于CPU繁忙型作业。
  I/O繁忙型作业是指CPU进行处理时需频繁地请求I/O。目前的大多数事务处理都属于I/O繁忙型作业。
  • 案例剖析2:采用FCFS调度算法时的调度性能

  上图表示出有五个进程A、B、C、D、E,它们到达的时间分别是0、1、2、3和4,所要求的服务时间分别是4、3、5、2和4,其完成时间分别是4、7、12、14和18。从每个进程的完成时间中减去其到达时间,即得到其周转时间,进而可以算出每个进程的带权周转时间。
  • SJF调度算法:
  短作业(进程)优先调度算法SJ(P)F,是指对短作业或短进程优先调度的算法。它们可以分别用于作业调度和进程调度。短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。
  为了和FCFS调度算法进行比较,我们仍利用FCFS算法中所使用的实例,并改用SJ(P)F算法重新调度,再进行性能分析。由上图中的(a)和(b)可以看出,采用SJ(P)F算法后,不论是平均周转时间还是平均带权周转时间,都有较明显的改善,尤其是对短作业D,其周转时间由原来的(用FCFS算法时)11降为3;而平均带权周转时间是从5.5降到1.5。这说明SJF调度算法能有效地降低作业的平均等待时间,提高系统吞吐量。
  SJ(P)F调度算法也存在不容忽视的缺点:
  1. 该算法对长作业不利,如作业C的周转时间由10增至16,其带权周转时间由2增至3.1。更严重的是,如果有一长作业(进程)进入系统的后备队列(就绪队列),由于调度程序总是优先调度那些(即使是后进来的)短作业(进程),将导致长作业(进程)长期不被调度。
  2. 该算法完全未考虑作业的紧迫程度,因而不能保证紧迫性作业(进程)会被及时处理。
  3. 由于作业(进程)的长短只是根据用户所提供的估计执行时间而定的,而用户又可能会有意或无意地缩短其作业的估计运行时间,致使该算法不一定能真正做到短作业优先调度。

二、实验介绍

  • 问题描述:

  设计程序模拟进程的先来先服务FCFS和短作业优先SJF调度过程。假设有n个进程分别在T1,… ,Tn时刻到达系统,它们需要的服务时间分别为S1,… ,Sn。分别采用先来先服务FCFS和短作业优先SJF进程调度算法进行调度,计算每个进程的完成时间、周转时间和带权周转时间,并且统计n个进程的平均周转时间和平均带权周转时间。

  • 程序要求:

---- 进程个数n;每个进程的到达时间T1, … ,Tn和服务时间S1, … ,Sn;选择算法1-FCFS,2-SJF。

---- 要求采用先来先服务FCFS和短作业优先SJF分别调度进程运行,计算每个进程的周转时间和带权周转时间,并且计算所有进程的平均周转时间和带权平均周转时间。

---- 输出:要求模拟整个调度过程,输出每个时刻的进程运行状态,如“时刻3:进程B开始运行”等等。

---- 输出:要求输出计算出来的每个进程的周转时间、带权周转时间、所有进程的平均周转时间以及带权平均周转时间。

三、程序设计和开发

  • 程序设计:
  1. Initial()进行初始化。
  2. input()对到达时间和服务时间进行输入。
  3. get_firstProcess()获得第一个进程,FCFS和SJF算法的实现相同。
  4. FCFS()对算法进行处理。
  5. SJF()对算法进行处理。
  6. choose_Algorithm();对实现算法的类别进行选择:具有容错性特征。
  • FCFS:
void FCFS()
{
	/*
		1. 找到最先到达的进程的坐标,并计算相关信息
		2. 依次找到接下去到达的进程
	*/

	int startWorkTime = 0;  //表示开始执行时间 = 当前进程之前的所有服务时间之和
	int first = get_firstProcess();  //获得第一个进程

	isFinished_FCFS[first] = true;
	FinishTime[first] = ArrivalTime[first] + ServiceTime[first];
	startWorkTime += ServiceTime[first];   //下一个进程的开始执行时间
	WholeTime[first] = FinishTime[first] - ArrivalTime[first];   //周转时间 = 完成时间 - 到达时间
	WeightWholeTime[first] = WholeTime[first]/ServiceTime[first]; //带权周转时间 = 周转时间/服务时间

	//接下去的进程
	int nextProcess = n; //初始化下一个进程的下标超出界限

	for (int i=1;i<n;i++)
	{
		nextProcess = n;  //每次对下一个进程的下标进行更新
		for (int j=0;j<n;j++)
		{
			if (!isFinished_FCFS[j])  //表示当前进程还未完成相关信息的计算
			{
				if (ArrivalTime[j]<=startWorkTime)  //满足到达时间小于等于开始执行时间的情况下
				{
					if (nextProcess==n)
					{
						nextProcess = j;
					}
					else
					{
						if (ArrivalTime[nextProcess]>ArrivalTime[j])  //筛选出最先到达的进程
						{
							nextProcess=j;    //获得当前进程中:最先到达的进程
						}
					}
				}
			}
		}//for(j)
		//获得当前需要处理的进程nextProcess后,对相关信息进行计算
		isFinished_FCFS[nextProcess] = true;
		FinishTime[nextProcess] = ServiceTime[nextProcess] + startWorkTime;
		startWorkTime += ServiceTime[nextProcess];  //获得下一个进程对应的“开始执行时间”
		WholeTime[nextProcess] = FinishTime[nextProcess] - ArrivalTime[nextProcess];
		WeightWholeTime[nextProcess] = (double)WholeTime[nextProcess]/ServiceTime[nextProcess];

	}//for(i)

	//计算平均周转时间和平均带权周转时间
	double totalWT = 0;
	double totalWWT = 0;
	for (int i=0;i<n;i++)
	{
		totalWT+=WholeTime[i];
		totalWWT+=WeightWholeTime[i];
	}
	AverageWT_FCFS = totalWT/n;
	AverageWWT_FCFS = totalWWT/n;

	//输出检测
	display();
	cout<<"平均周转时间="<<AverageWT_FCFS<<endl;
	cout<<"平均带权周转时间="<<AverageWWT_FCFS<<endl;
	cout<<"******************************************************"<<endl;
}
  • SJF:
void SJF()
{
	//与SCSF类似,相同的方法获得第一个进程
	int startWorkTime_SJF = 0;  //表示开始执行时间 = 当前进程之前的所有服务时间之和
	//第一个进程的处理
	int first = get_firstProcess();  //获得第一个进程

	isFinished_SJF[first] = true;
	FinishTime[first] = ArrivalTime[first] + ServiceTime[first];
	startWorkTime_SJF += ServiceTime[first];   //下一个进程的开始执行时间
	WholeTime[first] = FinishTime[first] - ArrivalTime[first];   //周转时间 = 完成时间 - 到达时间
	WeightWholeTime[first] = (double)WholeTime[first]/ServiceTime[first]; //带权周转时间 = 周转时间/服务时间

	//获得下一个进程的下标
	
	int nextProcess_SJF = n;
	for (int i=1;i<n;i++)
	{
		nextProcess_SJF = n;
		for (int j=0;j<n;j++)
		{
			if (!isFinished_SJF[j])
			{
				if (ArrivalTime[j]<=startWorkTime_SJF)
				{
					if (nextProcess_SJF==n)
					{
						nextProcess_SJF = j;
					}
					else
					{
						if (ServiceTime[nextProcess_SJF]>ServiceTime[j])
						{
							nextProcess_SJF = j;   //获得运行时间最短的作业的下标
						}
					}
				}
			}
		}//for(j)
		
		//对获得的进程进行处理
		isFinished_SJF[nextProcess_SJF] = true;
		FinishTime[nextProcess_SJF] = ServiceTime[nextProcess_SJF] + startWorkTime_SJF;
		startWorkTime_SJF += ServiceTime[nextProcess_SJF]; 
		WholeTime[nextProcess_SJF] = FinishTime[nextProcess_SJF] - ArrivalTime[nextProcess_SJF];
		WeightWholeTime[nextProcess_SJF] = (double)WholeTime[nextProcess_SJF]/ServiceTime[nextProcess_SJF];

	}//for(i)

	double totalWT = 0;
	double totalWWT = 0;
	for (int i=0;i<n;i++)
	{
		totalWT+=WholeTime[i];
		totalWWT+=WeightWholeTime[i];
	}
	AverageWT_SJF = totalWT/n;
	AverageWWT_SJF = totalWWT/n;

	//输出检测
	display();
	cout<<"平均周转时间="<<AverageWT_SJF<<endl;
	cout<<"平均带权周转时间="<<AverageWWT_SJF<<endl;
	cout<<"******************************************************"<<endl;
}

四、实验结果分析

-- 输入数据:
-- 进程数n = 5
-- 到达时间:0 1 2 3 4
-- 服务时间:4 3 5 2 4
  • FCFS算法:

  • SJF算法:

五、实验源码

// 操作系统_实验一.cpp : 定义控制台应用程序的入口点。
//
/*
//实验题目:先来先服务FCFS和短作业优先SJF进程调度算法
*******概念*******
1. 先来先服务FCFS:
2. 短作业优先SJF:
3. 高级调度:根据某种算法,在外存中把处于后备队列中的那些作业调入内存,当作业完成时做善后处理
4. 中级调度
5. 低级调度:对象是进程(或内核级线程);三个基本机制:排队器、分派器、上下文切换机制
6. 调度方式和算法的若干准则
	1)面向用户的准则:周期时间短、响应时间快、截止时间的保证、优先权准则
	2)面向系统的准则:系统吞吐量高、处理机利用率好、各类资源的平衡利用
7. 调度算法:根据系统的资源分配策略所规定的资源分配算法
8. FCFS调度算法有利于CPU繁忙型的作业,而不利于I/O繁忙型的作业或进程

*******实验要求*********
1. 先来先服务调度算法FCFS:
	1)是一种最简单的调度算法,适用于作业调度和进程调度
	2)每次调度都是从后备队列中选择一个或者多个最先进入该队列的作业,将它们调入内存,分配资源,创建进程,然后放入就绪队列
	3)FCFS算法比较有利于长作业(进程),不利于短作业(进程)
	4)既可用于作业调度,也可用于进程调度
2. 周转时间 = 完成时间 - 到达时间
   带权周转时间 = 周转时间/服务时间

*/

#include <iostream>
#include <iomanip>
using namespace std;

#define MaxNum 100

//typedef struct
//{
//	int  ArrivalTime[MaxNum];  //到达时间
//	int  ServiceTime[MaxNum];  //服务时间
//	int  FinishTime[MaxNum];   //完成时间
//
//
//}Process;

/* 算法思想:
   1. Initial()进行初始化
   2. input()对到达时间和服务时间进行输入
   3. get_firstProcess()获得第一个进程,FCFS和SJF算法的实现相同
   4. FCFS()对算法进行处理
   5. SJF()对算法进行处理
   6. choose_Algorithm();对实现算法的类别进行选择:具有容错性特征
*/

//相同的数组下标对应同一个进程的信息
int  ArrivalTime[MaxNum];  //到达时间
int  ServiceTime[MaxNum];  //服务时间
int  FinishTime[MaxNum];   //完成时间
int  WholeTime[MaxNum];    //周转时间
double  WeightWholeTime[MaxNum];      //带权周转时间
double AverageWT_FCFS,AverageWT_SJF;  //FCFS算法的平均周转时间,SJF算法的平均周转时间
double AverageWWT_FCFS,AverageWWT_SJF; //FCFS算法的平均带权周转时间,SJF算法的平均带权周转时间

bool isFinished_FCFS[MaxNum];
bool isFinished_SJF[MaxNum];

static int n;

void Initial()  //确定进程个数后再初始化
{
	cout<<"请输入作业(进程)个数n=";
	cin>>n;

	for (int i=0;i<n;i++)
	{
		ArrivalTime[i] = 0;
		ServiceTime[i] = 0;
		FinishTime[i] = 0;
		WholeTime[i] = 0;
		WeightWholeTime[i] = 0;
		AverageWT_FCFS = 0;
		AverageWT_SJF = 0;
		AverageWWT_FCFS = 0;
		AverageWWT_SJF = 0;
		isFinished_FCFS[i] = false;
		isFinished_SJF[i] = false;
	}
}

void input()
{
	cout<<"请分别输入每个进程的到达时间:"<<endl;
	for (int i=0;i<n;i++)
	{
		cin>>ArrivalTime[i];
	}

	cout<<"请分别输入每个进程的服务时间:"<<endl;
	for (int i=0;i<n;i++)
	{
		cin>>ServiceTime[i];
	}

	//输出用户输入的信息
	cout<<"******************************************************"<<endl;
	cout<<"用户输入的进程个数n="<<n<<endl;

	cout<<"用户输入的服务时间分别为:"<<endl;
	for (int i=0;i<n;i++)
	{
		cout<<ArrivalTime[i]<<" ";
	}
	cout<<endl;

	cout<<"用户输入的服务时间分别为:"<<endl;
	for (int i=0;i<n;i++)
	{
		cout<<ServiceTime[i]<<" ";
	}
	cout<<endl<<"******************************************************"<<endl;
}

int get_firstProcess()
{
	int first = MaxNum;
	for (int i=0;i<n;i++)
	{
		if (ArrivalTime[i]<=ArrivalTime[first])
		{
			first = i;
		}
	}
	return first;
}

void display()
{
	cout<<"******************************************************"<<endl;
	cout<<"进程相关信息如下:"<<endl;
	cout<<setw(10)<<"进程名(ID)"<<" ";
	cout<<setw(10)<<"到达时间"<<" ";
	cout<<setw(10)<<"服务时间"<<" ";
	cout<<setw(10)<<"完成时间"<<" ";
	cout<<setw(10)<<"周转时间"<<" ";
	cout<<setw(10)<<"带权周转时间"<<endl;
	for (int i = 0;i<n;i++)
	{
		cout<<setw(10)<<i+1<<" ";
		cout<<setw(10)<<ArrivalTime[i]<<" ";
		cout<<setw(10)<<ServiceTime[i]<<" ";
		cout<<setw(10)<<FinishTime[i]<<" ";
		cout<<setw(10)<<WholeTime[i]<<" ";
		cout<<setw(10)<<WeightWholeTime[i]<<" "<<endl;
	}
}

void FCFS()
{
	/*
		1. 找到最先到达的进程的坐标,并计算相关信息
		2. 依次找到接下去到达的进程
	*/

	int startWorkTime = 0;  //表示开始执行时间 = 当前进程之前的所有服务时间之和
	int first = get_firstProcess();  //获得第一个进程

	isFinished_FCFS[first] = true;
	FinishTime[first] = ArrivalTime[first] + ServiceTime[first];
	startWorkTime += ServiceTime[first];   //下一个进程的开始执行时间
	WholeTime[first] = FinishTime[first] - ArrivalTime[first];   //周转时间 = 完成时间 - 到达时间
	WeightWholeTime[first] = WholeTime[first]/ServiceTime[first]; //带权周转时间 = 周转时间/服务时间

	//接下去的进程
	int nextProcess = n; //初始化下一个进程的下标超出界限

	for (int i=1;i<n;i++)
	{
		nextProcess = n;  //每次对下一个进程的下标进行更新
		for (int j=0;j<n;j++)
		{
			if (!isFinished_FCFS[j])  //表示当前进程还未完成相关信息的计算
			{
				if (ArrivalTime[j]<=startWorkTime)  //满足到达时间小于等于开始执行时间的情况下
				{
					if (nextProcess==n)
					{
						nextProcess = j;
					}
					else
					{
						if (ArrivalTime[nextProcess]>ArrivalTime[j])  //筛选出最先到达的进程
						{
							nextProcess=j;    //获得当前进程中:最先到达的进程
						}
					}
				}
			}
		}//for(j)
		//获得当前需要处理的进程nextProcess后,对相关信息进行计算
		isFinished_FCFS[nextProcess] = true;
		FinishTime[nextProcess] = ServiceTime[nextProcess] + startWorkTime;
		startWorkTime += ServiceTime[nextProcess];  //获得下一个进程对应的“开始执行时间”
		WholeTime[nextProcess] = FinishTime[nextProcess] - ArrivalTime[nextProcess];
		WeightWholeTime[nextProcess] = (double)WholeTime[nextProcess]/ServiceTime[nextProcess];

	}//for(i)

	//计算平均周转时间和平均带权周转时间
	double totalWT = 0;
	double totalWWT = 0;
	for (int i=0;i<n;i++)
	{
		totalWT+=WholeTime[i];
		totalWWT+=WeightWholeTime[i];
	}
	AverageWT_FCFS = totalWT/n;
	AverageWWT_FCFS = totalWWT/n;

	//输出检测
	display();
	cout<<"平均周转时间="<<AverageWT_FCFS<<endl;
	cout<<"平均带权周转时间="<<AverageWWT_FCFS<<endl;
	cout<<"******************************************************"<<endl;
}

void SJF()
{
	//与SCSF类似,相同的方法获得第一个进程
	int startWorkTime_SJF = 0;  //表示开始执行时间 = 当前进程之前的所有服务时间之和
	//第一个进程的处理
	int first = get_firstProcess();  //获得第一个进程

	isFinished_SJF[first] = true;
	FinishTime[first] = ArrivalTime[first] + ServiceTime[first];
	startWorkTime_SJF += ServiceTime[first];   //下一个进程的开始执行时间
	WholeTime[first] = FinishTime[first] - ArrivalTime[first];   //周转时间 = 完成时间 - 到达时间
	WeightWholeTime[first] = (double)WholeTime[first]/ServiceTime[first]; //带权周转时间 = 周转时间/服务时间

	//获得下一个进程的下标
	
	int nextProcess_SJF = n;
	for (int i=1;i<n;i++)
	{
		nextProcess_SJF = n;
		for (int j=0;j<n;j++)
		{
			if (!isFinished_SJF[j])
			{
				if (ArrivalTime[j]<=startWorkTime_SJF)
				{
					if (nextProcess_SJF==n)
					{
						nextProcess_SJF = j;
					}
					else
					{
						if (ServiceTime[nextProcess_SJF]>ServiceTime[j])
						{
							nextProcess_SJF = j;   //获得运行时间最短的作业的下标
						}
					}
				}
			}
		}//for(j)
		
		//对获得的进程进行处理
		isFinished_SJF[nextProcess_SJF] = true;
		FinishTime[nextProcess_SJF] = ServiceTime[nextProcess_SJF] + startWorkTime_SJF;
		startWorkTime_SJF += ServiceTime[nextProcess_SJF]; 
		WholeTime[nextProcess_SJF] = FinishTime[nextProcess_SJF] - ArrivalTime[nextProcess_SJF];
		WeightWholeTime[nextProcess_SJF] = (double)WholeTime[nextProcess_SJF]/ServiceTime[nextProcess_SJF];

	}//for(i)

	double totalWT = 0;
	double totalWWT = 0;
	for (int i=0;i<n;i++)
	{
		totalWT+=WholeTime[i];
		totalWWT+=WeightWholeTime[i];
	}
	AverageWT_SJF = totalWT/n;
	AverageWWT_SJF = totalWWT/n;

	//输出检测
	display();
	cout<<"平均周转时间="<<AverageWT_SJF<<endl;
	cout<<"平均带权周转时间="<<AverageWWT_SJF<<endl;
	cout<<"******************************************************"<<endl;
}

void choose_Algorithm()
{
	cout<<"请选择算法“1-FCFS,2-SJF”"<<endl;
	int choose;
	cin>>choose;
	if (choose==1)
	{
		FCFS();
	}
		else if(choose==2)
		{
			SJF();
		}
	else
	{
		cout<<"请输入正确的选择“1-FCFS,2-SJF”"<<endl;
		cout<<"******************************************************"<<endl;
		choose_Algorithm();  //递归调用,实现排除错误的选择也可以继续输入
	}
}



int main()
{
	Initial();
	input();
	choose_Algorithm();
	system("pause");
	return 0;
}


展开阅读全文

没有更多推荐了,返回首页