什么是安全序列
所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有多个。
如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。当然,如果有进程提前归还了一些资源,那系统也就有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。
如果系统处于安全状态,就一定不会发生死锁。如果系统进入了不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
因此可以在资源分配之前预先预判这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是"银行家算法"的核心思想。
银行家算法
银行家算法是荷兰学者Dijkstra为银行系统设计的,以确保银行在发放现金贷款时,不会发生不能满足所有客户需要的情况。后来该算法被用在操作系统中,用于避免死锁。
核心思想:在进程提出资源申请时,先预判此处分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让线程先阻塞等待。
比如:系统中有5个进程P0~04,3种资源R0~R2,初始数量为(10,5,7),则某一时刻的情况可表示如下:
此时系统是否处于安全状态?
思路:尝试找出一个安全序列,依次检查剩余可用资源(3,3,2)是否能满足各进程的需求。
比如如果优先把资源分配给P1,那P1一定是可以顺利执行结束的。等P1结束了就归还资源。于是资源数就可以增加到(2,0,0)+(3,3,2)=(5,3,2)
将P1加入安全序列,并更新剩余可用资源值为(5,3,2)
依次检查剩余可用资源(5,3,2)是否满足剩余进程(不包括已加入安全序列的进程)的需求
可满足P3需求,将P3加入安全序列。并更新剩余进程(不包括已加入安全序列的进程)的需求
以此类推,共五次循环检查即可将5个进程都加入安全序列中,最终可得到一个安全序列。
说明此时系统处于安全状态,暂不可能发生死锁。
该算法称为安全性算法。可以很方便地用代码实现以上流程,每一轮检查都从编号较小地进程开始检查。
分析
假设系统中有n个进程,m种资源
每个进程在运行前先声明对各种资源地最大需求数,则可用一个n*m的矩阵(可用二维数组实现)表示所有进程对各种资源的最大需求数。不妨称为最大需求矩阵Max,Max[i,j] = K表示进程Pi最多需要K个资源Rj。同理,系统可用用一个n * m的分配矩阵Allocation表示对所有进程的资源分配情况。Max-Allocation = Need矩阵,表示各进程最多还需要多少各类资源。
另外,还要用一个长度为m的一维数组Available表示当前系统中还有多少可用资源。
某进程Pi向系统申请资源,可用一个长度为m的一维数组表示本次申请的各种资源量。
可用银行家算法预判本次分配是否会导致系统进入不安全状态:
1.如果便转向2,否则认为出错(因为它所需要的资源数已超过它所宣布的最大值)。
2.如果,便转向3,否则表示尚未足够资源,Pi必须等待。
3.系统试探着把资源分配给进程Pi,并修改想要的数据(并非真的分配,修改数值只是为了做预判):
4.操作系统执行安全性算法,检查此次资源分配后,系统是否处于安全状态。若安全,才正式分配;否则,恢复相应数据,让进程阻塞等待。
数据结构:
长度为m的一维数组Available表示还有多少可用资源
n*m矩阵Max表示各进程对资源的最大需求数
n*m矩阵Allocation表示已经给各进程分配了多少资源
Max - Allcoation = Need矩阵表示各进程最多还需要多少资源
用长度为m的一维数组Request表示进程此处申请的各种资源数
银行家算法步骤:
1.检查此次申请是否超过之前声明的最大需求数
2.检查此时系统剩余的可用资源是否满足这次请求
3.试探着分配,更该各数据结构
4.用安全性算法检查此次分配是否会导致系统进入不安全状态
安全性算法步骤:
检查当前的剩余可用资源是否满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。
不断重复上述过程,看最终是否能让所有进程都加入安全序列。
/**
* Created with IntelliJ IDEA.
* Description: 银行家算法
* User: OoOoOoOO
* Date: 2021-12-06
* Time: 15:11
*/
public class BankerAlgorithm {
// 开始时需要指定进程的数量,和资源的数量
static int processNumber = 5;
static int resourcesNumber = 3;
// MAX为最大需求矩阵
static int[][] MAX;
// Allocation所有进程已分配资源的情况
static int[][] Allocation;
// Need = MAX - Allocation
static int[][] Need;
// Request表示本次请求的资源
static int[] Request;
// Available剩余可用资源
static int[] Available;
public static void main(String[] args) {
MAX = new int[][]{
{7,5,3},
{3,2,2},
{9,0,2},
{2,2,2},
{4,3,3}
};
Allocation = new int[][]{
{0,1,0},
{2,0,0},
{3,0,2},
{2,1,1},
{0,0,2}
};
Need = new int[processNumber][resourcesNumber];
for (int i = 0; i < processNumber; i++){
for (int j = 0; j < resourcesNumber; j++){
Need[i][j] = MAX[i][j] - Allocation[i][j];
}
}
Request = new int[resourcesNumber];
Available = new int[]{10,5,7};
for (int i = 0; i < processNumber; i++){
for (int j = 0; j < resourcesNumber; j++){
Available[j] -= Allocation[i][j];
}
}
System.out.println(Arrays.toString(Available));
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请选择要分配资源的进程:0~"+processNumber);
int process = scanner.nextInt();
for (int i = 0; i < resourcesNumber; i++){
System.out.println(String.format("请输入分配的第%d种资源的数量", i + 1));
Request[i] = scanner.nextInt();
}
if (oneStep(process)){
System.out.println(String.format("进程%d资源分配完成",process));
System.out.println("Need:");
for (int i = 0; i < processNumber; i++){
System.out.print(i+"== ");
System.out.println(Arrays.toString(Need[i]));
}
System.out.print("Available: ");
System.out.println(Arrays.toString(Available));
System.out.println("Allocation: ");
for (int i = 0; i < processNumber; i++){
System.out.print(i +"== ");
System.out.println(Arrays.toString(Allocation[i]));
}
}
}
}
/**
* todo : 第一步检验
*/
static boolean oneStep(int process){
for (int i = 0; i < resourcesNumber; i++){
if (Request[i] > Need[process-1][i]){
System.out.println("超过最大限制!");
return false;
}
}
return twoStep(process);
}
static boolean twoStep(int process){
for (int i = 0; i < resourcesNumber; i++){
if (Request[i] > Available[i]){
System.out.println(String.format("进程%d所需要的资源尚未满足,进入阻塞状态",process));
return false;
}
}
// 进入安全性算法
return SafeCheck(process);
}
static boolean SafeCheck(int process){
// 需要先把Allocation、Need、Available数组拷贝一份
int[][] CopyAllocation = new int[processNumber][resourcesNumber];
int[][] CopyNeed = new int[processNumber][resourcesNumber];
int[] CopyAvailable = new int[resourcesNumber];
for (int i = 0; i < processNumber; i++){
System.arraycopy(Allocation[i],0,CopyAllocation[i],0,resourcesNumber);
System.arraycopy(Need[i],0,CopyNeed[i],0,resourcesNumber);
}
System.arraycopy(Available, 0, CopyAvailable, 0, resourcesNumber);
// Copy完成
// 资源分配
for (int i = 0; i < resourcesNumber; i++){
CopyAvailable[i] -= Request[i];
CopyAllocation[process-1][i] += Request[i];
CopyNeed[process-1][i] -= Request[i];
}
// 开始检查
int count = 0;
// isVisited表示进程符合条件
boolean[] isVisited = new boolean[processNumber];
while(count < processNumber){
int prevCount = count;
for (int j = 0; j < processNumber; j++) {
// 已经分配完成
if (isVisited[j]){
continue;
}
if (isChecked(CopyNeed[j], CopyAvailable)){
// 更新CopyAvailable
isVisited[j] = true;
for (int i = 0; i < CopyNeed[j].length; i++) {
CopyAvailable[i] += CopyNeed[j][i];
}
count++;
}
}
if (prevCount == count){
System.out.println("资源分配不合理 进入阻塞");
return false;
}
}
Need = CopyNeed;
Allocation = CopyAllocation;
for (int i = 0; i < resourcesNumber; i++){
Available[i] -= Request[i];
}
return true;
}
static boolean isChecked(int[] o1, int[] o2){
int length = o1.length;
for (int i = 0; i < length; i++){
if (o1[i] > o2[i]){
return false;
}
}
return true;
}
}