死锁
死锁的概念
对死锁进行抽象化的表述:
其实信号量也是一种共享的资源
形成死锁的必要条件
注意这些是死锁产生的必要条件,也就是说即使满足这些条件,也不一定产生死锁
mutual exclusion表示该资源必须是互斥访问的,这是设备的固有特性导致的,例如打印机、信号量
circular wait:循环等待条件
死锁的预防
对于死锁,操作系统的处理态度可以是不一样的:操作系统可以选择预防死锁产生,也可以允许死锁产生但是在死锁产生后进行干预,也可以对死锁完全不理睬。
死锁预防的主要思路就是保证任何情况下,四个条件不能同时满足
第一个条件由于是设备的固有特性,不好处理
第二个条件:要么不允许动态申请资源,要么只能在不占用资源的情况下动态申请资源,显然限制太大。因为进程不是一直使用资源的,但是第一种方法却使得进程一直占用资源;而第二种对进程的功能限制太大,也可能导致需要资源比较多的进程导致饥饿
这个思路看起来比前面的靠谱一些,但是还是存在一些问题,就是这个过程对于进程必须是无损的,但是不是所有的资源都能实现隐含抢占对进程的无损,一些不可共享的资源,例如信号量,就不能这样做
意思就是,如果已经申请了8号资源,那么所有编号小于8的资源都不可以再申请了
显然,越常用的资源应该编号越小
这种方法能避免循环等待的“循环”条件:占用了大编号的资源的进程不可以再申请小编号的资源,所以避免了环的形成
但是这种方法有两个突出的问题:第一个就是这个顺序号很难确定,因为许多资源就是普通的文件,很难说它的级别是多少。另外,这种方式导致了进程如果想要先获取大编号的资源,再获取小编号的资源,这种方式是不被允许的,只能在申请大编号资源之前先把小编号资源获得了,这降低了资源利用率
综上,死锁的预防是很难实现的,所以目前的实际应用不是预防
死锁避免
我们建立一个安全状态的概念:
对于安全状态的例子如下:
这个例子很好的说明了安全状态的概念:安全状态下,我们可以通过先满足排在序列前面的进程的要求,从而在其执行完后释放其的资源,以满足排在后面的进程的要求,最终满足所有进程的要求。也就是说,满足要求的顺序就是安全序列的顺序
所以:
安全状态是不死锁的充分不必要条件
操作系统只运行在安全状态,这是一种偏保守的状态,代价就是资源利用率不高
可以看出银行家算法是很符合安全状态的概念的
Max就是最大需求矩阵
Allocation表示现在的资源占用
第四步:如果都完成了,说明系统是安全的,否则就是说明系统已经处于不安全的状态了
注意上面的C是小于等于符号,是符号错误
如果安全,就真的分配给该进程,否则就将该进程挂起,不让它加入安全队列中
也就是说,一旦有进程提出申请,就更新状态,然后判断是否依然是安全状态,效率不太高
死锁检测和恢复
上面为了避免死锁的产生,所用的方法都是代价很大的,而这里的思路就是从避免死锁产生到死锁产生后如何即使发现和修复
死锁检测算法只需要在怀疑系统出现死锁的时候调用即可
可以看到,死锁检测算法其实就是死锁避免算法
那么,应该在什么时候调用该检测算法呢?
可以有两种思路:
- 定时运行:和系统一般产生死锁的时间间隔向配套,如果经常生产死锁,运行间隔的周期就短一点,反之就长一点,这样,死锁对进程的最长影响时间就是一个运行间隔
- 监测运行:如果CPU利用率低于阈值就运行一下(因为CPU利用率低可能就是死锁造成的,另外CPU利用率低的时候运行对效率影响比较小)。或者是如果有进程请求很大量的进程时运行一下
当然,这两种思路不是互斥的,两条规则可以都应用上
死锁恢复
第二种方法相对温柔一些(但是第一种方法也有实际的操作系统在用),那么如何决定先杀死哪个进程呢?有多种判断依据:
实际使用的时候不能使用单一判决,可以使用多种判决计算综合代价,它的算法折射了哪些进程在该操作系统看来更加重要
如果保存了进程的image,可以做的不将进程彻底杀掉,而是回退到某个时间点