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

【实验目的】

通过这次实验,加深对进程概念的理解,进一步掌握进程状态的转变、进程调度的策略及对系统性能的评价方法。

实验内容

问题描述:

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

程序要求:

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

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

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

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

实验要求:

  1. 上机前认真复习FCFS和SJF进程调度调度算法,熟悉进程调度的执行过程;
  2. 上机时独立编程、调试程序;
  3. 根据具体实验要求,完成好实验报告(包括实验的目的、内容、要求、源程序、实例运行结果截图、发现的问题以及解决方法)。

需求分析

  1. 输入的形式和输入值的范围
  • 输入形式:
  1. 程序会提示“请输入作业个数(输入0时退出程序)”,然后等待用户输入一个整数,代表作业的个数。
  2. 程序会提示用户输入每个作业的id、到达时间和服务时间,分别对应着作业的标识符、作业到达的时间点和作业需要的服务时间。
  3. 程序会要求用户选择要使用的调度算法:1 代表先来先服务(FCFS),2 代表短作业优先(SJF),输入其他值会退出程序。
  • 输入值的范围:
  1. 作业个数(k)的取值范围:非负整数,包括 0。
  2. 每个作业的id:字符型,用来标识作业,可以是任意字符。
  3. 到达时间(ArrivalTime):非负整数,代表作业到达的时间点。
  4. 服务时间(ServiceTime):非负整数,代表作业需要的服务时间。
  5. 选择的算法(n):1 或 2,分别对应先来先服务和短作业优先算法。
  1. 输出的形式

该程序的输出形式是以文字提示和表格的形式呈现作业的执行过程和最终的调度结果。输出形式包括以下部分:

  1. 作业开始执行提示:在每个作业开始执行时,会输出类似于 "时刻X, 作业Y开始执行" 的提示,其中 X 代表时间,Y 代表作业的标识符。
  2. 作业完成执行提示:在每个作业完成执行时,会输出类似于 "作业Y在时刻Z完成执行" 的提示,其中 Y 代表作业的标识符,Z 代表作业完成执行的时间点。
  3. 作业执行情况表格:最终会以表格的形式输出每个作业的信息,包括作业的id、到达时间、服务时间、开始时间、完成时间、周转时间和带权周转时间。
  4. 平均周转时间和带权平均周转时间:最后会输出平均周转时间和带权平均周转时间的数值。
  1. 程序所能达到的功能

该程序能够模拟先来先服务和短作业优先两种调度算法,并输出作业的执行过程和相关的调度结果,该程序可以实现以下功能:

  1. 输入作业信息:用户可以输入作业的数量,以及每个作业的标识符、到达时间和服务时间。
  2. 先来先服务(FCFS)调度算法:若用户选择算法1,程序会按照先来先服务(FCFS)算法对作业进行调度。在执行过程中,会输出作业开始执行和完成执行的提示,以及相应的执行时间点。最后,会输出每个作业的执行情况表格和平均周转时间、带权平均周转时间的计算结果。
  3. 按照短作业优先(SJF)调度算法:同样地,若用户选择算法2,程序也可以按照短作业优先(SJF)算法对作业进行调度。在执行过程中,同样会输出作业开始执行和完成执行的提示,以及相应的执行时间点。最后,会输出每个作业的执行情况表格和平均周转时间、带权平均周转时间的计算结果。
  4. 循环输入:程序设计了一个循环,允许用户反复输入作业信息并选择调度算法,直到用户输入作业个数为0时退出程序,同时用户输入错误信息时退出程序

概要设计

  1. 抽象数据类型定义

JOB类:包括作业id、到达时间、服务时间、开始执行时间、完成时间、周转时间、带权周转时间、进程是否已执行等成员变量,这个类表示了作业的基本信息以及与作业相关的计算结果。

  1. 主程序流程

在main函数中,首先用户可以输入作业的数量和每个作业的id、到达时间和服务时间。然后用户需要选择要使用的调度算法,1代表先来先服务(FCFS),2代表最短作业优先(SJF)。根据用户选择的算法,调用相应的函数进行作业调度,如果用户输入了0或者选择的算法序号错误,则退出程序。

  1. 程序模块之间的层次(调用)关系
  1. 主函数模块:

main():

通过键盘输入作业个数、作业id、到达时间和服务时间,选择要使用的调度算法调用 FCFS()或SJF()函数。

  1. 调度算法模块:
  • FCFS(JOB jobs[], int num):

