【操作系统】进程调度算法-FCFS-SJF-RR

目录

进程调度的概念 

引起进程调度的原因

进程调度算法

先来先服务算法(FCFS)

短作业优先算法(SJF)

时间片轮转算法(RR)

进程调度源码

程序源码 

运行结果


进程调度的概念 

    首先我们简洁明了的说, 进程调度就是决定内存中的就绪队列中的那个进程将获得处理机, 进程调度是操作系统中必不可少的一

种调度, 因为我们在电脑上运行的进程很多, 我们在运行进程的还会有新进程到来, 那么操作系统就会采用一系列的调度算法来为

进程分配cpu, 使各个进程都正常运行, 本文中我们说三种算法, 即先来先服务, 短作业(进程)优先,还有时间片轮转算法.

    注: 进程调度有抢占式和非抢占式, 我们说的是非抢占式, 有新的进程到来并不会暂停当前正在运行的进程.


引起进程调度的原因

    1. 正在执行的进程执行完毕

    2. 执行中的进程因为提出某种请求暂停执行

    3. 时间片完成

    4. 进程阻塞

    5. 到来了优先级高的进程


进程调度算法

先来先服务算法(FCFS)

    按照进程进入内存的时间进行先后排序选取下一个要执行的作业/进程, 先来到的先分配CPU

    具体思路:

        我们给所有进程按照到达时间进行排序, 然后依次进行执行(非抢占式), 执行完成之后, 如果当前有进程到来, 则执行下

一个进程,  如果没有下一个进程到来则等待, 更新当前系统执行的总时间, 把等待时间也要算进去.

    特点: 

        1. 容易实现, 但是效率不高

        2. 只考虑进程的等候时间, 没有考虑进程服务时间的长短, 对于短小的进程不利, 等待时间可能会远远大于服务时间.

短作业优先算法(SJF)

    对于已经到达的进程, 选取一个服务时间最短的进行调度.

    具体思路: 

        因为第一个到达的进程一定是先运行的, 所以我们还是按照到达时间进行排序, 这样有一个好处, 我们慢慢说, 首先开始

运行第一个进程, 然后下一个进程要运行的是已经到达的进程中服务时间最短的, 那我们就从第二个开始向后搜索, 如果当前

进程到了并且没有运行, 就把它的服务时间保存起来, 依次向后搜索, 最终找到已经到达的进程中服务时间最短的, 但是还有

一个情况, 同上面一样, 当前进程运行完成之后没有别的进程到来怎么办, 这个时候我们不用管后来的进程服务时间的长短了,

下一个肯定要运行即将到来的第一个进程, 那么这个时候我们用时间排序就派上用场了, 我们直接搜索第一个没有被运行的

进程即可, 因为已经到来的进程肯定已经运行完了, 所以不用管了, 这个时候同上面一样总时间就要变成下一个要到来的进程

的到达时间, 因为处理机在等待, 这个时间要加上, 然后直到把所有的进程运行完.

    特点: 

        1. 跟上面差不多, 容易实现, 但是效率不高

        2. 对于一个早来的但是服务时间很长的进程很不利, 很长时间内不能调度, 容易出现"饥饿"现象

时间片轮转算法(RR)

    时间片轮转算法就是给一个时间片, 每个进程只能运行这么长时间, 如果到了时间运行完了就从内存中的就绪队列中删除

如果时间片到了但是没有运行完, 就把这个进程挂到就绪队列队尾

    具体思路: 

        我们先给一个时间片, 然后跟上面一样, 第一个到达的进程肯定先运行, 所以我们还是先按照到达时间进行排序, 然后让

第一个进程开始运行, 将第一个进程push进就绪队列中, 然后我们还是记录当前系统运行的总时间, 在一个时间片之内, 如果

运行完成了就pop出队, 当然在运行的过程中还可能有别的进程进来, 所以我们要保存当前进程运行的总时间, 如果有进程到

来, 直接push入队, 如果当前进程没有运行完成并且就绪队列里面没有别的进程, 那就再分配时间片继续运行, 一直没有就一

直运行, 如果就绪队列里面有别的进程, 那就将这个进程挂到就绪队列队尾, 运行下一个. 如果当前进程运行完了并且就绪队

列里面没有别的进程, 这个时候所有的进程并没有全部入过队, 我们就要判断, 如果全部入过了就结束, 如果没有全部如果就

等待, 总时间一直累加, 直到有进程进来了, 最终运行完成.

    特点:

        1. 每个进程有平等的机会可以获得CPU

        2. 时间片要合适, 如果偏大: 性能退化, 如果偏小: 进程调度频繁, 增加系统开销


进程调度源码

ps : 进程的一些时间属性

    1. 到达时间: 一个进程到达内存的时间.

    2. 服务时间: 这个进程需要运行的时间.

    3. 完成时间: 这个进程被CPU调度完成之后的完成时间.

    4. 周转时间: 这个进程从进入内存到被执行完的时间,  周转时间 = 完成时间 - 到达时间.

    5. 带权周转时间: 带权周转时间越小, 证明对这个进程越有利, 带权周转时间 = 周转时间 / 服务时间. 

