- 银行家算法是荷兰学者 Dijkstra 为银行系统 设计的, 以确保银行在发放现金贷款时, 不会发生不能满足所有客户需要的情况。 后来该算法被用在操作系统中, 用于避免死锁(避免系统进入不安全状态) 。
- 在学习著名的银行家算法之前我们先要了解什么是安全序列。
什么是安全序列?
你是—位成功的银行家,手里掌握着100个亿的资金...
有三个企业想找你贷款,分别是企业B、企业A、企业T,为描述方便,简称BAT。
B表示:“大哥,我最多会跟你借70亿...”
A表示:“大哥,我最多会跟你借40亿...”
T表示:“大哥,我最多会跟你借50亿...”
然而...江湖中有个不成文的规矩:如果你借给企业的钱总数达不到企业提出的最大要求,那么不管你之前给企业借了多少钱,那些钱都拿不回来了...
刚开始,BAT三个企业分别从你这儿借了20、10、30亿...
| 最大需求 | 已借走 | 最多还会借 | |
|---|---|---|---|
| B | 70 | 20 | 50 |
| A | 40 | 10 | 30 |
| T | 50 | 30 | 20 |
此时手里还有40亿
B还想再借30亿,还敢借吗?
假如答应了B的请求
| 最大需求 | 已借走 | 最多还会借 | |
|---|---|---|---|
| B | 70 | 20 + 30 = 50 | 50 - 30 = 20 |
| A | 40 | 10 | 30 |
| T | 50 | 30 | 20 |
你现在手里只剩下10亿,如果B、A、T都提出再借 20亿的请求,那么任何一个企业 的需求都得不到满足...于是就处于不安全状态,可能会发生死锁!
- 我们再回到刚刚,手里还有40亿的时候,假如A想再借20亿,可以借吗?
| 最大需求 | 已借走 | 最多还会借 | |
|---|---|---|---|
| B | 70 | 20 | 50 |
| A | 40 | 10+20=30 | 30-20=10 |
| T | 50 | 30 | 20 |
此时手里还剩20亿
①接下来可以先把20亿全部借给T,等T把钱全部还回来了,手里就会有20+30=50亿,再把这些钱全借给B,B还钱后总共有50+20=70亿,最后再借给A,就不会存在不安全状态(顺序就是T->B->A)也即是安全序列之一,安全序列可以有多个!
②或者我们先借给A 10亿,等A还钱了手里就有 20+30 = 50亿,再给T 20亿,等T还钱了就有 50+30 = 80亿,最后再给B借....这也不会存在不安全状态(顺序就是A->T->B)也是安全序列之一!
综上我们可以发现有的资源请求不能答应,有的资源请求可以答应,比如我们给A借20亿后,我们依然可以找到像T->B->A这样的安全序列。
所以所谓安全序列, 就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有多个。
如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。
如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
例子(安全序列):
系统中有5个进程P0~P4,3种资源R0,R1,R2,初始数量为(10,5,7),则某一时刻的情况如下:
| 进程 | 最大需求(Max) | 已分配(Allocation) | 最多还需要(Need) |
|---|---|---|---|
| P0 | (7,5,3) | (0,1,0) | (7,4,3) |
| P1 | (3,2,2) | (2,0,0) | (1,2,2) |
| P2 | (9,0,2) | (3,0,2) | (6,0,0) |
| P3 | (2,2,2) | (2,1,1) | (0,1,1) |
| P4 | (4,3,3) | (0,0,2) | (4,3,1) |
- 资源总数(10,5,7),剩余可用资源(3,3,2)
总共已分配(7,2,5),还剩余(3,3,2),Max-Allocation=Need。
此时系统是否处于安全状态?
思路:尝试找出一个安全序列...{P1,P3,P0,P2,P4}
依次检查 剩余可用资源(3,3,2)是否能满足各进程的需求。
可满足P1需求,将P1加入安全序列,并更新剩余可用资源值为(5,3,2)
依次检查剩余可用资源(5,3,2)是否能满足剩余进程(不包括已加入安全序列的进程)的需求
- 第一轮:到P1的时候发现need<=Available,说明如果优先把资源分配给P1,那P1一定是可以顺利执行结束的。等P1结束了就会归还给Available。于是Available=(2,0,0)+(3,3,2)=(5,3,2)
第二轮:到P3也一样,资源数就会增加到(2,1,1)+(5,3,2)=(7,4,3)
第三轮:同理...
- 依次类推,共五次循环检查即可将5个进程都加入到安全序列中,最终可得到一个安全序列。该算法称为安全性算法。可以很方便地用代码实现以上流程,每一轮检查都从编号较小的进程开始检查,实际做题时可以更快速的得到安全序列。
- 还有一种更快的方法找到一个安全序列:
经过need和Available的初始对比发现,(3,3,2)可满足P1,P3,说明无论如何,这两个进程的资源需求一定是可以依次被满足的,因此P1,P3一定可以顺利的执行完,并归还资源。可把P1,P3都先加入安全序列。
所以Available=(3,3,2)+(2,0,0)+(2,1,1)=(7,4,3)
剩下的P0,P2,P4都可以被满足。同理,这些进程都可以加入安全序列。
于是,5个进程全部加入安全序列,说明此时系统处于安全状态,暂不可能发生死锁。
例子(不安全序列)
条件: ****资源总数(10,5,7),Available(3,3,2),Request0(2,1,1)
| 进程 | 最大需求(Max) | 已分配(Allocation) | 最多还需要(need) |
|---|---|---|---|
| P0 | (7,5,3) | (0,1,0) | (8,4,3) |
| P1 | (3,2,2) | (2,0,0) | (1,2,2) |
| P2 | (9,0,2) | (3,0,2) | (6,5,0) |
| P3 | (2,2,2) | (2,1,1) | (0,1,1) |
| P4 | (4,3,3) | (0,0,2) | (4,3,4) |
- 经过对比发现,(3,3,2)可满足P1,P3,说明无论如何,这两个进程的资源需求一定是可以依次被满足的,因此P1,P3一定可以顺利的执行完,并归还资源。可把P1,P3先加入安全序列。
Available=(3,2,2)+(2,0,0)+(2,1,1)=(7,4,3)
会发现剩下的P0,P2,P4中need都有资源比Available中的资源大,任何一个进程都不能被完全满足
于是,无法找到任何一个安全序列,说明此时系统处于不安全状态,有可能发生死锁(如果处于不安全状态后,还有进程申请资源,那就会发生死锁)
总结
假设系统中有n个进程,m种资源
可以用银行家算法预判本次分配是否会导致系统进入不安全状态
- 如果Request<=Need,那么申请通过进行下一步,否则失败。
- 如果Request<=Available,那么系统中无充足资源,Pi必须等待。
- 系统试探着把资源分配给进程Pi,并修改数据(并不是真正的分配,只是为了预判而修改数据)
Available = Available -Request;
Allocation[i,j] = Allocation[i,j] +Request[j];
Need[i,j] =Need[i,j] - Request[j]
如果说系统执行安全性算法之后检查资源分配后,系统是否处于安全状态。若安全,才正式分配;否则,恢复相应数据,让进程阻塞等待。
安全性算法步骤:
检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收,不断重复上述过程,看最终是否能让所有进程都加入安全序列