首先调用工具函数对作业按照到达时间进行排序,遍历并执行作业,计算计算每一个作业执行的开始时间、完成时间、周转时间和带权周转时间,输出每个作业的执行情况以及平均周转时间、带权平均周转时间。

  • SJF(JOB jobs[], int num):

首先调用工具函数对作业按照到达时间进行排序,选择服务时间最短且未执行的作业执行,计算每一个作业执行的开始时间、完成时间、周转时间和带权周转时间,输出每个作业的执行情况以及平均周转时间、带权平均周转时间。

  1. 工具函数模块:

sort(JOB jobs[], int num):

对作业按照到达时间从小到大排序,用于调度算法模块中对作业的排序。

  1. 作业类模块:

class JOB:

定义了作业的属性,如进程id、到达时间、服务时间、开始执行时间、完成时间、周转时间、带权周转时间等。 用于存储作业信息,在调度算法模块中被使用。

主函数模块通过键盘输入作业信息和选择调度算法,然后调用不同的调度算法模块,调度算法模块中使用工具函数模块实现对作业的排序,计算作业的执行情况和统计平均周转时间、带权平均周转时间。

程序模块之间的层次关系如下:

main()

|

|--- FCFS(jobs, num)

     |--- sort(jobs, num)

|--SJF(jobs,num)

     |--- sort(jobs, num)

详细设计

  1. sort函数:用于对作业按照到达时间从小到大进行排序,确保后续调度算法能够按照作业到达的先后顺序进行处理。使用冒泡排序算法对JOB数组进行排序,按照ArrivalTime属性的升序进行排序。
  2. FCFS函数(先来先服务调度算法):首先调用sort函数对作业按照到达时间的升序进行排序。接着依次遍历排序后的作业列表,根据作业到达的顺序执行作业,并计算作业的执行情况和结果。current_time 表示当前的时间,初始值为0,对于每个作业,记录其开始执行时间、完成时间:
  3. SJF函数(最短作业优先调度算法): 同样,首先调用sort函数对作业按照到达时间进行排序。然后遍历排序后的作业列表,在每个时间点选择已到达、剩余服务时间最短且未执行的作业进行执行。对于每个作业,记录其开始执行时间、完成时间,并计算周转时间和带权周转时间(此部分与FCFS函数相同)。最后输出每个作业的执行情况以及计算得到的平均周转时间和带权平均周转时间等信息。

【调试分析】

1、调试过程中遇到的问题以及解决方法,设计与实现的回顾讨论和分析;

  1. 所有进程的开始时间均为0,带权周转时间出先负值。原因时在进程执行完成之后,没有更新此刻的时间,即将current_time设置为 jobs[i].FinishTime;

修改后

修改后输出正确结果:

  1. 短作业优先算法中,由于在每一个作业执行完成后没有设置执行完成的标志,所以导致每次找到的作业只是服务时间最短的作业,而没有考虑到作业是否已经执行,导致某些作业重复执行,某些作业无法执行,加上布尔类型的标志位bool finish,设置初始值为false,每次执行完一个作业以后将该作业的标志位设为true。

在找寻最短服务时间的作业和执行完作业均加上标志位的判断和修改。

修改后输出正确结果:

改进设想

  • 输入数据验证与异常处理:对于作业id、到达时间、服务时间的输入,也需要添加异常处理机制,确保输入的数据类型正确,并且对输入范围进行合理限制。
  • 模块化重构:将排序和调度逻辑分离成单独的函数,提高代码的可读性和可维护性,将作业的输入逻辑、算法选择逻辑等封装成单独的函数,使得main函数更加简洁清晰。
  • 算法改进:对于最短作业优先(SJF)算法,可以考虑加入抢占式调度,即当有更短的作业到达时,能够中断当前作业的执行,提高调度的灵活性。对于先来先服务(FCFS)算法,可以考虑加入对等待时间的计算,以及对于长作业和短作业交替出现时的调度优化
  1. 经验和体会。
  1. 算法实现经验:通过编写该程序,我对先来先服务和短作业优先调度算法有了更深入的理解。在实现过程中,遇到了一些具体的算法实现细节和技巧,例如对于有序序列的处理过程可以先对有序序列进行排序再处理,进一步巩固了排序算法的相关知识,编写该程序的过程中,遇到了一些逻辑错误、边界情况或者需要优化的地方,例如在短作业优先算法中对于到达了的程序是否已经执行需要设置一个标志,否则会造成某些作业重复执行,锻炼了我快速定位问题并进行修复的能力。对于以后的算法实现会很有帮助。
  2. 算法性能分析:对程序涉及的算法进行性能分析,我发现无论是否加入排序算法,时间和空间复杂度都不变,但是加入排序算法之后,使得算法执行过程更为简单,即如果当前进程可执行,不用再考虑之后的进程。在进行算法性能分析的同时,提出了一些改进设想,比如引入抢占式调度或动态调整作业优先级、程序模块化,这展示了我对算法优化和改进的思考能力。
  3. 编程实践:编写该程序提升了我设计算法逻辑和组织代码结构方面的能力。