程序源码 

  链接: github源码

pcb.h
#pragma once
#include <iostream>
#include <queue>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

typedef struct Pcb
{
	char name;
	float arr_time;//到达时间
	float ser_time;//服务时间
	float fin_time;//完成时间
	float tur_time;//周转时间
	float b_tur_time;//带权周转时间
	float run_time = 0;//运行时间
	bool flag_run = false;//执行标志
	bool flag_push = false;//入队标志
}Pcb;

void Input(vector<Pcb>& pcb, int &n)
{
	cout << "请输入进程数量: ";
	cin >> n;
	cout << endl;
	pcb.resize(n);
	for (int i = 0; i < n; ++i)
	{
		cout << "请输入进程" << i + 1 << "的进程名, 到达时间, 服务时间: ";
		cin >> pcb[i].name >> pcb[i].arr_time >> pcb[i].ser_time;
	}
}

void Output(vector<Pcb>& pcb, int n, float tur_sum, float b_tur_sum)
{
	float avg_tur_sum = tur_sum / n;
	float avg_b_tur_sum = b_tur_sum / n;

	cout << endl;
	cout << "id|   arr  |   ser  |   fin  |   tur  |  b_tur |" << endl;
	for (int i = 0; i < n; ++i)
	{
		printf("%c | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f | \n", pcb[i].name, pcb[i].arr_time, pcb[i].ser_time, pcb[i].fin_time, pcb[i].tur_time, pcb[i].b_tur_time);
	}
	cout << endl;
	printf("平均周转时间 : %6.2f\n", avg_tur_sum);
	printf("平均带权周转时间 : %6.2f\n", avg_b_tur_sum);
	cout << endl;
}

bool ForArr(Pcb& a, Pcb& b)
{
	return a.arr_time < b.arr_time;
}

void Time_BubbleSort(vector<Pcb>& pcb, int n)
{
	sort(pcb.begin(), pcb.end(), ForArr);
}

//先来先服务
void FCFS(vector<Pcb>& pcb, int &n)
{
	Input(pcb, n);
	Time_BubbleSort(pcb, n);

	float tur_sum = 0.0;
	float b_tur_sum = 0.0;
	float Time = 0;//运行总时间
	for (int i = 0; i < n; ++i)
	{
		if (i == 0)
		{
			pcb[0].fin_time = pcb[0].ser_time;
			Time = pcb[0].fin_time;
		}
		else
		{
			if (pcb[i].arr_time > Time)
			{
				//如果前一个进程运行完成下一个进程没有到达	
				Time = pcb[i].arr_time;
			}
			pcb[i].fin_time = pcb[i].ser_time + Time;
			//更新一下程序运行的总时间
			Time = pcb[i].fin_time;
		}
		pcb[i].tur_time = pcb[i].fin_time - pcb[i].arr_time;
		tur_sum += pcb[i].tur_time;
		pcb[i].b_tur_time = pcb[i].tur_time / pcb[i].ser_time;
		b_tur_sum += pcb[i].b_tur_time;
	}

	Output(pcb, n, tur_sum, b_tur_sum);
}

//短作业优先
void SJF(vector<Pcb>& pcb, int n)
{
	Input(pcb, n);
	//先按照到达时间排序
	Time_BubbleSort(pcb, n);	

	float Time = 0;
	bool flag_have = false;
	float tur_sum = 0.0;
	float b_tur_sum = 0.0;

	//一次运行一个进程
	//第一个进程一定先运行
	pcb[0].fin_time = pcb[0].ser_time;
	pcb[0].flag_run = true;
	pcb[0].tur_time = pcb[0].fin_time - pcb[0].arr_time;
	pcb[0].b_tur_time = pcb[0].tur_time / pcb[0].ser_time;
	Time = pcb[0].fin_time;
	tur_sum += pcb[0].tur_time;
	b_tur_sum += pcb[0].b_tur_time;

	for (int i = 1; i < n; ++i)
	{
		flag_have = false;
		//找出当前数组中还没有运行的第一个pcb
		//不会存在全部运行过的情况, 因为上面的for循环是n - 1次, 所以肯定刚好把下标从1 ~ n - 1的访问完
		int Min_ser = 0;
		for (int index = 1; index < n; ++index)
		{
			if (!pcb[index].flag_run)
			{
				Min_ser = index;
				break;
			}
		}
		//找到之后, 从第一个节点开始向后访问, 判断有没有到达时间小于Time的
		//有 : 再找出服务时间最小的
		for (int j = 1; j < n; ++j)
		{
			if ((pcb[j].arr_time < Time) && (!pcb[j].flag_run))
			{
				flag_have = true;
				if (pcb[j].ser_time < pcb[Min_ser].ser_time)
					Min_ser = j;
			}
		}

		//没有: 当前进程就是到达时间最小的, 直接运行它, Min_ser不用变
		if (!flag_have)
		{
			Time = pcb[Min_ser].arr_time;
		}

		//计算当前进程的各个时间
		pcb[Min_ser].fin_time = Time + pcb[Min_ser].ser_time;
		pcb[Min_ser].tur_time = pcb[Min_ser].fin_time - pcb[Min_ser].arr_time;
		pcb[Min_ser].b_tur_time = pcb[Min_ser].tur_time / pcb[Min_ser].ser_time;
		pcb[Min_ser].flag_run = true;
		Time = pcb[Min_ser].fin_time;
		
		tur_sum += pcb[Min_ser].tur_time;
		b_tur_sum += pcb[Min_ser].b_tur_time;
	}

	Output(pcb, n, tur_sum, b_tur_sum);
}

