在这篇博客中,我将总结一下操作系统中的三种常用算法:多级队列调度算法、银行家算法以及动态分区式存储管理算法。算法用C++实现,其中部分代码思路是从网上查找的。
多级队列调度算法
该算法是将多个进程存储到一个队列当中,分别用不同的调度方式对进程的执行进行约束和管理,调度方式有轮转法和短进程优先调度法。轮转法的思想是:设置一个时间片。从队列头部开始遍历,每一个进程运行一个时间片的时间,如果在时间片结束时进程还在运行,则停止并把该进程移到队列的末尾,进入下一个进程。如果进程在时间片结束前结束,则当即进行切换。短进程优先调度算法的思路为:按需运行的时间把进程的优先级从小到大排列,即所需运行时间越短,越先运行。下面是详细代码。
PCB结构(进程链表队列的节点):
class PCB
{
public:
string name;
int need;//须运行的时间
int turn;//周转时间
PCB *next;
PCB(stringn, inta, intb, PCB*next) :name(n), need(a), turn(b), next(next) {}
};
创建两个链表,并对其属性值初始化:
PCB * RQ1, *RQ2, *Finish = NULL;
int clock = 0; //时钟
//定义各进程的运行时间及等待时间
PCB*p, *q, *s = NULL;
int piecetime = 7;
string name1[5] = { "p1","p2","p3","p4","p5" };
string name2[5] = { "p6","p7","p8","p9","p10" };
int need1[5] = {16,11,14,13,15 };
int wait1[5] = {6,5,4,3,2 };
int need2[5] = {21,18,10,7,14 };
int wait2[5] = {1,2,3,4,5 };
RQ1= newPCB(name1[0], need1[0],wait1[0], NULL);
p= RQ1;
for (int i = 1; i < 5;i++) {
q= newPCB(name1[i], need1[i],wait1[i], NULL);
p->next= q;
p= p->next;
}
RQ2= newPCB(name2[0], need2[0],wait2[0], NULL);
p= RQ2;
for (int i = 1; i < 5;i++) {
q= newPCB(name2[i], need2[i],wait2[i], NULL);
p->next= q;
p= p->next;
}
轮转调度算法:
p = RQ1;
while (p)
{
bool flag = false;
if (p->need)
{
q= p;
while (q->next)q =q->next;//q指针指向最后一个结点
if (p->need >piecetime) //如果该节点表示的进程还需的时间大于一个时间片
{
clock+= piecetime;
p->need-= piecetime;
//把刚运行了一个时间片的结点放到队列尾部
q->next= p;
p= p->next;
q->next->next= NULL;
}
else
{
clock += p->need;
p->turn+= clock;
p->need= 0;
if (s == NULL)
{
s= p;
Finish= s; //Finish指针指向最后运行后的链表的第一个节点
flag= true;
}
else s->next = p;
if (flag == false)s = s->next;
}
}
else p = p->next;
}
RQ1= Finish;
短进程优先调度算法:
for (int i = 0; i < 5;i++)
{
p= RQ2;
q= RQ2;
while (q) //找到所需时间最短的进程
{
if (q->need <p->need)p = q;
q= q->next;
}
clock+= p->need;
p->turn+= clock;
p->need= max;
}
输出两个链表队列中每个进程的运行周转时间:
for (int i = 0; i < 5;i++)
{
cout<< RQ1->name <<":"<< RQ1->turn << endl;
RQ1= RQ1->next;
}
cout<< endl;
for (int i = 0; i < 5;i++)
{
cout<< RQ2->name <<":"<< RQ2->turn << endl;
RQ2= RQ2->next;
}
测试数据:
测试结果:
银行家算法
银行家算法是为了对进程的资源进行分配管理,首先创建两个二维数组,二维数组中的每一行代表一个进程,每一列表示不同的资源,这两个数组分别表示每个进程已经分配的资源和还需分配的资源,然后创建一个一维数组,存储还可以使用的不同的资源。本题的整体思路如下:
详细代码:
#include<iostream>
using namespace std;
const int p = 5;//进程数
const int r = 3;//资源种类
void Apply(int allo[p][r], int need[p][r], int ava[r], int req[r], int n);
bool IsSafe(int allo[p][r], int need[p][r], int ava[r]);
bool com(int m[r], int n[r]);
int main()
{
int allocation[p][r], need[p][r];
int available[r], request[r];
cout << " 输入每个进程已分配的资源数据" << endl;;
for (int i = 0; i < p; i++)
for (int j = 0; j < r; j++)cin >> allocation[i][j];
cout << " 输入每个进程还需要的资源数据" << endl;;
for (int i = 0; i < p; i++)
for (int j = 0; j < r; j++)cin >> need[i][j];
cout << "输入可用的资源数据" << endl;;
for (int j = 0; j < r; j++)cin >> available[j];
if (IsSafe(allocation, need, available))cout << "初始状态安全" << endl;
else cout << "初始状态不安全" << endl;
while (true)
{
cout << " 输入申请的资源"<<endl;
for (int i = 0; i < r; i++)cin >> request[i];
cout << "第n个进程申请资源——n的值" << endl;;
int n;
cin >> n;
Apply(allocation, need, available, request, n);
}
return 0;
}
/*比較函数*/
bool com(int m[r], int n[r])
{
for (int i = 0; i < r; i++)
{
if (m[i] < n[i]) return(0);
}
return true;
}
/*安全性检验函数*/
bool IsSafe(int allo[p][r], int need[p][r], int ava[r])
{
bool finish[p];
for (int i = 0; i < p; i++)finish[i] = false;//finish为true即表示available满足某一进程
for (int i = 0; i < p; i++)//全部进行检查
{
if (finish[i])continue;
else
{
int temp[r];
for (int j = 0; j < r; j++)temp[j] = need[i][j];
if (com(ava, temp))
{
finish[i] = 1;
cout << "第" << i + 1 << "个进程安全" << endl;
for (int l = 0; l < r; l++)ava[l] += allo[i][l];
}
}
}
cout << endl;
for (int i = 0; i < p; i++)
{
if (!finish[i])
{
cout << "第" << i + 1 << "个进程不安全!" << endl;
return false;
}
}
return true;
}
/*申请资源函数*/
void Apply(int allo[p][r], int need[p][r], int ava[r], int req[r], int n)
{
n = n - 1;
int temp[r];
for (int i = 0; i < r; i++)temp[i] = need[n][i];
if (!com(temp, req))
{
cout << "请求违法!" << endl;
return;
}
else if (!com(ava, req))
{
cout << "发生阻塞" << endl;
return;
}
else
{
for (int j = 0; j < r; j++)//试探性分配
{
allo[n][j] = allo[n][j] + req[j];
need[n][j] = need[n][j] - req[j];
ava[j] = ava[j] - req[j];
}
if (IsSafe(allo, need, ava))cout << "同意" << n + 1 << "个进程申请资源!" << endl;
else
{
cout << "不同意" << n + 1 << "个进程申请资源!" << endl;
cout << "恢复之前的状态!" << endl;
for (int j = 0; j < r; j++)
{
allo[n][j] = allo[n][j] - req[j];
need[n][j] = need[n][j] + req[j];
ava[j] = ava[j] + req[j];
}
}
}
}
动态分区式存储区管理
该算法是为了设计一个动态分区式存贮区管理程序,要求支持不同的放置策略。如首次、最佳、最坏。
首先创建一个分区节点类,构成一个分区链表,每个分区节点具有是否可用、占用大小等属性,并有一个指向下一个分区的指针。然后针对每一个进程,构造一个进程结构,含有每个进程的id、地址、大小等关键属性。创建好以上两个类后,再创建一个管理类,其具有一个指针,指向分区链表的第一个节点。并用一个顺序表存储所有已经成功申请的进程。在此类中,定义首次、最佳、最坏等放置策略函数,以及内存释放函数。这些是本题目的算法核心部分。其次再定义一个读函数读取分区放置情况,方便测试。首次放置算法的思路是在内存中查找到第一个能放入程序的分区,将程序放入。因为分区是按顺序连接在一起,所以每次都从低地址处开始查询。最佳放置算法的思路是将程序放入一个与其需要的大小最接近的空闲分区中。此处需要设置一个记录分区指针,来保存记录最适合的分区的位置。最坏放置算法与最佳放置算法相反,是将程序放入到一个与其最不适合的空闲分区当中。释放进程时应考虑是否需要合并分区,有以下四个情况(假设回收区为r,上邻为f1,下邻为f2):
A)f1空闲,f2已分配,需合并;
B)f1已分配,f2空闲,需合并;
C)f1空闲,f2空闲,需合并;
D)f1已分配,f2已分配,不需合并;
下面是详细代码:
分区节点类:
class Rd
{
public:
bool m_Flag;
int m_Size;
Rd* m_Next;
Rd(boolflag, intsize, Rd* next) :
m_Flag(flag), m_Size(size), m_Next(next)
{}
};
进程类:
class Single
{
public:
int m_ID;
int m_Begin;
int m_Size;
Single(intid, intsize, intbegin) :
m_ID(id), m_Begin(begin), m_Size(size)
{}
};
管理类:
class Management
{
private:
Rd* m_First; //指向分区链表的第一个节点
vector<Single>all; //存储所有已经申请成功的进程
public:
Management(Rd* first) :m_First(first) {}
void FirstWay();
void BestWay();
void WorstWay();
void Read();
void free();
};
首次放置算法:
void Management::FirstWay()
{
int address(0);//address记录当前块的地址
cout<<"输入进程id和大小:";
int id, size;
cin>> id >> size;
Rd* p(m_First);
while (p)
{
address+= (p->m_Size + 1);
if (p->m_Flag)
{
if (p->m_Size ==size)
{
p->m_Flag= false;
address-= (size + 1);
break;
}
elseif (p->m_Size >size) //当前块大于进程申请的大小
{
p->m_Size-= (size + 1);
address-= (size + 1);
Rd* newrd = newRd(false, size,p->m_Next);
p->m_Next= newrd;
break;
}
else p = p->m_Next;
}
else p = p->m_Next;
}
if (p)//循环结束后,若p不为空,则说明进程成功申请,将其加入vector
{
Single newsingle(id, size,address);
all.push_back(newsingle);
}
else cout <<"assign fail"<< endl;
}
最佳放置算法:
void Management::BestWay()
{
int address(0);//address记录当前块的地址
cout<<"输入进程id和大小:";
int id, size;
cin>> id >> size;
Rd* p(m_First);
int min = 0x7fffffff;
Rd*q(NULL); //记录指针
while (p)
{
if (p->m_Flag)
{
if (p->m_Size >size) //当前块大于进程申请的大小
{
if (p->m_Size <min) //当前块更适合
{
min= p->m_Size;
q= p;
}
}
elseif (p->m_Size ==size)
{
p->m_Flag= false;
q= p;
break;
}
}
p= p->m_Next;
}
if (q) //循环结束后,若q不为空,则说明进程成功申请,将其加入vector
{
q->m_Size-= (size + 1);
Rd* newrd = newRd(false, size,q->m_Next);
q->m_Next= newrd;
p= m_First;
while (p) {
if (p == q->m_Next)break;
address+= (p->m_Size + 1);
p= p->m_Next;
}
Single newsingle(id, size,address);
all.push_back(newsingle);
}
else cout <<"assign fail"<< endl;
}
最坏放置算法:
void Management::WorstWay()
{
int address(0);//address记录当前块的地址
cout<<"输入进程id和大小:";
int id, size;
cin>> id >> size;
Rd* p(m_First);
int max = 0;
Rd*q(NULL); //记录指针
while (p)
{
if (p->m_Flag)
{
if (p->m_Size >size) //当前块大于进程申请的大小
{
if (p->m_Size >max) //当前块更糟糕
{
max= p->m_Size;
q= p;
}
}
elseif (p->m_Size ==size)
{
p->m_Flag= false;
q= p;
break;
}
}
p= p->m_Next;
}
if (q)//循环结束后,若q不为空,则说明进程成功申请,将其加入vector
{
q->m_Size-= (size + 1);
Rd* newrd = newRd(false, size,q->m_Next);
q->m_Next= newrd;
p= m_First;
while (p)
{
if (p == q->m_Next)break;
address+= (p->m_Size + 1);
p= p->m_Next;
}
Single newsingle(id, size,address);
all.push_back(newsingle);
}
else cout <<"assign fail"<< endl;
}
读分区函数:
void Management::Read()
{
int i = 1;
Rd*temp = m_First;
while (temp)
{
if(temp->m_Flag)cout <<"第"<< i <<"块分区可用, ";
else cout <<"第"<< i <<"块分区不可用, ";
cout<<"大小为"<< temp->m_Size << endl;
i++;
temp= temp->m_Next;
}
cout<< endl;
vector<Single>::iterator it = all.begin();
while (it != all.end()) {
cout<<"id为"<< it->m_ID <<"的进程的地址为:";
cout<< it->m_Begin << endl;
it++;
}
}
释放进程函数:
void Management::free() {
cout<<"输入释放进程的id:";
int id;
cin>> id;
//在vector中查找是否存在该进程
vector<Single>::iterator one;
for (one = all.begin(); one != all.end(); one++)
{
if (one->m_ID == id)break;
}
if (one == all.end())
{
cout<<"该进程不存在,请检查输入"<< endl;
return;
}
int address(0);
Rd *p(m_First);
while (p) { //查找该进程所用的分区
if (one->m_Begin == address)break;
address+= (p->m_Size + 1);
p= p->m_Next;
}
p->m_Flag= true; //该块的状态更改为可用
if (p->m_Next)
{
if(p->m_Next->m_Flag) //若所释放的分区后面存在可用的分区,合并
{
p->m_Size+= (p->m_Next->m_Size + 1);
Rd*t(p->m_Next);
p->m_Next= p->m_Next->m_Next;
delete t;
}
}
Rd *q(m_First);
while (q) {
if (q->m_Next ==p&&q->m_Flag == true)//所释放的分区前面存在可用的分区,合并 {
q->m_Size+= (p->m_Size + 1);
q->m_Next= p->m_Next;
delete p;
break;
}
q= q->m_Next;
}
all.erase(one);
主函数
int main() {
Rd*first = new Rd(true, 511, NULL);
Management a(first);
while (true)
{
a.Read();
cout << "********************" << endl;
cout << "请输入想使用的功能" << endl;
cout << "1.申请进程" << endl;
cout << "2.释放分区" << endl;
cout << "3.退出系统" << endl;
cout << "********************" << endl;
int input(0);
cin >> input;
switch (input)
{
case(1):
{
cout << "********************" << endl;
cout << "请输入申请进程的方式" << endl;
cout << " 1.首次申请" << endl;
cout << " 2.最好申请" << endl;
cout << " 3.最坏申请" << endl;
cout << "********************" << endl;
int input2(0);
cin >> input2;
if (input2 == 1)a.FirstWay();
if (input2 == 2)a.BestWay();
if (input2 == 3)a.WorstWay();
break;
}
case(2):
a.free();
case(3):
break;
default:
continue;
}
}
return 0;
}