【用户使用说明】

当运行这段程序时,首先会要求用户输入作业的个数,以及每个作业的id、到达时间和服务时间。接下来,需要选择要使用的调度算法,其中1代表先来先服务(FCFS),2代表短作业优先(SJF)。接下来,程序将根据用户的输入模拟作业调度过程,并输出调度结果。具体的操作步骤如下:

  1. 输入作业个数:程序会提示用户输入作业的个数,用户需要根据实际情况输入要处理的作业个数,然后按提示逐个输入每个作业的id、到达时间和服务时间,每输入一个数点击回车键。
  2. 选择调度算法:程序会要求用户输入希望使用的算法,1代表FCFS,2代表SJF。
  3. 根据用户的选择,程序将模拟作业调度过程:
  • 对于FCFS算法,程序会按照作业到达时间的顺序进行作业调度,对每个作业计算开始执行时间、完成时间、周转时间和带权周转时间,最终输出每个作业的调度详情以及平均周转时间和带权平均周转时间。
  • 对于SJF算法,程序会按照作业到达时间的顺序进行作业调度,但在选择下一个要执行的作业时,会优先选择服务时间最短且未执行的作业执行。最后会输出每个作业的调度详情以及平均周转时间和带权平均周转时间。

通过这些步骤,用户可以观察不同调度算法对作业执行顺序的影响,以及评估它们的性能表现。这样的模拟可以帮助用户更好地理解作业调度算法的工作原理和效果。

【测试结果】