void RR(vector<Pcb>& pcb, int n, int time)
{
	//输入
	Input(pcb, n);
	//先按到达时间排序
	Time_BubbleSort(pcb, n);
	queue<Pcb> pq;

	//第一个一定先运行
	//先将pcb[0]push进去
	pcb[0].flag_push = true;
	pq.push(pcb[0]);
	int q = time;//时间片
	float Time = 0;//程序的运行时间
	bool flag_all = false;//是否全部入队
	while ((!pq.empty()) || (!flag_all))
	{
		--q;
		++Time;
		//如果有进程到达, 入队
		//如果已经全部入过队了, 就不用判断了, 省时间
		if (!flag_all)
		{
			for (int i = 1; i < n; ++i)
			{
				if ((pcb[i].arr_time == Time) && (!pcb[i].flag_push))
				{
					pcb[i].flag_push = true;
					pq.push(pcb[i]);
					if (pq.size() == n)
						flag_all = true;
					break;
				}
			}
			//判断是否所有的进程都进入了队列, 作用是当有一个进程执行完但是后面有进程没有到达时
			//要一直++Time
			for (int i = 0; i < n; ++i)
			{
				if (pcb[i].flag_push = false)
					flag_all = false;
			}
		}
		
		//队列不为空就去访问
		//队列为空有两种情况:
		//		1. 所有进程运行完了
		//		2. 当前进程运行完了, 但是下一个没有到达, 这个不用管, 会继续while循环
		if (!pq.empty())
		{
			//自身的完成时间+1
			++pq.front().run_time;
			//如果运行完了, 队首出队, 换下一个运行 
			if (pq.front().run_time == pq.front().ser_time)
			{
				pq.front().fin_time = Time;
				for (int i = 0; i < n; ++i)
				{
					if (pcb[i].name == pq.front().name)
					{
						pcb[i] = pq.front();
						break;
					}
				}
				pq.pop();
				q = time;
			}
			else
			{
				//没有运行完
				//后面还有别的进程, 挂到队尾, 运行的下一个进程
				if (!pq.empty() && pq.size() > 1)
				{
					if (q == 0)
					{
						q = time;
						pq.push(pq.front());
						pq.pop();
					}
				}
			}
		}
		//一个进程运行完但是另一个没有进来, 时间片会轮完, 所以要更新
		if (q == 0)
			q = time;
	}

	float tur_sum = 0;
	float b_tur_sum = 0;
	//计算后面每一个的各个时间
	for (int i = 0; i < n; ++i)
	{
		pcb[i].tur_time = pcb[i].fin_time - pcb[i].arr_time;
		tur_sum += pcb[i].tur_time;
		pcb[i].b_tur_time = pcb[i].tur_time / pcb[i].ser_time;
		b_tur_sum += pcb[i].b_tur_time;
	}

	Output(pcb, n, tur_sum, b_tur_sum);
}
main.cpp
#include "pcb.h"
void menu()
{
	cout << "************进程调度算法**********" << endl;
	cout << "*********1. 先来先服务算法********" << endl;
	cout << "*********2. 短作业优先算法********" << endl;
	cout << "*********3. 时间片轮转算法********" << endl;
	cout << "**************0. 退出************" << endl;
	cout << "请选择算法:";
}

void start()
{
	int choose;
	while (1)
	{
		menu();
		cin >> choose;
		if (choose == 0)
		{
			break;
		}
		else if (choose == 1)
		{
			//先来先服务算法
			vector<Pcb> pcb;
			int n = 0;
			FCFS(pcb, n);
		}
		else if (choose == 2)
		{
			//短作业优先算法
			vector<Pcb> pcb;
			int n = 0;
			SJF(pcb, n);
		}
		else if (choose == 3)
		{
			//时间片轮转算法
			vector<Pcb> pcb;
			int n = 0;
			int time = 0;
			cout << "请输入时间片大小: ";
			cin >> time;
			RR(pcb, n, time);
		}
		else
		{
			cout << "-_-输入错误, 请重新输入-_- " << endl;
		}
	}
}

int main()
{
	start();
	system("pause");
	return 0;
}

运行结果

以上就是本人关于进程调度算法的一些理解, 有错误往指出, 万分感谢!!!

 

 

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值