银行家算法
课程设计说明书
(操作系统)
题目: 银行家算法
院 系:计算机科学与工程学院
专业班级:
学 号:
学生姓名:
指导教师:
2011年12月25日
安徽理工大学课程设计(论文)任务书
计算机科学与工程学院 网络与信息安全系
2011年12月25日
操作系统课程设计报告
摘 要
Dijkstra提出的银行家算法,是最具代表性的避免死锁的算法。
本文对如何用银行家算法来处理操作系统给进程分配资源做了详细的说
明,包括需求分析、概要设计、详细设计、测试与分析、总结、源程序清单。
首先做了需求分析,解释了什么是银行家算法,并指出它在资源分配中的
重要作用。
然后给出了银行家算法的概要设计,包括算法思路、步骤,以及要用到的
主要数据结构、函数模块及其之间的调用关系等。
在概要设计的基础上,又给出了详细的算法设计,实现概要设计中定义的
所有函数,对每个函数写出核心算法,并画出了流程图。
接着对编码进行了测试与分析(并在最后附上c#编写的程序代码)。
最后对整个设计过程进行了总结。
关键词:安全状态;安全序列;银行家算法;安全性算法;安全序列;流
程图。
目录
1绪论 .................................................................................................................. 2
1.1前言 .................................................................................................................... 2
1.2研究意义 ............................................................................................................. 2
1.3结构安排 ............................................................................................................. 3
2 需求分析 .......................................................................................................... 4
2.1题目描述 ............................................................................................................. 4
2.2银行家算法 ......................................................................................................... 4
2.3基本要求 ............................................................................................................. 4
2.4目的 .................................................................................................................... 5
3 概要设计 .......................................................................................................... 6
3.1算法思路 ............................................................................................................. 6
3.2银行家算法步骤 .................................................................................................. 6
3.3安全性算法步骤 .................................................................................................. 6
3.4数据结构 ............................................................................................................. 7
3.4. 1主要用到的数据结构 ....................................................................................................... 7
3.4. 2程序模块 ........................................................................................................................... 7
3.4. 3各模块间的调用关系 ....................................................................................................... 7
4 详细设计 .......................................................................................................... 8
4.1主要函数的核心代码 ........................................................................................... 8
4.2程序流程图 ......................................................................................................... 8
5 测试 ................................................................................................................. 11
5.1测试用例 ........................................................................................................... 11
5.2测试结果截图 .................................................................................................... 11
6 总结 ................................................................................................................. 15
参考文献 ............................................................................................................. 17
附录:源程序清单(核心代码) ........................................................................ 18
1绪论
1.1前言
Dijkstra (1965)提出了一种能够避免死锁的调度算法,称为银行家算法。
它的模型基于一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额
度,每个客户都有一个贷款额度,银行家知道不可能所有客户同时都需要最大贷
款额,所以他只保留一定单位的资金来为客户服务,而不是满足所有客户贷款需
求的最大单位。
这里将客户比作进程,贷款比作设备,银行家比作系统。
客户们各自做自己的生意,在某些时刻需要贷款。在某一时刻,客户已获得
的贷款和可用的最大数额贷款称为与资源分配相关的系统状态。一个状态被称为
是安全的,其条件是存在一个状态序列能够使所有的客户均得到其所需的贷款。
如果忽然所有的客户都申请,希望得到最大贷款额,而银行家无法满足其中任何
一个的要求,则发生死锁。不安全状态并不一定导致死锁,因为客户未必需要其
最大贷款额度,但银行家不敢抱这种侥幸心理。
银行家算法就是对每一个请求进行检查,检查如果满足它是否会导致不安全
状态。若是,则不满足该请求;否则便满足。
检查状态是否安全的方法是看他是否有足够的资源满足一个距最大需求最
近的客户。如果可以,则这笔投资认为是能够收回的,然后接着检查下一个距最
大需求最近的客户,如此反复下去。
如果所有投资最终都被收回,则该状态是安全的,最初的请求可以批准。
1.2研究意义
在多道程序系统中,多个进程的并发执行来改善系统的资源利用率,提高系
统的吞吐量,但可能发生一种危险——死锁。所谓死锁(Deadlock),是指多个进
程在运行过程中因争夺资源而造成的一种僵局(DeadlyEmbrace),当进程处于这
种状态时,若无外力作用,他们都无法在向前推进。
要预防死锁,有摒弃“请求和保持”条件,摒弃“不剥夺”条件,摒弃“环
路等待”条件等方法。
但是,在预防死锁的几种方法之中,都施加了较强的限制条件;而在避免死
锁的方法中,所施加的限制条件较弱,有可能获得令人满意的系统性能。在该方
法中把系统状态分为安全状态和不安全状态,便可避免死锁的发生。
而最具代表性的避免死锁的算法,便是Dijkstra的银行家算法。
利用银行家算法,我们可以来检测CPU为进程分配资源的情况,决定CPU是
否响应某进程的的请求并为其分配资源,从而很好避免了死锁的产生。
1.3结构安排
一、绪论: 介绍了题目背景以及研究意义。
二、需求分析: 介绍了题目描述、银行家算法、以及基本要求和所需达到
的目的。
三、概要设计:介绍了基本的算法思路、步骤,以及数据结构和主要的函数
模块及其调用关系。
四、详细设计:介绍了主要函数及其核心代码,以及程序流程图。
五、测试
六、总结
参考文献
附录:原程序清单
2 需求分析
2.1题目描述
银行家算法是一种最具有代表性的避免死锁的算法。
要解释银行家算法,必须先解释操作系统的安全状态和不安全状态。
所谓安全状态,是指系统能按照某种进程顺序{P1,P2,„,Pn}(称{P1,P2,„,
Pn }序列为安全序列),来为每个进程Pi分配其所需资源,直至满足每个进程对
资源的最大需求,使每个进程都可以顺利完成。安全状态一定没有死锁发生。
如果系统无法找到这样一个安全序列,则称系统处于不安全状态。
那么,什么是安全序列呢?
如果对每一个进程Pi(1
用的资源量与所有的进程Pj(j
{P1,P2,„,Pn}是安全的,称作安全序列。
2.2银行家算法
我们可以把操作系统看做是银行家,操作系统管理的资源相当于银行家管理
的资金,进程向操作系统请求资源相当于客户向银行家贷款。
操作系统按银行家制定的规则为进程分配资源,当进程首次申请资源时,要
测试该进程尚需求的资源量,若是系统现存的资源可以满足它尚需求的资源量,
则按当前的申请量来分配资源,否则就推迟分配。
当进程在执行中继续申请资源时,先测试该进程申请的资源量是否超过了它
尚需的资源量。若超过则拒绝分配,若没有超过则再测试系统尚存的资源是否满
足该进程尚需的资源量,若满足即可按当前的申请量来分配,若不满足亦推迟分
配。
2.3基本要求
(1)可以输入某系统的资源以及T0时刻进程对资源的占用及需求情况的表
项,以及T0时刻系统的可利用资源数。
(2)对T0时刻的进行安全性检测,即检测在T0时刻该状态是否安全。
(3)进程申请资源,用银行家算法对其进行检测,分为以下三种情况:
A. 所申请的资源大于其所需资源,提示分配不合理不予分配并返回。
B. 所申请的资源未大于其所需资源,但大于系统此时的可利用资源,
提示分配不合理不予分配并返回。
C. 申请的资源未大于其所需资源,亦未大于系统此时的可利用资源,
预分配并进行安全性检查:
a. 预分配后系统是安全的,将该进程所申请的资源予以实际分配并打
印后返回。
b. 与分配后系统进入不安全状态,提示系统不安全并返回。
(4)对输入进行检查,即若输入不符合条件,应当报错并返回重新输入。
2.4目的
根据设计题目的要求,充分地分析和理解题目,叙述系统的要求,明确程序
要求实现的功能以及限制条件。
明白自己需要用代码实现的功能,清楚编写每部分代码的目的,做到有的放
矢,有条理不遗漏的用代码实现银行家算法。
3 概要设计
3.1算法思路
先对用户提出的请求进行合法性检查,即检查请求是否大于需要的,是否大
于可利用的。若请求合法,则进行预分配,对分配后的状态调用安全性算法进行
检查。若安全,则分配;若不安全,则拒绝申请,恢复到原来的状态,拒绝申请。
3.2银行家算法步骤
(1)如果Requesti<or =Need,则转向步骤(2);否则,认为出错,因为它所需
要的资源数已超过它所宣布的最大值。
(2)如果Request<or=Available,则转向步骤(3);否则,表示系统中尚无
足够的资源,进程必须等待。
(3)系统试探把要求的资源分配给进程Pi,并修改下面数据结构中的数值:
Available=Available-Request[i];
Allocation=Allocation+Request;
Need=Need-Request;
(4)系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。
3.3安全性算法步骤
(1)设置两个向量
①工作向量Work。它表示系统可提供进程继续运行所需要的各类资源数目,
执行安全算法开始时,Work=Allocation;
②布尔向量Finish。它表示系统是否有足够的资源分配给进程,使之运行
完成,开始时先做Finish[i]=false,当有足够资源分配给进程时,令
Finish[i]=true。
(2)从进程集合中找到一个能满足下述条件的进程:
①Finish[i]=false
②Need
如找到,执行步骤(3);否则,执行步骤(4)。
(3)当进程P获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
Work=Work+Allocation;
Finish[i]=true;
转向步骤(2)。
(4)如果所有进程的Finish[i]=true,则表示系统处于安全状态;否则,系统
处于不安全状态。
3.4数据结构
3.4. 1主要用到的数据结构
(1) 最大需求矩阵Max[][]
(2) 已分配矩阵Allocation[][]
(3) 仍需求矩阵Need[][]=Max[][]-Allocation[][]
(4) 可利用资源向量Available[]
(5) 申请各类资源向量Request[] (6) 工作向量 work[] , Finish[]
3.4. 2程序模块
static void Main()
{
Application.Run(new Form1());
} //系统的主函数
public BankBase[] InitialMethod(); //初始化
public void RequestMethod(Object bankbase)//利用安全性算法进行安全性检测
public bool SecurityMethod(ref Dictionary test, string test1)//进行资源分配
3.4. 3各模块间的调用关系
主函数void Main()
要调用:BankBase(Dictionary test,Form1 f), RequestMethod(Object bankbase)
public BankBase[] InitialMethod()
安全性检测函数SecurityMethod(ref Dictionary test, string test1);
触发鼠标事件自定义初始化:button1_Click(object sender, EventArgs e)
4 详细设计
4.1主要函数的核心代码
1. 进行初始化输入的函数
2. 打印输出的函数
3. 利用安全性算法进行检测的函数
4. 进行资源分配的函数
5. 利用行家算法进行判定的函数
注:具体代码请见附录—源程序清单。
4.2程序流程图
1、系统主要过程流程图
2、银行家算法流程图
3、安全性算法流程图
5 测试
5.1测试用例
测试用例为课本上的例题:
某系统有R1、R2和R3共3种资源,在T0时刻P1、P2、P3和P4这4个
进程对资源的占用和需求情况见下表,此刻系统的可用资源为(2,1,2)。
R1 R2 R3 R1 R2 R3
P1 3 2 2 1 0 0
P2 6 1 3 4 1 1
P3 3 1 4 2 1 1
P4 4 2 2 0 0 2
取了4种不同的例子,来测试系统的主要功能是否 实现:
进程i Request[i] 检测结果
a. 1 2 1 2 Request>Need
b. 0 2 2 2 Request>Available
c.
1 1 0 1 可以分配
d. 0 1 0 1 系统不安全
5.2测试结果截图
1. 开始界面
2. 初始化并打印输出
(1)使用默认资源初始化:
(2)使用自定义资源初始化:单击资源详情修改后显示
3. 用例测试a:进程1发出请求Request(1,3,3)Rquest>Need,不予分
配。
4. 用例测试c:进程1发出请求Request(1,1,0)——可以分配。
6 总结
在银行家算法这个系统之中,所采用的数据结构应是最基本的部分。银行家
算法的数据结构我们采用了一维数组与二维数组来存储,比如最大需求量
Max[][]、已分配资源数Allocation[][]、仍需求资源数Need[][]、以及系统可利用
的资源数、申请各类资源等数组。
数据结构虽然重要但却只是基础,而最主要的用以实现系统功能的应该有两
个部分,一是用银行家算法来判断,二是用安全性算法来检测系统的安全性。
首先,输入欲申请资源的进程以及其所申请的资源数,存放在Request数组
中。
然后,判断进程请求的资源数是否大于其所需的资源数,若大于则报错并返
回,若不大于则继续判断它是否大于系统在此时刻可利用的资源数,同样,如果
大于则报错并反回,如果不大于则进行预分配,之后再调用安全型算法检查。
最后,无论此次分配是否成功,我们都可以选择继续分配或者退出系统。
首先,Finish[]为布尔型,默认是False,即该进程未完成。而Work——即该
系统中可以用来工作的资源数——最开始为系统最初可以用的资源数。
然后,我们从第一个进程开始判断该进程未完成且其所需求的资源量不大于
该系统中可以用来工作的资源量这个条件是否成立,即Finish[]=False且
Need[][]
的资源量相加,存放于当前可用来工作的资源量当中,即
Work[]=Work[]+Allocation,并将Finish[]的值改为True。否则便将此进程的优先
级减一,排在队位,然后开始往后循环。
待所有的进程循环完毕,我们再次判断是否还存在进程的Finish[]=False,如
果仍存在,则说明系统没有安全序列,处于不安全状态,不可以进行分配;否则,
系统处于安全状态,将预分配变为实际分配,求出安全序列并且将实际分配后的
资源分配情况打印输出。
除此之外,在程序当中,我们也得强调一下对输入的合法性的判断。比如,
我们输入的欲申请资源的进程号没有在系统已存在的进程当中,或者进程号定义
为整型,但是却错输成字母等情况,我们需要对这些情况进行判断,让程序报错
返回而并非因错误而中断。
这样的情况处理起来比较麻烦,相当于对每次输入针对各种不同的情况都得
做判断。我也没有涵盖全部的输入,仅仅只是对输入的进程号不在已存在进程当
中、以及输入的操作选择不存在两种情况分别作了判断,并且针对第二种情况设
定了五次输入错误的话系统关闭的功能。而因为对于某些——比如进程号——本
来设定就是整型,因此对输入的是字母的判别因比较复杂而未能加上。
总之,银行家算法是避免死锁的主要方法,其思路在很多方面都非常值得我
们来学习借鉴。
参考文献
[1] 汤小丹,梁红兵,哲凤屏,汤子瀛.计算机操作系统. 西安:西安电子科技大学出版社,2007.
[2] 严蔚敏,吴伟民.数据结构. 北京:清华大学出版社,2006.
[3] 赵莉,杨国梁,孙喁喁,徐飞. Java程序设计教程. 西安:西安科技大学出版社,2009.
[4] http://wenku.baidu.com/view/32d7df73f242336c1eb95efa.html (百度文库:银行家算法报告)
[5] http://www.zhiwenweb.cn/default.asp?id=204 (志文工作室: 银行家算法模拟实现)
附录:源程序清单(核心代码)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace 操作系统
{
public class BankBase
{
int[] Avaliable;
int[] Max;
int[] Allocation;
int[] Need;
int[] Request;
int count = 0;
bool[] Finish = new bool[5];
public delegate void setText(string text);
public BankBase(Dictionary test,Form1 f)
{
Avaliable = new int[] { 3, 3, 2 };
this.bankDic = test;
f.textBoxOutput.AppendText("");
for (int i = 0; i
{
f.textBoxOutput.AppendText(bankDic.ElementAt(i).Key+" ");
}
f.textBoxOutput.AppendText("\r\n\r\n");
this.f = f;
Avaliable =Resource.BankBaseAvailable();
}
public BankBase(int[] Max, int[] Allocation, int[] Need)
{
this.Max = Max;
this.Allocation = Allocation;
this.Need = Need;
}
private Object obj = new Object();
Dictionary bankDic = new Dictionary();
Form1 f;
Input input = new Input();
public void RequestMethod(Object bankbase)
{
lock (obj) {
BankBase newBankbase = bankbase as BankBase;
string currentThreadName = Thread.CurrentThread.Name;
f.textBoxOutput.AppendText("\r\n\r\n");
input.groupBox1.Text = "" + currentThreadName + "";
input.ShowDialog();
Request = new int[] { Convert.ToInt32(input.textBoxRequestA.Text),
Convert.ToInt32(input.textBoxRequestB.Text), Convert.ToInt32(input.textBoxRequestC.Text) }; f.textBoxOutput.AppendText("" + currentThreadName + "" + Request[0] + "," + Request[1] + "," + Request[2] + ")\r\n\r\n");
f.textBoxOutput.AppendText("" + currentThreadName + "" + newBankbase.Need[0] + "," + newBankbase.Need[1] + "," + newBankbase.Need[2] + ")\r\n\r\n");
f.textBoxOutput.AppendText("" + Avaliable[0] + "," + Avaliable[1] + "," + Avaliable[2] + ")\r\n\r\n");
if (Request[0] > newBankbase.Need[0] || Request[1] > newBankbase.Need[1] || Request[2] > newBankbase.Need[2])
{
f.textBoxOutput.AppendText("\r\n\r\n");
Thread.CurrentThread.Abort();
}
else if (Request[0] > Avaliable[0] || Request[1] > Avaliable[1] || Request[2] > Avaliable[2])
{
f.textBoxOutput.AppendText("\r\n\r\n");
Thread.CurrentThread.Abort();
}
else
{
for (int i = 0; i
{
Avaliable[i] -= Request[i];
newBankbase.Allocation[i] += Request[i];
newBankbase.Need[i] -= Request[i];
}
}
if (SecurityMethod(ref bankDic, currentThreadName))
{
f.textBoxOutput.AppendText("\r\n\r\n\r\n\r\n"); }
else
{
f.textBoxOutput.AppendText("\r\n\r\n\r\n\r\n"); }
}
}
public bool SecurityMethod(ref Dictionary test, string test1)
{
int[] Work = new int[3];
bool[] finish = new bool[5];
int index;
String str = "";
Array.Copy(Avaliable, 0, Work, 0, Avaliable.Length);
for (int i = 0; i
{
finish[i] = false;
}
Dictionary newTest = test;
char[] c = test1.ToCharArray();
int c1 = Convert.ToInt32(c[1].ToString());
if (newTest.ElementAt(c1).Value.Need[0]
newTest.ElementAt(c1).Value.Need[1]
{
f.textBoxOutput.AppendText("\r\n\r\n");
Work[0] += newTest.ElementAt(c1).Value.Allocation[0];
Work[1] += newTest.ElementAt(c1).Value.Allocation[1];
Work[2] += newTest.ElementAt(c1).Value.Allocation[2];
index = c1;
finish[c1] = true;
str += test1;
int i = 0;
for (; i
{
if (i == index)
{
continue;
}
else
{
if (newTest.ElementAt(i).Value.Need[0]
newTest.ElementAt(i).Value.Need[1]
if (!finish[i])
{
Work[0] += newTest.ElementAt(i).Value.Allocation[0]; Work[1] += newTest.ElementAt(i).Value.Allocation[1]; Work[2] += newTest.ElementAt(i).Value.Allocation[2]; finish[i] = true;
str += " " + newTest.ElementAt(i).Key.ToString();
i = -1; }}}}}
else
{
return false;
}
bool right = true;
for (int i = 0; i
{
if (!finish[i]) { right = false; }
}
if (right)
{
f.textBoxOutput.AppendText("" + str + "\r\n\r\n");
return true;
}
else
{
for (int i = 0; i
{
Avaliable[i] += Request[i];
newTest.ElementAt(c1).Value.Allocation[i] -= Request[i]; newTest.ElementAt(c1).Value.Need[i] += Request[i]; }
return false;
}
}
public void Display()
{
foreach (var a in Finish)
{
Console.WriteLine("{0}", a);
}
}
}
}