系列文章目录
计算机操作系统-计算机系统中的死锁
文章目录
- 系列文章目录
- 前言
- 一、资源问题:
- 计算机系统当中的死锁:
- 二、死锁的定义、必要条件和处理方法:
- 1.死锁的定义:
- 2.产生死锁的必要条件:
- 3.处理死锁的方法:
- 三、避免死锁:
- 1.系统安全状态的定义:
- 2.安全状态的例子:
- 3.不安全状态的例子:
- 4.利用银行家算法避免死锁:
- 5.具体示例:
- 总结
前言
在第二章中,我们已经涉及到死锁的概念,例如系统中只有一个扫描仪R1和一台刻录机R2,有两个进程p1和p2,他们都准备将扫描的文档刻录在CD光盘上,进程1先请求扫描仪R1并获得成功,进程2先请求刻录机R2也获得成功,后来P1又请求CD刻录机,因它已被分配给了P2而阻塞,P2又请求扫描仪,也因被分配给了进程1而阻塞,此时两个进程都被阻塞,双方都希望对方能释放出自己所需要的资源,但他们谁都因不能获得自己所需的资源去继续运行,从而无法释放出自己占有的资源,并且一直处于这样的僵持状态而形成死锁,又如,在第二章的哲学家进餐问题中,如果每个哲学家因饥饿都拿起了他们左边的筷子,当每一个哲学家又试图去拿起他们右边的筷子时,将会因为无筷子可拿而无限期地等待,从而产生死锁问题。接下来我们将对死锁发生的原因,如何预防和避免死锁等问题作较详细的介绍。
一、资源问题:
在系统中有许多不同类型的资源,其中可以引起死锁的主要是,需要采用互斥访问方法的、不可以被抢占的资源,即前面介绍的临界资源。系统中这类资源有很多,如打印机,数据文件,队列,信号量等。
可抢占性资源和不可抢占性资源
(1)可抢占性资源(可剥夺)
可把系统中的资源分成两类,一类是可抢占性资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统抢占。例如优先级高的进程可以抢占优先级低的进程的处理机。又如可把一个进程从一个存储区转移到另一个存储区,在内存紧张时,还可将一个进程从内存调出到外存上,即抢占该进程在内存空间,可见,CPU和主存均属于可抢占性资源。对于这类资源是不会引起死锁了。
(2)不可抢占性资源(非剥夺)
另一类资源是不可抢占性资源,即一旦系统把某资源分配给该进程,就不能将它强行收回,只能在进程用完后自行释放。例如,当一个进程已开始刻录光盘时,如果突然将刻录机分配给另一个进程,其结果必然会损坏正在刻录的光盘,因此只能等刻好光盘后由进程自己释放刻录机。另外磁带机,打印机等也都属于不可抢占性资源。
计算机系统中的死锁:
1.竞争不可抢占性资源引起死锁
2.竞争可消耗资源引起死锁
3.进程推进顺序不当引起死锁
二、死锁的定义、必要条件和处理方法:
1.死锁的定义:
死锁指两个或多个进程在执行过程中,因争夺资源而陷入相互等待的僵局,若无外力干预,这些进程将无法向前推进。此时,系统处于停滞状态,资源被无效占用。
2.产生死锁的必要条件:
虽然进程在运行过程中可能会发生死锁,但产生进程死锁是必须具备一定条件的,综上所述不难看出,产生死锁必须同时具备下面四个必要条件,只要其中任一个条件不成立,死锁就不会发生。
(1)互斥条件:进程对所分配到的资源进行排他性使用,即在一段时间内,某资源只能被一个进程占用。如果此时还有其他进程请求该资源,则请求进程只能等待,直至占用该资源的进程用毕释放。
(2)请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
(3)不可抢占条件:进程已获得的资源在未使用之前不能被抢占,只能在进程使用完时由自己释放。
(4)循环等待条件:在发生死锁时,必然存在一个进程——资源的循环链,即进程集合(p0,p1,p2....pn)中的p0正在等待一个p1占用的资源,p1正在等待p2占用的资源,.....,pn正在等待已被p0占用的资源。
3.处理死锁的方法:
(1)预防死锁
(2)避免死锁
(3)检测死锁
(4)解除死锁
上述的四种方法,从(1)到(4)对死锁的防范程度逐渐减弱,但对应的是资源利用率的提高,以及进程因资源因素而阻塞的频度下降。
三、避免死锁:
在死锁避免方法中,把系统的状态分为安全状态和不安全状态。当系统处于安全状态时,可避免发生死锁。反之,当系统处于不安全状态时,则可能进入死锁状态。
系统安全状态的定义
安全状态(Safe State):
系统存在至少一个安全序列(Safe Sequence),使得所有进程都能按此顺序依次获得所需资源并完成执行,而不会导致死锁。
- 安全序列:假设进程按顺序 P1,P2,…,Pn 执行,每个进程 Pi 的资源请求都可以被当前可用资源满足,且执行完成后会释放所有资源,从而为后续进程提供更多可用资源。
不安全状态(Unsafe State):
不存在这样的安全序列。此时系统可能进入死锁,但不安全状态不一定会导致死锁(取决于后续资源请求的实际情况)。
虽然并非所有非安全状态都必然会转为死锁状态,但当系统进入不安全状态后,就有可能进入死锁状态(不一定)。反之,只要系统处于安全状态,系统便不会进入死锁状态。
安全状态的例子
场景- 资源类型:假设系统有 12 个同类型资源(如内存块)。
- 进程:3 个进程 P1,P2,P3。
- 资源分配状态:
进程 最大需求(Max) 已分配(Allocated) 剩余需求(Need = Max - Allocated) P1 10 5 5 P2 4 2 2 P3 9 2 7 - 当前可用资源:12−(5+2+2)=3。
判断安全状态
- 初始可用资源:3。
- 寻找可进程:
- P2 的剩余需求为 2 ≤ 可用资源 3 → 可分配。
- 假设分配给 P2,P2 执行完成后释放资源:可用资源变为 3+2=5。
- 更新可用资源后继续寻找:
- P1 的剩余需求为 5 ≤ 可用资源 5 → 可分配。
- P1 执行完成后释放资源:可用资源变为 5+5=10。
- 最后处理 P3:
- P3 的剩余需求为 7 ≤ 可用资源 10 → 可分配。
- P3 执行完成后释放资源:可用资源变为 10+2=12。
安全序列:P2→P1→P3(或其他可行顺序)。
结论:系统处于安全状态。
不安全状态的例子
假设在初始状态下,进程 P3 先请求 1 个资源:
资源分配状态变化
- 已分配资源更新:P3 的已分配从 2 → 3,剩余需求从 7 → 6。
- 可用资源:12−(5+2+3)=2。
进程 | 最大需求 | 已分配 | 剩余需求 |
---|---|---|---|
P1 | 10 | 5 | 5 |
P2 | 4 | 2 | 2 |
P3 | 9 | 3 | 6 |
判断是否存在安全序列
- 初始可用资源:2。
- 检查可满足的进程:
- P2 需要 2 ≤ 2 → 分配后可用资源变为 2+2=4。
- 后续检查:
- P1 需要 5 ≤ 4 → 不满足。
- P3 需要 6 ≤ 4 → 不满足。
- 无其他进程可分配。
结论:无法找到安全序列 → 系统处于不安全状态。
此时系统会拒绝 P3 的资源请求,强制其等待,避免进入不安全状态。
安全状态的实际意义
- 动态资源分配:
每次资源分配前,系统通过算法(如银行家算法)模拟分配后的状态,仅允许导致安全状态的请求。 - 资源利用率:
安全状态确保资源分配不会因过度保守而浪费,也不会因过度冒险导致死锁。 - 应用场景:
主要用于理论模型或关键系统(如某些数据库管理系统),因实时计算安全状态可能带来性能开销。
利用银行家算法避免死锁:
1.银行家算法中的数据结构:
为了实现银行家算法,在系统中必须设置这样四个数据结构,分别用来描述系统中可利用的资源、所有进程对资源的最大需求,系统中的资源分配,以及所有进程还需要多少资源的情况。
(1)可利用资源向量Available。(可分配的)这是一个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目,其初始值时系统中配置的该类全部可用资源的数目,其数值随该类的资源的分配和回收而动态地改变。如果Available[j] = k,则表示系统中现有Rj类资源 K个。
(2)最大需求矩阵Max。这是一个n*m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=k,则表示进程i需要Rj类资源的最大数目为K。
(3)分配矩阵Allocation(已经分配的) 这也是一个n*m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j] = k,则表示进程i当前已分得Rj类资源的数目为k。
(4)需求矩阵Need。这也是一个n*m的矩阵,用来表示每一个进程尚需的各类资源数。如果Need[i,j]=k,则表示进程i还需要Rj类资源K个方能完成其任务。
Need[i,j] = Max[i,j]-Allocation[i,j]
处理资源请求
当进程 Pi 请求资源时,系统执行以下步骤:
Step 1: 检查请求合法性
- Request ≤ Need[i],继续;否则拒绝(非法请求)。
Step 2: 检查可用资源是否足够
- Request ≤ Available,继续;否则让进程等待。
一定要先满足上面的两个条件
Step 3: 模拟分配资源
- 临时更新数据:
- Available=Available−Request
- Allocation[i]=Allocation[i]+Request
- Need[i]=]−Request
Step 4: 执行安全状态检查
- 调用安全状态检查算法(见下文)。
- 如果安全 → 正式分配资源;
- 不安全 → 撤销模拟分配,让进程等待
安全状态检查算法
目标:判断是否存在一个安全序列,使得所有进程都能按顺序完成。
Step 1: 初始化
- 定义两个向量:
- Work=Available(当前可用资源副本)
- Finish[1..n]=false(标记进程是否完成)
Step 2: 寻找可完成的进程
- 遍历所有进程,找到满足以下条件的进程 Pi:
- Finish[i]=false
- Need[i]≤Work(即进程的剩余需求 ≤ 当前可用资源)
Step 3: 模拟进程完成
- 若找到 Pi:
- Work=Work+Allocation[i](释放其占有的资源)
- Finish[i]=true
- 重复 Step 2,直到所有进程完成或找不到可完成的进程。
Step 4: 判断结果
- 如果所有 Finish[i]=true → 系统安全,存在安全序列;
- 否则 → 系统不安全。
具体示例(三种资源类型)
假设系统有三种资源 R1、R2、R3,初始状态如下:
进程 | Max (R1, R2, R3) | Allocation (R1, R2, R3) | Need (R1, R2, R3) |
---|---|---|---|
P1 | (7, 5, 3) | (0, 1, 0) | (7, 4, 3) |
P2 | (3, 2, 2) | (2, 0, 0) | (1, 2, 2) |
P3 | (9, 0, 2) | (3, 0, 2) | (6, 0, 0) |
P4 | (2, 2, 2) | (2, 1, 1) | (0, 1, 1) |
P5 | (4, 3, 3) | (0, 0, 2) | (4, 3, 1) |
当前可用资源:Available=(3,3,2)(总资源数减去已分配资源)。
场景:进程 P1 请求资源 (0, 2, 0)
- 检查合法性:
- Request=(0,2,0)≤Need[P1]=(7,4,3) → 合法。
- 检查可用性:
- Request=(0,2,0)≤Available=(3,3,2) → 合法。
- 模拟分配:
- Available=(3,3,2)−(0,2,0)=(3,1,2)
- Allocation[P1]=(0,1,0)+(0,2,0)=(0,3,0)
- Need[P1]=(7,4,3)−(0,2,0)=(7,2,3)
- 安全状态检查:
- Step 1:初始化 Work=(3,1,2),Finish=[false,false,false,false,false]。
- Step 2:寻找可完成的进程:
- P4:Need[P4] = (0, 1, 1) ≤ Work = (3, 1, 2) → 符合条件。
- Step 3:模拟 P4 完成:
- Work=(3,1,2)+Allocation[P4]=(2,1,1)→(5,2,3)
- Finish[P4]=true
- 继续寻找:
- P2:Need[P2] = (1, 2, 2) ≤ Work = (5, 2, 3) → 不满足(R2分量2 > Work的2)。
- P3:Need[P3] = (6, 0, 0) ≤ Work = (5, 2, 3) → 不满足(R1分量6 > 5)。
- P5:Need[P5] = (4, 3, 1) ≤ Work = (5, 2, 3) → 不满足(R2分量3 > 2)。
- P1:Need[P1] = (7, 2, 3) ≤ Work = (5, 2, 3) → 不满足(R1分量7 > 5)。
- 无法完成所有进程 → 系统不安全,拒绝请求!
总结
以上就是今天要讲的内容,我们简单的介绍了一下死锁,包括定义和产生死锁的必要条件,处理死锁的方法,包括详细讲了避免死锁的算法和系统安全状态等等,接下来会持续更新的,谢谢大家。