并发实现机制-3-死锁和饥饿
死锁和饥饿是并发处理的两个基本问题
死锁的三种常用方法:预防、检测和避免
死锁原理
- 死锁是永久性的。
- 一组进程中的每个进程都在等待某个事件(典型情况下是等待释放所请求的资源),而仅有这组进程中被堵塞的其他进程才可以触发该事件,这样这组进程就发生了死锁。
todo: 联合进程图的死锁无法避免的区域
不同资源类型的死锁
资源分为两类:可重用资源,可消耗资源。
可重用资源是一次仅供一个进程安全使用并且不会因为使用耗尽的资源,进程会使用并释放资源,可重用资源例子如:处理器、I/O通道、内存和外村、设备,以及诸如文件、数据库和信号量之类的数据结构。可重用资源死锁的例子:
- 两个进程竞争独占访问文件D和T,每个进程都占用一个文件并请求另一个文件从而发生死锁。(设计资源请求顺序的约束可以解决)
- 内存请求,可分配空间为200kb,如果请求顺序为:P1进程请求80kb,P2请求70kb,(此时间点之前p1、p2都完成之前请求且没有释放空间)p1请求60kb,p2请求80kb,那么就会发生死锁,因为都得不到需要的空间。(事先知道请求空间的总量、使用虚存 两个方法可以解决死锁)
可消耗资源可以被创建(生产)和销毁(消耗)的资源。消费进程得到资源后资源将不复存在,例子有:终端、信号、消息和I/O缓冲区。例子如下:,可消耗资源通常为设计错误很难发现,罕见的事件可能导致死锁,可能很长时间才会遇到。
- 每个进程都试图从另一个进程接收消息,p1:receive(p2)... send(p2,m1) p2: receive(p1) ... send (p1,m1)
死锁条件
三个必要条件:
- 互斥。一次只有一个进程可以使用一个资源。
- 占有且等待。进程等待其他进程时,继续占有已经分配的资源。
- 不可抢占。不能强行抢占已占有的资源。
一个充分条件:
- 循环等待。存在一个闭合的进程链,每个进程至少占有此链中下一个进程所需的一个资源
上述四个条件共同构成死锁的充要条件。第四个条件是前三个条件的潜在结果,前三个条件只是表明死锁可能存在。
处理死锁的方法是根据这四个条件的,
预防:消除四个条件的某个条件的出现
避免:基于资源分配的当前状态做动态选择
检测:检错死锁的存在并从死锁恢复
todo: 表6.1 操作系统中死锁检测、预防和避免方法
死锁预防
死锁预防分为两类,一类是间接死锁预防,即防止前面列出的三个必要条件;一类是直接死锁预防,即防止第四个条件循环等待。
死锁预防存在着抢占和回滚的现象。
互斥:不可避免
占有且等待:进程一次性请求所有需要的资源。(问题:进程可能要被堵塞很长时间,虽然只要有一些资源就可以继续运行;进程不知道所需要的全部资源)
不可抢占:1. 占有某些资源的进程进一步申请资源若被拒绝,则必须释放最初占有的资源。2. 进程请求当前被另一个进程占有的一个资源时,操作系统可以抢占另一个进程,要求释放资源(优先级若相同则不可解)。只有资源状态可以很容易的保存和恢复的情况下(处理器资源),这种方法才是实用的。
循环等待:定义资源类型的先行顺序来预防,(会使进程执行速度变慢),即设计机制防止条件的发生
死锁避免(也可以视为死锁预防的例子)
允许三个必要条件,通过明智的选择,确保永远不会到达死锁点。可允许更多的并发。是否允许资源的分配请求是通过判断该请求是否可能导致死锁来决定的。(判断请求,杀死可能性)
死锁避免策略并不能确切的预测死锁,踏进是预测死锁的可能性并确保永远不会发生这种可能性(从banker算法可以看出)
两种方法:(均是拒绝将要发生的事情)
- 若进程请求会导致死锁,则不启动该进程
- 若进程增加的资源请求会导致死锁,则不允许分配
四种向量或举证:
- resource 系统中每种资源的总量
- available 未分配个进程的每种资源的总量
- claim 进程i对资源j的需求矩阵
- allocation 当前分配给进程i的资源j
资源分配拒绝算法为银行家算法(banker算法),见下文的经典问题
死锁避免有限制:
- 事先声明请求的最大资源
- 进程的执行顺序没有同步需求,进程是无关的
- 分配的资源数量固定
- 占有资源时程序不能退出
死锁检测
死锁预防策略非常保守,通过限制访问资源和在进程上强加约束来解决死锁问题。死锁检测不限制资源访问或约束进程行为。对于死锁检测,只要有可能就会给进程分配其请求的资源。
死锁检测可以频繁的在每个资源请求发生时进行,也可以进行的少一些,在每次请求资源时检查死锁:尽早检测死锁情况,算法简单,但是会耗费很多处理器时间。
死锁检测可以分为:死锁检测和恢复
死锁检测算法:
- 标记allocation矩阵中一行全为零的进程
- 初始化一个临时向量w,令w等于available
- 查找下表i,使得i档期啊那位标记且q的第i行小于等于w,即对所有的1<=k<=m, Qik<=Wk,若找不到这样的行,终止算法(满足一个进程的需求)
- 若找到这样的行,标记进程i,并把allocation矩阵中的相应行加到w中,即对所有1<=k<=m, 令Wk=Wk+Aik,返回步骤3(回收上一步3中的进程资源,重复3)
死锁检测算法的策略是查找一个进程,使得可用资源能满足该进程的资源请求,然后假设同意这些资源,让该进程运行直到结束,在释放它的所有资源,然后,算法在寻找另一个可以满足资源请求的进程,该算法不能保证防止死锁,是否思索取决于将来同意请求的次序。他所做的一切事确定当前是否存在死锁
仅当这个算法的最终结果有未标记的进程时,才存在死锁,每个未标记的进程都是死锁的。
恢复的策略:
- 取消所有的死锁进程
- 把每个死锁进程回滚到前面定义的某些检查点,重启所有进程,需要在系统中构建回滚和重启机制
- 连续取消死锁进程直到不再存在死锁,取消顺序基于某种最小代价原则。取消进程的顺序应基于某种最小代价原则。
- 连续抢占资源直到不在存在死锁,需要一种基于代价的选择算法,一个资源被抢占的进程必须回滚到获得这个资源之前的某一状态。
选择原则:(思想)
- 目前为止消耗的处理器时间最少
- 目前为止产生的输出最少
- 预计剩下的时间最长
- 目前为止分配的资源总量最少
- 优先级最低
避免死锁的三种方法:(加到之前的死锁的文章里面去)
-
固定加锁的顺序(针对锁顺序死锁)
-
开放调用(针对对象之间协作造成的死锁)
-
使用定时锁-->tryLock()
- 如果等待获取锁时间超时,则抛出异常而不是一直等待!