学《操作系统》这门课必须会学到死锁,那么死锁是什么概念呢?死锁是指:由于竞争资源或者通信关系,两个或更多线程在执行中出现,永远相互等待只能由其他进程引发的事件。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
下面我们来看一个有趣的例子:单向同行桥梁
这个桥梁只能单向同行,要么往左,要么往右,不同方向的车会共享这个单向通行的桥梁。桥梁的每一部分可视为一个资源,如果整一个桥梁是一个资源,那么就不会出现问题,要占用的话就整个占用,然而,这个桥梁会之出现通行的问题,是由于部分占用的原因。
当对向行驶的车辆在桥上相遇,彼此都不让路时,彼此都在等待对方,而这种等待又是无限期的,这时就会出现死锁。
这种情况下,我们通常的做法是:某一个方向的车辆倒退(资源抢占和回退),这样另一个方向的车就可以通行了。
这种方法是不是很有效呢?非也,虽然不会出现死锁了,但是可能会出现饥饿的情况:由于一个方向的持续车流,另一个方向的车辆无法通行桥梁。
通过这个单向通行桥梁的例子,我们可以地更好的审视死锁这个问题的产生。
系统里头存在各种各样的资源:CPU执行时间,内存空间,I/O设备等等,每一类资源Ri有Wi个实例。
一个资源会大概经历这样一个阶段:首先,一个进程会申请空闲资源,系统里刚好有所需要的空闲资源的话,那么这个资源会被这个进程所占用,这个进程会使用该资源一段时间,最后资源的状态由占用变为空闲。
资源可以分为两类:可重用资源和消耗资源。
1.可重用资源有这么一些特点:
- 资源不能被删除且在任何时刻只能由一个进程所使用;
- 进程释放该资源后,其他的进程可重用。
- 可重用的资源在硬件层面上的有:处理器、I / O 通道、主和副存储器、设备等;在软件层面上的有:文件、数据库和信号量等数据结构。
2.消耗资源则有如下特点:
- 资源需要创建才能使用,用完之后需要进行销毁;
- 消耗资源有这些:在I / O缓冲区的中断、信号、消息等。
以上这两种资源都会出现死锁,其中消耗资源会出现死锁的原因是进程间在相互等待对方的消息(至于原因,到文章最后,你自然就会了解啦!!)
下面再来介绍资源分配图,它是描述资源和进程间的分配和占用关系的有向图。
资源分配图里有两种圆点和两种边,下图的第1个图案是指某一个进程,第2个图案是指某一种资源,而这种资源有4个实例(方框里有4个实心黑色圆点),第3个图案是指进程Pi想向资源Rj申请资源,第4个图案是指资源Rj中的1个实例已经被进程Pi所占用。
下面来看资源分配图1,你可以先自行判断一下图中所示情况是否会出现死锁。
P3正在占用资源R3,资源R3只有一个实例,所以进程P2只有在等待进程P3占用完后,才可申请到R3,而P2也在占用着R1和R2的资源...一段时间后,进程P3已经使用完资源R3了,于是进程P2可以得到资源R3,也再过一段时间后,进程P2使用完资源R1,资源R1有空闲的实例后,进程P1可以申请得到资源R1。
整一个过程下来,我们发现不会出现死锁,只是会需要一些时间来等待。
再看下面资源分配图2,这个图指比资源分配图1多了一条边。
我们很容易发现里面有一个循环,P3想申请R2但没有空闲的实例,所以一直占用着R3,而P2想申请R3但也申请不到,所以P2一直占用着R2和R1,而P1想申请R1,但也申请不到,所以P1也一直占用着R2。这里面有一个循环等待的情况,所以这种情况是会出现死锁的。
然鹅,资源分配图只是一种理想化的模型,在实际的操作系统中,有几百个或几千个进程,几十种或者更多的资源,再使用资源分配图来判断是否会出现死锁,那就会有很大的时间开销,而且实现起来也比较困难。
毕竟我们不能根据是否有“圈圈”来判断是否会出现死锁。看下面的资源分配图3。
虽然P1、R1、P2和R2围成了一个圈,但是进程P4和进程P2在经过一段时间后,都会释放出各自占有的资源,进程P1和进程P3也由此可以申请到自己想要的资源,并不会循环等待下去。
所以不能根据是否有“圈圈”来判断是否会出现死锁。
出现死锁必须满足下面4个条件:
- 互斥:任何时刻只能有一个进程使用一个资源实例;
- 持有并等待:进程保持至少一个资源,并正在等待获取其他进程持有的资源(如果一个进程不保持资源的话,那么别的进程就不需要等它,如果它不需要申请资源的话,那它也不需要等别人);
- 非抢占:资源只能在进程使用后自愿释放,不可以强行剥夺;
- 循环等待:存在等待进程集合{P0,P1,P2,P3,....,Pn},P0正在等待P1所占用的资源,P1正在等待P2所占用的资源,... ,Pn-1正在等待Pn所占用的资源,Pn正在等待P0所占用的资源。