#include<iostream>
using namespace std;
#define MaxNum 100
#define INF 0x7fffffff//int型变量的上界
class JOB {
public:
	char id=' ';//进程id
	int  ArrivalTime = 0;//到达时间(给出)
	int  ServiceTime = 0;//服务时间(给出)
	int StartTime = 0;//开始执行时间
	int  FinishTime = 0;//完成时间=服务时间+开始执行时间
	double  WholeTime = 0.0;//周转时间=完成时间-到达时间
	double  WeightWholeTime = 0.0;//带权周转时间=周转时间/服务时间
	bool finish = false;//进程是否已经执行
};
//把进程按照到达时间从小到大进行排序
void sort(JOB jobs[], int num)
{
	for (int i = 0; i < num - 1; i++) {
		for (int j = 0; j < num - i - 1; j++) {
			if (jobs[j].ArrivalTime > jobs[j + 1].ArrivalTime) {
				// 交换JOB[j]和JOB[j+1]
				JOB temp = jobs[j];
				jobs[j] = jobs[j + 1];
				jobs[j + 1] = temp;
			}
		}
	}
}
void FCFS(JOB jobs[], int num)
{
	double AverageWT_FCFS = 0.0, AverageWWT_FCFS = 0.0;//平均周转时间、带权平均周转时间
	sort(jobs, num);//按照到达时间进行排序
	int current_time = 0;//当前的时间
	for (int i = 0; i < num; i++)
	{
			jobs[i].StartTime = current_time;//开始执行时间为time
			cout << "时刻" << current_time << ",作业" << jobs[i].id << "开始执行" << endl;
			jobs[i].FinishTime = jobs[i].ServiceTime + jobs[i].StartTime;//完成时间=服务时间+开始执行时间
			jobs[i].WholeTime = jobs[i].FinishTime - jobs[i].ArrivalTime;//周转时间=完成时间-到达时间
			jobs[i].WeightWholeTime = jobs[i].WholeTime / jobs[i].ServiceTime;//带权周转时间=周转时间-服务时间
			current_time = jobs[i].FinishTime;
			cout << "作业" << jobs[i].id << "在" << current_time << "时刻完成执行" << endl;
	}
	cout << "作业id\t\t到达时间\t服务时间\t开始时间\t完成时间\t周转时间\t带权周转时间" << endl;
	//计算平均周转时间、带权平均周转时间
	for (int i = 0; i < num; i++)
	{
		AverageWT_FCFS += jobs[i].WholeTime;
		AverageWWT_FCFS += jobs[i].WeightWholeTime;
		cout << jobs[i].id << "\t\t " << jobs[i].ArrivalTime << "\t\t " << jobs[i].ServiceTime
			<< "\t\t " << jobs[i].StartTime << "\t\t " << jobs[i].FinishTime
			<< "\t\t " << jobs[i].WholeTime << "\t\t " << jobs[i].WeightWholeTime << endl;
	}
	AverageWT_FCFS = AverageWT_FCFS / num;
	AverageWWT_FCFS = AverageWWT_FCFS / num;
	cout << "平均周转时间为:" << AverageWT_FCFS << endl;
	cout << "带权平均周转时间为:" << AverageWWT_FCFS << endl;
}
void SJF(JOB jobs[], int num)
{
	double AverageWT_SJF = 0.0, AverageWWT_SJF = 0.0;//平均周转时间、带权平均周转时间
	int time = 0;//当前的时间
	sort(jobs, num);//按照到达时间进行排序
	for (int i = 0; i < num; i++)
	{
		int mintime = INF;
		int k = 0, j = 0;//k为要执行的进程
		while (j < num)
		{
			if (jobs[j].ArrivalTime > time)//排在前面的进程的到达时间晚于当前时间
				break;
			//找到已经到达了的服务时间最小且没有执行的进程
			if ((jobs[j].ServiceTime < mintime) && (!jobs[j].finish))
			{
				k = j;
				mintime = jobs[j].ServiceTime;
			}
			j++;
		}
		//执行作业k
		jobs[k].StartTime = time;//开始执行时间为time
		cout << "时刻" << time << ",作业" << jobs[i].id << "开始执行" << endl;
		jobs[k].FinishTime = jobs[k].ServiceTime + jobs[k].StartTime;//完成时间=服务时间+开始执行时间
		jobs[k].WholeTime = jobs[k].FinishTime - jobs[k].ArrivalTime;//周转时间=完成时间-到达时间
		jobs[k].WeightWholeTime = jobs[k].WholeTime / jobs[k].ServiceTime;//带权周转时间=周转时间-服务时间
		jobs[k].finish = true;//把作业设置为已执行状态
		time = jobs[k].FinishTime;
		cout << "作业" << jobs[k].id << "在" << time << "时刻完成执行" << endl;
	}
	cout << "作业id\t\t到达时间\t服务时间\t开始时间\t完成时间\t周转时间\t带权周转时间" << endl;
	//计算平均周转时间、带权平均周转时间
	for (int i = 0; i < num; i++)
	{
		AverageWT_SJF += jobs[i].WholeTime;
		AverageWWT_SJF += jobs[i].WeightWholeTime;
		cout << jobs[i].id << "\t\t " << jobs[i].ArrivalTime << "\t\t " << jobs[i].ServiceTime
			<< "\t\t " << jobs[i].StartTime << "\t\t " << jobs[i].FinishTime
			<< "\t\t " << jobs[i].WholeTime << "\t\t " << jobs[i].WeightWholeTime << endl;
	}
	AverageWT_SJF = AverageWT_SJF / num;
	AverageWWT_SJF = AverageWWT_SJF / num;
	cout << "平均周转时间为:" << AverageWT_SJF << endl;
	cout << "带权平均周转时间为:" << AverageWWT_SJF << endl;
}
int main()
{
	JOB jobs[MaxNum];
	int n, k;
	k = 4;
	while (true)
	{
		cout << "请输入作业个数(输入0时退出程序)";
		cin >> k;
		if (k==0)
		{
			cout << "已退出" << endl;
			break;
		}
		else if (k < 0)
		{
			cout << "输入数据错误,退出程序" << endl;
			return 0;
		}
		cout << "输入作业的id、到达时间、服务时间" << endl;
		for (int i = 0; i < k; i++)
		{
			cout << "作业" << i + 1 << "的id:";
			cin >> jobs[i].id;
			cout << "作业" << i + 1 << "到达时间:";
			cin >> jobs[i].ArrivalTime;
			cout << "作业" << i + 1 << "服务时间:";
			cin >> jobs[i].ServiceTime;
		}
		cout << "请输入要使用的算法:1:FCFS,2:SJF" << endl;
		cin >> n;
		if (n == 1)
			FCFS(jobs, k);
		else if (n == 2)
			SJF(jobs, k);
		else
		{
			cout << "输入数据错误,退出程序" << endl;
			return 0;
		}
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值