1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;
1)死锁: 多个进程由于互相等待对方持有的资源而造成的谁也无法执行的情况;

1.1)死锁造成的结果:
1.2)正常代码示例
- // 生产者
- Producer(item)
- {
- P(empty); // empty减1后判断empty是否小于0,是则表明没有可用缓冲区,当前进程阻塞
- P(mutext);// mutex减1后判断mutex是否小于0,是则表明已有进程进入临界区(mutex初始值为1),当前进程阻塞
- // 读入in;将item写入到in的位置上; (临界区)
- V(mutex); // mutex加1后判断mutex是否小于等于0,是则表明存在阻塞进程,唤醒在mutex信号量上阻塞的进程
- V(full); // full加1后判断full是否小于等于0,是则表明缓冲区中没有内容,唤醒在full信号量上阻塞的消费者进程(消费当前进程产生的数据内容);
- }
-
- // 消费者
- Consumer()
- {
- P(full); // full减1后判断full是否小于0,是则表明缓冲区没有内容(可消费),当前进程阻塞
-
- P(mutex); // mutex减1后判断mutex是否小于0,是则表明已有进程进入临界区(mutex初始值为1),当前进程阻塞
- // 读入out; 从文件中的out 位置读出到item;(临界区)
- // 打印item
- V(mutex); // mutex加1后判断mutex是否小于等于0,是则表明存在阻塞进程,唤醒在mutex信号量上阻塞的进程
- V(empty); // empty加1后判断 empty 是否小于等于0,是则表明没有可用缓冲区,唤醒在empty信号量上阻塞的生产者进程(使用当前进程释放的一个缓冲区);
- }
补充:PV操作:
- // 生产者:消费资源(这里消费资源指的是生产者消费一个空闲缓冲区,或一个数组项)
- P (semaphore s)
- {
- s.value--; // 消费资源
- if (s.value < 0) {
- sleep(s.queue); // 当前生产者进程睡眠
- }
- }
-
- // 消费者:产生资源 (这里产生资源指的是消费者释放一个空闲缓存区,或一个数组项)
- V (semaphore s)
- {
- s.value++; // 释放资源
- if (s.value <=0 ) {
- wakeup(s.queue); // 消费者唤醒睡眠的生产者进程
- }
- }
1.3)死锁代码示例(可以看做是用户程序写错了顺序)
同正常代码相比,把生产者的P(mutex) 放在P(empty)的前面,把消费者的P(mutex)放在 P(full)的前面,如下:
| 生产者 | 消费者 |
| Producer(item) { P(mutex); P(empty); // 读入in;将item写入到in的位置上; (临界区) V(mutex); V(full); } | Consumer() { P(mutex); P(full); // 读入out; 从文件中的out 位置读出到item;(临界区) // 打印item V(mutex); V(empty); } |
【代码解说】
【产生死锁场景】

生产者代码1处的empty信号量上阻塞;
为什么会出现死锁?
1)死锁成因: 各自占有的资源和互相申请的资源形成环路等待。

1)4个必要条件

1)死锁处理的4种方法:

方法1:在进程执行前,一次性申请所有需要的资源,不会占有资源再去申请其他资源;
方法2: 对资源类型进行排序,资源申请必须按序进行,不会出现环路等待;
1)判断此次请求是否引起死锁;
2)如果系统中的所有进程存在一个可完成的执行序列 P1, ..., Pn,则称系统处于安全状态;
3)银行家算法:

4)银行家算法:找出一个可完成的执行序列的算法

补充:

图片解说:
银行家算法步骤:

因为把可用资源210分配给进程P0后,P0无法执行,且其他进程因为没有可用资源也无法执行,这就造成了死锁,所以银行家算法的分配结果是拒绝进程P0的资源申请请求;
1)定时检测或者是发现资源利用率低时检测;

2)进程回滚
许多通用操作系统,如windows,linux,都 采用了死锁忽略方法,因为死锁忽略的代价最小;
