目录
一、死锁的概念
1.死锁的定义
各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进。
2.死锁与饥饿
死锁 | 饥饿 | |
产生的主要原因 | 资源竞争和进程的非法推进。 | 当系统中有多个进程同时申请某类资源时,由分配策略确定资源分配给进程的次序,有的分配策略可能是不公平的,即不能保证等待时间上界的存在。在这种情况下,即使系统未发生死锁,某些进程也可能长时间等待。 |
共同点 | 进程无法顺利向前推进的现象。 | |
主要差别 | 而死锁是因循环等待对方手里的资源而导致的,因此,如果有死锁现象,那么发生死锁的进程必然大于或等于两个 | 发生饥饿的进程可以只有一个 |
而发生死锁的进程必定处于阻塞态。 | 发生饥饿的进程可能处于就绪态(长期得不到CPU,如SIF算法的问题),也可能处于阻塞态(如长期得不到所需的 UO 设备,如上述举例); |
3.死锁产生的原因
(1)系统资源的竞争
(2)进程推进的顺序非法
4.死锁产生的必要条件
(1)互斥
在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
(2)不可剥夺
进程所获得的资源在未使用完之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
(3)请求和保持
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。(捧着碗里的,看着锅里的。)
(4)循环等待
存在一种进程资源的循环等待链,链中每个进程已获得的资源同时被链中下一个进程所请求。
二、死锁的处理策略
1.死锁的预防
预防死锁只需要破坏死锁产生的四个条件之一即可。
其中的破坏循环等待条件一般使用“顺序分配法”,即限制了资源的分配顺序。
(1)破坏互斥条件
方法 | 缺点 |
让资源可以互斥使用 | 有些资源必须互斥使用,破坏互斥条件会导致系统不安全,这种方式不可行。 |
(2)破坏不可剥夺条件
方法 | 缺点 |
当一个已经保持了某些不可剥夺资源的进程,请求新的资源而得不到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。这意味着,进程已占有的资源会被暂时释放,或者说是被剥夺了,从而破坏了不可剥夺条件。 | 该策略实现起来比较复杂。释放已获得的资源可能造成前一阶段工作的失效,因此这种方法常用于状态易于保存和恢复的资源,如CPU的寄存器及内存资源,一般不能用于打印机之类的资源。反复地申请和释放资源既影响进程推进速度,又增加系统开销,进而降低系统吞吐量。 |
(3)破坏请求保持条件
方法 | 缺点 |
1)采用预先静态分配方法,即进程在运行前一次申请完它所需要的全部资源。在它的资源未满足前,不让它投入运行。在进程的运行期间,不会再提出资源请求,从而破坏“请 求”条件。在等待期间,进程不占有任何资源,从而破坏了“保持”条件。 | 方法一的实现简单,但缺点也显而易见,系统资源被严重浪费,其中有些资源可能仅在运行初期或快结束时才使用。而且还会导致“饥饿"现象,由于个别资源长期被其他进程占用,将导致等待该资源的进程迟迟不能开始运行。方法二则改进了这些缺点。 |
2)允许进程只获得运行初期所需的资源后,便可开始运行。进程在运行过程中再逐步释放已分配给自己且已使用完毕的全部资源后,才能请求新的资源。 |
(4)破坏循环等待条件
方法 | 缺点 |
为了破坏循环等待条件,可以采用顺序资源分配法。首先给系统中的各类资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源(编号相同的资源)一次申请完。也就是说,一个进程只在已经占有小编号的资源时,才有资格申请更大编号的资源。按此规则,已持有大编号资源的进程不可能再逆向申请小编号的资源,因此不会产生循环等待的现象。 | 这种方法的缺点:编号必须相对稳定,因此不便于增加新类型设备;尽管在编号时己考虑到大多数进程使用这些资源的顺序,但是进程实际使用资源的顺序还是可能和编号的次序不一致,这就会造成资源的浪费;此外,必须按规定次序申请资源,也会给用户编程带来麻烦。 |
2.死锁的避免
银行家算法
“在资源分配之前预先判断这次分配是否会导致系统进入不安全状态。”
银行家算法虽然会通过检测是否存在安全序列来判断申请资源的请求是否合法,但安全序列并不是唯一的,也不是固定的,它只是一种可能的分配方案而不是一种必须遵循的规则,银行家算法更没有给出固定的申请资源的顺序。
银行家算法是最著名的死锁避免算法,其中的最大需求矩阵 Max 定义了每个进程对 m类资源的最大需求量。
- 数据结构
假设系统中有n个进程,m类资源,在银行家算法中需要定义下面4个数据结构。
1)可利用资源向量 Available:含有m个元素的数组,其中每个元素代表一类可用的资源数
目。Available[j]=K表示此时系统中有K个R类资源可用。
2)最大需求矩阵 Max: n x m矩阵,定义系统中n个进程中的每个进程对 m类资源的最大需求。Max[ i , j ]=K表示进程P需要R,类资源的最大数目为K。
3)分配矩阵 Allocation: n x m矩阵,定义系统中每类资源当前已分配给每个进程的资源数。Allocation[ i , j ]=K表示进程P,当前已分得R,类资源的数目为 K。
4)需求矩阵 Need: nxm矩阵,表示每个进程接下来最多还需要多少资源。Need[i,j]=K表示进程P 还需要R类资源的数目为K。
上述三个矩阵间存在下述关系:
Need = Max-Allocation
通常,Max 矩阵和 Allocation 矩阵是题中的己知条件,而求出 Need 矩阵是解题的第一步。
- 银行家算法
- 安全性算法
设置工作向量 Work,表示系统中的剩余可用资源数目,它有m个元素,在执行安全性算法令Work= Available。
①初始时安全序列为空。
②从 Need 矩阵中找出符合下面条件的行:该行对应的进程不在安全序列中,而且该行小于或等于 Work 向量,找到后,将对应的进程加入安全序列;若找不到,则执行步骤④。
③进程P进入安全序列后,可顺利执行,直至完成,并释放分配给它的资源,所以应执行Work= Work+Allocation[i],其中 Allocation[i]是 Allocation 矩阵中对应的行,返回步骤②.
④)若此时安全序列中已有所有进程,则系统处于安全状态,否则系统处于不安全状态。
3.死锁的检测和解除
如果系统中既不采取预防死锁的措施,也不采取避免死锁的措施,系统就很可能发生死锁。在这种情况下,系统应当提供两个算法:死锁的检测和解除。
在死锁的检测和解除中,系统为进程分配资源时不采取任何措施,但提供死锁检测和解除的手段,一旦检测到系统发生死锁,就立即采取相应的措施来解除死锁,因此不用关心进程所需的总资源量。
(1)死锁的检测
为了能对系统是否已发生了死锁进行检测,必须:
- 用某种数据结构来保存资源的请求和分配信息;
资源分配图:
进程结点 | 对应一个进程 |
资源结点 | 对应一类资源,一类资源可能有多个 |
进程结点-->资源结点 | 表示进程想申请几个资源(每条边代表一个) |
资源节点-->进程结点 | 表示已经为进程分配了几个资源(每条边代表一个) |
- 提供一种算法,利用上述信息来检测系统是否已进入死锁状态。
如果某时刻系统的资源分配图是不可简化的,那么此时系统死锁[死锁定理]。
如果系统中剩余的可用资源数足够满足进程的需求,那么这个进程暂时是不会阻塞的,可以顺利地执行下去。如果这个进程执行结束了把资源归还系统,就可能使某些正在等待资源的进程被激活,并顺利地执行下去。相应的,这些被激活的进程执行完了之后又会归还一些资源,这样可能又会激活另外一些阻塞的进程...
如果按上述过程分析,最终能消除所有边,就称这个图是可完全简化的。此时一定没有发生死锁(相当于能找到一个安全序列)
如果最终不能消除所有边,那么此时就是发生了死锁
最终还连着边的那些进程就是处于死锁状态的进程。
(2)死锁的解除
一旦检测出死锁的发生,就应该立即解除死锁。
补充:并不是系统中所有的进程都是死锁状态,用死锁检测算法化简资源分配图后,还连着边的那些进程就是死锁进程解除死锁的主要方法有:
1.资源剥夺法。 | 挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给1.其他的死锁进程。但是应防止被挂起的进程长时间得不到资源而饥饿。 |
2.撤销进程法(或称终止进程法)。 | 强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这种方式的优点是实现简单,但所付出的代价可能会很大。因为有些进程可能已经运行了很长时间,已经接近结束了,一旦被终止可谓功亏一篑,以后还得从头再来。 |
3.进程回退法。 | 让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。 |
三、死锁公式
n>p·(r-1)
[注]n是系统中临界资源的总数,p是并发进程的个数,r是每个进程所需要临界资源的个数。
理解:
在操作系统中,死锁的产生需要同时满足四个条件:互斥条件、请求和保持条件、不可剥夺条件、循环等待条件。
用数学方法推导死锁公式通常基于资源分配图和有向图理论。
假设系统中有 n 个进程和 m 种资源。对于每个进程 Pi,它可能需要 Ri1 个资源类型 1,Ri2 个资源类型 2,以此类推。系统中每种资源的总数分别为 T1、T2、…、Tm。
当考虑死锁情况时,我们要确定在什么情况下进程会陷入死锁。如果每个进程都已经分配了一部分资源并且正在等待其他资源,同时这些等待的资源被其他进程持有,就可能发生死锁。
可以通过建立不等式组来表示资源分配情况。例如,对于资源类型 1,已分配的资源总量加上正在请求的资源总量不能超过该资源的总数,即 Σ(Ri1)(已分配给各个进程的资源类型 1 的数量)+ Σ(Pi 请求的资源类型 1 的数量) ≤ T1。
对于所有资源类型都满足这样的不等式。如果这些不等式同时成立,并且存在循环等待,就可能发生死锁。
通过分析这些不等式和条件,可以推导出一些判断死锁是否可能发生的公式。
例如,最常见的死锁公式是“进程数×(每个进程所需资源数 - 1) + 1 ≤ 资源总数”,这个公式是在特定条件下推导出来的,即每个进程都需要相同数量的资源,且资源请求是一次性的等。
推导过程大致如下:假设每个进程需要 k 个资源,有 n 个进程,那么总共需要 n×k 个资源才能让所有进程都运行完毕。但由于每个进程在得到 k - 1 个资源时会等待最后一个资源,所以实际上系统中只要有 n×(k - 1) + 1 个资源就能保证不会发生死锁,因为当有一个资源剩余时,可以分配给某个进程,使其完成并释放所有资源,从而打破死锁状态。如果资源总数小于这个值,就可能发生死锁。