参考视频:王道计算机考研 操作系统_哔哩哔哩_bilibili
目录
第二章 进程管理4
五、管程
1.为什么要引入管程
(此图来自MOOC)
针对信号量、PV操作的缺点,提出管程。
2.管程的定义和基本特征
Hansen(创作者)的管程定义:
一个管程定义了①一个数据结构和能为并发进程所执行(在该数据结构上)的②一组操作,这组操作能同步进程和改变管程中的数据。
其中还有③管程名以及④管程的初始化语句。
1️⃣管程是一种特殊的软件模块,不仅有共享数据,而且有对数据操作的代码。只有通过调用这些操作的过程函数,才能对管程内的数据进行修改。每次仅允许一个进程进入管程执行一个过程。
2️⃣管程是半透明的,管程的外部过程实现了某些功能,至于这些功能是怎样实现的,在其外部则是不可见的。
3️⃣由于管程的存在,生产者进程和消费者进程简单了许多。对于缓冲区互斥、进程互斥、进程同步等等,均是由管程和编译器所关心的问题。
六、死锁与饥饿
1.什么是死锁、饥饿
死锁:参与死锁的进程都进入无限等待的状态,都占有资源并不会主动释放,导致各进程都阻塞。
饥饿:资源分配策略不公平引起,不一定处于等待(阻塞态),可能忙等(就绪态),即此进程不断地请求此资源,但是很长时间中资源即使释放也不会分配给它。
死循环是可以处于运行态不断循环运行的。
补充:
当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿。饥饿到一定程度的进程所赋予的使命即使完成也不再具有实际意义时,称该进程被饿死(starved to death)。
饥饿是指没有时间上界的等待,有两种情况:
- 排队等待(队列)
- 忙式等待(忙式等待条件下发生的饥饿称为活锁(live lock))
2.死锁产生的原因
Coffman条件(必要条件):
资源独占(互斥条件):只能互斥访问的资源。
不可抢占(不剥夺条件):已获得的资源不可被抢走。
保持申请(请求和保持条件):已占有部分资源,却还在不断申请新的资源。
循环等待:资源的循环等待链,链中每个进程都获得部分资源的同时,又请求其他进程所持有的资源。
3.什么时候会发生死锁
当对不可剥夺资源的分配不合理,就可能导致死锁。
3.1 定义死锁的时刻
- 无限等待发生时【已占有一个资源的两个进程正在申请对方的资源】
- 等待发生前(已注定死锁)【两个进程各占一个资源时注定接下来会发生死锁】
3.2 死锁类型
1️⃣竞争资源引起的死锁
(1)不同种资源。
(2)同种资源。P1占有三个打印机,P2占有一个,互相不释放。
2️⃣进程通讯引起的死锁
P1、P2、P3三个进程互相等待对方的消息,只有收到消息才能发送消息。
3️⃣其他原因
After you / after you 问题
判断对方是否申请资源,一旦发现对方想用就放弃资源的竞争,可能最后双方都等着对方使用。
4.死锁的处理策略——死锁的预防
4.1 处理死锁的方法
破坏四个必要条件中的任意一个都可以消除死锁。死锁可以通过动态或静态的方式来避免:
【目的在于不发生死锁】①死锁预防(静态);②死锁避免(动态);
【发生死锁后如何处理】③死锁检测;④死锁恢复。
4.2 预防死锁
基本思想:对于进程有关资源申请命令规定某种协议,所有进程都遵守就不会进入死锁。
①破坏互斥条件:把互斥使用的资源改为共享使用。
如SPOOLing技术改造打印机。若进程1和进程2同时争抢一个打印机就有产生死锁的可能。SPOOLing技术就相当于拥有一个输入进程,将进程都排队放进去,然后在按序处理。
缺点:并不是所有资源都可以改为共享的,很多时候还需要保护互斥。
②破坏不剥夺条件:资源得不到满足就主动释放;优先级辅助强制剥夺。
缺点:
- 实现复杂;
- 申请大部分后释放,不易保存状态,若一直重新申请,容易“饥饿”;
- 反复申请和释放,增加开销。
③破坏保持申请(请求和保持)条件:预先分配策略。一次性申请全部资源,若系统不能满足全部要求,就不分配资源;否则,一次性分配完全部资源,投入运行。
缺点:
- 资源利用率低。进程获得了所需全部的资源,但不会一开始就全部使用,而那些未使用但已经被申请的资源处于闲置,造成资源浪费;
- 运行前可能并不知道所需全部资源。在条件结构中,可以有多个分支,因此,有的分支上的资源都申请了却都未使用;
- “饥饿”(下图)。资源1和资源2一直被A和B使用,C永远凑不齐所有资源。
④破坏循环等待条件:有序分配策略。所有资源排序编号,规定进程必须按照资源编号从小到大的次序申请资源。(即已申请大编号资源的进程,无法申请小编号资源)
缺点:
- 给资源类一个合理的编号比较难;
- 暂不需要使用的资源也可能被提前申请,增加资源浪费。
5.死锁的处理策略——死锁的避免
进程向系统请求资源:
系统可满足时,需要进行安全检测:
若在预分配中,参与并发运行的进程都能运行完,就安全,才能真正分配资源;否则,不安全。
5.1 安全状态与安全进程序列
如果系统中所有进程能够按照某一种次序(如 <p1, p2, p3, ··· , pn>)依次顺利进行,就说系统处于安全状态,而这个次序就是安全序列(可能有多个)。因此,安全状态就是非死锁状态,而不安全状态不一定是死锁状态。
5.2 银行家算法
与实际的银行家借贷有一点不同,这里没有利息。银行家算法借鉴了银行家发放贷款的思想。假设银行家有100万元,3个借贷者D1、D2、D3,分别借贷50万元、40万元、30万元,应当避免出现下述情况:已分别贷给D1、D2、D3资金40万元、35万元、25万元,此时3位借贷者还需要10万元 、5万元、5万元,而银行家已没有资金,无法继续周转,就陷入了死锁。
核心思想:当新进程进入系统时,必须声明其最大资源需求量。当进程申请资源而且系统能够满足请求时,系统将判断:如果系统分配所申请的资源,系统的状态是否安全。安全则分配并让申请者继续;否则不分配,并让申请者等待。
为实现银行家算法,需设置几个数据结构:
① int Available[m]:系统当前可用资源。
② int Claim[n, m]:进程最大需求资源。(Max)
③ int Allocation[n, m]:当前分配。
④ int Need[n, m]:尚需资源。
⑤ int Request[n, m]:当前请求。
为进行安全检测,需再定义数据结构:
⑥ int Work[m]:记录当前可用资源。
⑦ int Finish[n]:记录进程是否可以执行完。
6.死锁的检测
6.1 资源分配图
进程的死锁问题可以用又有向图更加准确地加以描述。资源分配图是用于当前系统的运行状态之下进程的情况、资源的分配和占有的情况。
- 两种结点:进程结点和资源结点
- 两种边:申请边(◯→▭)和分配边(▭→◯)
- 方框中的圆点表示同一资源类中的资源数
如果图中没有环路,则系统中没有死锁;如果图中存在环路,则系统中可能存在死锁。
(1)无环路。没有循环等待,故无死锁。
(2)有环路,有死锁。
p3申请 r2,但 r2 两个资源被 p1 和 p2 占有;
P1申请 r1,但 r1 分配给了 p2;
p2申请 r3,但 r3 分配给了 p3。
p1、p2、p3都参与了死锁。
(3)有环路,无死锁。
由于 p2 只是占有资源 r1,并未申请其他资源,因此 p2 可以运行完,释放 r1 的一个资源。
之后,p1 到 r1 的申请边改为分配边。
6.2 资源分配图的约简
资源分配图的约简用于判断资源分配图是否有死锁:
- 寻找一个非孤立且没有阻塞(有资源可以分配)的节点 pi ,若无,算法结束;
- 去除 pi 的所有分配边使其成为一个孤立节点;
- 寻找所有请求边都可满足的进程 pj,将 pj 的所有请求边全部改为分配边;
- 转步骤1。
若算法结束时,所有节点均为孤立结点,则资源分配图是可完全约简,否则称为不可完全约简。
死锁定理:系统状态S为死锁状态的充分必要条件是S的资源分配图不可完全约简。
死锁的判断问题实质上就是一个环路检测问题。
6.3 死锁检测时刻
何时进行死锁检测,主要取决于两个因素:
① 死锁发生的频率。
② 死锁所涉及的进程数。
而通常可以在如下时刻进行死锁检测:
- 进程等待时检测。因为仅当进程发出的资源申请未被满足时才有可能发生死锁,所以每当进程等待时便进行死锁检测,那么在死锁形成时就能够发现,系统开销大,与避免死锁算法相近。
- 定时检测。
- 资源利用率降低时检测。
- 交互式任务没有响应时检测。
7.死锁的解除(死锁的恢复)
资源分配图约间后,还连这边的那些进程就是死锁进程。
解除死锁的主要方法有:
- 系统重新启动:简单,代价大。
- 终止进程:终止环路上占有资源的进程(1)一次性全部终止;(2)逐步终止(优先级,代价函数)
- 剥夺资源 + 进程回退:回退到申请资源之前。缺点1:开销很大。因为要“回退”,就必须“记住”以前某一点处的“快照”(snapshot),快照包含进程的全部映像。缺点2:消除影响困难。一个回退的进程应当“挽回”它在回退点到死锁点之间所造成的影响,如修改某一文件,给其他进程发送消息。缺点3:饥饿。有可能每次回退的进程都是同一个,那这个进程就会饿死。