一、实验目的
银行家算法是操作系统中避免死锁的典型算法,通过本实验加深对银行家算法的理解。
二、实验内容
用C语言或C++编写一个简单的银行家算法模拟程序,实现多个进程争用系统临界资源时的分配过程。要求程序实现:
- 当一进程请求一组资源时,先确定是否有足够的资源分配给该进程。如果有,再进一步计算在将这些资源分配给进程后,系统是否处于不安全状态。如果安全,显示安全序列,将资源分配给该进程,否则进程等待。
- 可以显示当前时刻各进程的的资源分配情况。
三、实验过程
实验采用C++编写银行家算法,主要存储结构为vector,以方便实验。
1、实验封装的函数们:
void init(); // 初始化数据,即获得用户输入
void showResourse(); // 显示系统资源
void showInfoStart(); // 显示程序开始的信息
void showInfoOp(); // 显示用户操作信息
void showInfoEnd(); // 显示程序结束的信息
bool safe(); // 检查系统是否安全
vector<vector<int>> operator-(const vector<vector<int>> &v1, const vector<vector<int>> &v2);
vector<vector<int>> operator+(const vector<vector<int>> &v1, const vector<vector<int>> &v2);
vector<int> operator-(const vector<int> &v1, const vector<int> &v2);
vector<int> operator+(const vector<int> &v1, const vector<int> &v2);
bool operator<=(const vector<int> &v1, const vector<int> &v2);
bool operator>(const vector<int> &v1, const vector<int> &v2);
实验中重载了vector的部分运算符,以方便代码读写和演示。
2、实验中用到的数据结构们:
vector<int> Available; // 可利用资源向量
vector<string> resourseName; // 资源 Available[i] 的名称
vector<vector<int>> Max; // 最大需求矩阵
vector<vector<int>> Allocation; // 分配矩阵
vector<vector<int>> Need; // 需求矩阵
vector<int> Request_i; // 进程 Pi 的请求向量
vector<int> P; // 记录安全序列
vector<int> Allocated; // 记录进程是否已被分配资源
int m = 0; // m 种资源
int n = 0; // n 个进程
大部分变量按照课本算法来命名。这里用m表示资源种类,n表示进程的个数,后面我们将用这两个变量与用户进行交互。
3、main函数的实现:
(1)初始化部分
int main() {
int choice = 0; // 记录用户的选择
int Pi; // 记录用户选择的进程
// 显示程序开始信息
showInfoStart();
// 初始化操作,获得用户输入数据
init();
// 显示系统资源
showResourse();
// 首先检查一遍,输出系统是否安全
if (safe()) {
cout << "\n系统安全!\n";
cout << "安全序列为:";
for (int i = 0; i < P.size() - 1; ++i) {
cout << P[i] << " -> ";
}
cout << P[n - 1] << "\n";
}
else {
cout << "系统不安全!!!\n";
}
初始化部分的主要操作是输出一系列信息以及与用户交互,读取用户输入的进程和资源数据。然后判断用户输入的模拟系统是否是安全的,并输出相关信息。
(2)主体部分(while循环)
// 进入循环,开始与用户交互
do {
// 显示用户的操作信息
showInfoOp();
cout << "请选择:";
// 获取用户操作
cin >> choice;
switch (choice) {
...
} while (choice != 0); // 用户输入 0,则退出循环
采用while循环进行交互,不断获取用户的选择并执行。若用户输入为0,则退出循环结束。
- 选择1,使用银行家算法尝试资源分配,并输出相应结果
首先获取用户数据,然后依次执行算法的四个步骤:// 操作 1:尝试分配资源 case 1: // 与用户交互,获得需要的信息 cout << "请输入要求分配的资源进程号(0 - " << (n - 1) << "):"; cin >> Pi; cout << "请输入进程 " << Pi << "申请的资源\n( "; for (const auto &elem: resourseName) cout << elem << " "; cout << "):\n"; for (auto &elem: Request_i) cin >> elem; /******************************************************************** * 进入银行家算法 * *******************************************************************/ // 步骤 1:检查请求是否超过其需要 if (Request_i > Need[Pi]) { cout << "\n进程 " << Pi << " 申请的资源大于它需要的资源,不给予分配!\n"; break; } // 步骤 2:检查系统剩余资源是否能够满足请求 if (Request_i > Available) { cout << "\n进程 " << Pi << " 申请的资源大于系统已有的资源,请下次再申请!\n"; break; } // 步骤 3:系统尝试分配资源 Available = Available - Request_i; Allocation[Pi] = Allocation[Pi] + Request_i; Need[Pi] = Need[Pi] - Request_i; // 步骤 4:执行安全性算法 // 如果安全,则分配并打印信息显示 if (safe()) { // Available = Available + Max[Pi]; // 系统安全,令进程运行结束,回收资源 showResourse(); cout << "\n系统安全!\n"; cout << "安全序列为:"; for (int i = 0; i < P.size() - 1; ++i) { cout << P[i] << " -> "; } cout << P[n - 1] << "\n"; } // 如果不安全,则恢复分配之前的状态,打印信息 else { cout << "\n系统不安全,撤销资源分配!!!\n"; Available = Available + Request_i; Allocation[Pi] = Allocation[Pi] - Request_i; Need[Pi] = Need[Pi] + Request_i; }
1)检查请求是否超过需要
2)检查请求是否能够满足
3)尝试分配资源
4)安全性检测,若不通过,尝试分配作废,恢复原来的状态
这些在代码中注释部分解释的相对比较清楚,因此不一一列举讲解了。 - 选择2,显示资源信息。
这里我们调用封装好的函数进行显示,函数使用cout的格式化输出来打印信息。 - 选择3,退出。
这没啥好说的,啥也不干就行了。
(4)结束部分
// 打印程序结束信息
showInfoEnd();
return 0;
}
打印结束信息,完成!
4、安全性检查:
/**
* 检查系统是否安全
* @return 若安全,返回 true <br>
* 若不安全,返回 false
*/
bool safe() {
// 初始化向量 Work 和 Finish
vector<int> Work = Available;
vector<bool> Finish(n, false);
// 清除上次的进程队列
P.clear();
// 遍历 n 次
for (int j = 0; j < n; ++j)
// 遍历 1 次,寻找 n 个进程中满足条件的一个
for (int i = 0; i < n; ++i)
if (!Finish[i] && Need[i] <= Work) {
// 选中该进程 i,进行相应操作后将其加入队列 P 中
Finish[i] = true;
Work = Work + Allocation[i];
P.push_back(i);
}
// 检查每个进程是否都被选中
for (int i = 0; i < n; ++i)
// 如果有一个进程未被选中,说明系统不安全,返回 false
if (!Finish[i]) return false;
// 所有进程都被选中,说明系统状态安全,返回 true
return true;
}
该算法步骤正如上述注释所说:
1)从集合中找到一个满足条件的进程
2)尝试分配资源,并回收其运行完成后的所有资源
3)所有进程都被选择,则系统安全;否则不安全
四、函数实现
- init()
/** * 初始化数据,即获得用户输入 */ void init() { cout << "请输入系统可供资源种类的数量 m:"; cin >> m; cout << "\n"; // 设置资源向量的大小 resourseName.resize(m); Available.resize(m); Request_i.resize(m); // 获取用户输入 for (int i = 0; i < m; ++i) { cout << "资源 Available[" << (i + 1) << "] 的名称:"; cin >> resourseName[i]; cout << "资源的数量:"; cin >> Available[i]; cout << "\n"; } cout << "\n请输入作业的数量 n:"; cin >> n; // 设置相应数据结构的大小 Max.resize(n); Allocation.resize(n); for (int i = 0; i < n; ++i) { Max[i].resize(m); Allocation[i].resize(m); } Allocated.resize(n, 0); // 输出信息 cout << "\n请输入各进程的最大需求量 Max[" << n << "][" << m << "]:\n"; for (auto &line : Max) for (auto &elem : line) cin >> elem; cout << "\n请输入各进程已经申请的资源量 Allocation[" << n << "][" << m << "]:\n"; for (auto &line : Allocation) for (auto &elem : line) cin >> elem; // 初始化 Need 矩阵 Need = Max - Allocation; }
- showResourse()
/** * 显示系统资源 */ void showResourse() { // 记录原先的 cout 输出格式,并设置其为左对齐输出 auto oldStyle = cout.setf(ios_base::left, ios_base::adjustfield); // 设置默认数据打印宽度,用于格式化输出 int DEFAULTNUMWIDTH = DEFAULTNAMEWIDTH / m; int DEFAULTLEFT = DEFAULTNAMEWIDTH - DEFAULTNUMWIDTH * m; // 打印 Available cout << "\n"; cout << "系统目前可用的资源 Available[m]:"; cout << "\n"; for (const auto &elem : resourseName) cout << setw(DEFAULTNUMWIDTH) << elem; cout << "\n"; for (const auto &elem : Available) cout << setw(DEFAULTNUMWIDTH) << elem; cout << "\n\n"; // 打印信息表 cout << setw(DEFAULTNAMEWIDTH) << "-----------"; cout << setw(DEFAULTNAMEWIDTH) << "Max"; cout << setw(DEFAULTNAMEWIDTH) << "Allocation"; cout << setw(DEFAULTNAMEWIDTH) << "Need"; cout << "\n"; // 打印资源名称 cout << setw(DEFAULTNAMEWIDTH) << "resourseName"; for (int i = 0; i < 3; ++i) { for (int j = 0; j < m; ++j) { cout << setw(DEFAULTNUMWIDTH) << resourseName[j]; } cout << setw(DEFAULTLEFT) << ""; } cout << "\n\n"; // 格式化打印资源数据 for (int i = 0; i < n; ++i) { cout << setw(DEFAULTNAMEWIDTH) << i; for (int j = 0; j < m; ++j) { cout << setw(DEFAULTNUMWIDTH) << Max[i][j]; } cout << setw(DEFAULTLEFT) << ""; for (int j = 0; j < m; ++j) { cout << setw(DEFAULTNUMWIDTH) << Allocation[i][j]; } cout << setw(DEFAULTLEFT) << ""; for (int j = 0; j < m; ++j) { cout << setw(DEFAULTNUMWIDTH) << Need[i][j]; } cout << setw(DEFAULTLEFT) << ""; cout << "\n"; } // 将 cout 的输出格式恢复 cout.setf(oldStyle, ios_base::adjustfield); }
- showInfoStart()
/** * 显示程序开始的信息 */ void showInfoStart() { cout << "*******************************************\n"; cout << "* 银行家算法演示 *\n"; cout << "*******************************************\n\n"; }
- showInfoOp()
/** * 显示用户操作信息 */ void showInfoOp() { cout << "***************银行家算法演示***************\n"; cout << " 1 —— 分配资源\n"; cout << " 2 —— 显示分配情况\n"; cout << " 0 —— 离开\n"; cout << "******************************************\n"; }
- showInfoEnd()
/** * 显示程序结束的信息 */ void showInfoEnd() { cout << "*******************************************\n"; cout << "* 银行家算法演示结束 *\n"; cout << "*******************************************\n"; }
- safe()
/** * 检查系统是否安全 * @return 若安全,返回 true <br> * 若不安全,返回 false */ bool safe() { // 初始化向量 Work 和 Finish vector<int> Work = Available; vector<bool> Finish(n, false); // 清除上次的进程队列 P.clear(); // 遍历 n 次 for (int j = 0; j < n; ++j) // 遍历 1 次,寻找 n 个进程中满足条件的一个 for (int i = 0; i < n; ++i) if (!Finish[i] && Need[i] <= Work) { // 选中该进程 i,进行相应操作后将其加入队列 P 中 Finish[i] = true; Work = Work + Allocation[i]; P.push_back(i); } // 检查每个进程是否都被选中 for (int i = 0; i < n; ++i) // 如果有一个进程未被选中,说明系统不安全,返回 false if (!Finish[i]) return false; // 所有进程都被选中,说明系统状态安全,返回 true return true; }
- 重载运算符
/** * 执行矩阵(二维数组)的减法 * @param v1 矩阵 1 * @param v2 矩阵 2 * @return 相减后的矩阵 */ vector<vector<int>> operator-(const vector<vector<int>> &v1, const vector<vector<int>> &v2) { vector<vector<int>> ans(n, vector<int>(m, 0)); for (int i = 0; i < n; ++i) { ans[i] = v1[i] - v2[i]; } return ans; } /** * 执行矩阵(二维数组)的加法 * @param v1 矩阵 1 * @param v2 矩阵 2 * @return 相加后的矩阵 */ vector<vector<int>> operator+(const vector<vector<int>> &v1, const vector<vector<int>> &v2) { vector<vector<int>> ans(n, vector<int>(m, 0)); for (int i = 0; i < n; ++i) { ans[i] = v1[i] + v2[i]; } return ans; } /** * 执行向量(数组)的减法 * @param v1 向量 1 * @param v2 向量 2 * @return 相减后的向量 */ vector<int> operator-(const vector<int> &v1, const vector<int> &v2) { vector<int> ans(m, 0); for (int i = 0; i < m; ++i) ans[i] = v1[i] - v2[i]; return ans; } /** * 执行向量(数组)的加法 * @param v1 向量 1 * @param v2 向量 2 * @return 相加后的向量 */ vector<int> operator+(const vector<int> &v1, const vector<int> &v2) { vector<int> ans(m, 0); for (int i = 0; i < m; ++i) ans[i] = v1[i] + v2[i]; return ans; } /** * 重载 vector <= 符号,使得满足银行家算法的条件 * @param v1 向量 1 * @param v2 向量 2 * @return 若 v1 <= v2,返回 true <br> * 若 v1 > v2,返回 false */ bool operator<=(const vector<int> &v1, const vector<int> &v2) { for (int i = 0; i < v1.size(); ++i) if (v1[i] > v2[i]) return false; return true; } /** * 重载 vector > 符号,使得满足银行家算法的条件 * @param v1 向量 1 * @param v2 向量 2 * @return 若 v1 > v2,返回 true <br> * 若 v1 <= v2,返回 false */ bool operator>(const vector<int> &v1, const vector<int> &v2) { return !(v1 <= v2); }
五、实验总结和说明
实验总共有400+行代码,其中大部分为注释。实验并不注重用户输入数据的合法性,需要用户输入正确的输入方可运行。
以上内容仅代表个人,代码纯属原创,如有错误请自行修改。