死锁原理

死锁:一组相互竞争系统资源或者进行通信的进程间“永久”阻塞的现象。

资源分为两类:可重用资源和可消耗资源。

可重用资源:一次只能被一个进程使用且不会被耗尽的资源。如处理器、内存和外存等。

可消耗资源:可被创建和销毁的资源。如中断、信号和缓冲区中的内容等。

死锁的三个必要条件:

1.互斥:资源只能互斥使用。

2.占有并等待:进程能在等待获取资源的过程中持有已获得的资源。

3.不可抢占:不能强行抢占进程已持有的资源。

这三个条件满足有可能产生死锁,加上一个条件,构成死锁产生的充分条件:

4.循环等待:存在一个进程链,每个进程占有相邻一边进程需要的资源,同时请求相邻另一边进程需要的资源。

对于死锁的处理方法:1.预防、2.避免、3.检测。

死锁预防

死锁预防(deadlock prevention)是设计一种排除死锁发生的可能性的策略。避免产生死锁的必要条件之一发生的方法称为间接死锁预防;避免循环等待发生的方法称为直接死锁预防。

互斥无法预防,必须保证互斥功能能够正常生效。

占有并等待预防,进程一次性请求所有资源,当部分资源暂时得不到时阻塞直到请求的所有资源都获得时解除。缺点是阻塞时间长;分配给一个进程的某些资源长时间不会被使用,同时不能被其他进程使用。

不可抢占预防,占有部分资源的进程在请求资源时若得不到满足则必须释放已占有的资源;当高优先级进程请求的资源被低优先级进程占有时,可以通过 OS 抢占的方式,使低优先级进程释放占有的资源。

循环等待预防,将所有的资源按照某种方式规定一种顺序,所有进程必须根据资源的顺序请求资源。优点是进程执行速度变慢;非必要情况下资源拒绝访问。

死锁避免

死锁避免(deadlock avoidance)和死锁预防差别很小,允许三个必要条件的发生,检查发生死锁的可能性,使得出现死锁的可能性为0。

在死锁避免中,通过判断资源分配请求是否可能发生死锁来决定是否允许该请求。

死锁避免方法:若一个进程的请求会导致死锁,则不启动该进程;若一个进程的资源分配请求会导致死锁,则不同意该请求。

当启动一个新进程时,评估该进程每种类型的资源需要的最大资源量和当前所有进程需要的同种类型资源的最大资源量之和是否大于计算机中该类型资源的最大量,如果大于,则不启动该进程;如果小于,则启动该进程。

资源分配拒绝策略又称银行家算法(banker algorithm)。在此策略中,系统状态指当前系统全部资源分配给所有进程、系统剩余可用的资源、所有进程还未完全满足的资源等情况。安全状态(safe state)指当前系统剩余资源有至少一种分配方式使得当前进程中有进程可以完全获取需要的资源执行完毕的情况;非安全状态与之相反,剩余资源无论怎么分配都无法使得当前进程中有进程需要的资源得到满足从而可以执行。

进程请求一组资源时,假设同意该请求,在此前提下,系统状态发生改变,此时检查系统状态是否处于安全状态,如果处于,则同意该请求;否则,拒绝该请求并将进程阻塞直到同意该请求后系统状态处于安全状态。

/*
** 银行家算法(资源分配拒绝策略)
*/ // 系统状态
struct state {
int resource[m]; // 系统中假设有m种类型的资源,每种资源的总量一定
int available[m]; // 当前系统中每种类型资源的可分配数量。
int claim[n][m]; // 系统中当前n个进程对每种资源需要的最大量。单个进程仅当占有全部所需的资源时才能正常运行
int alloc[n][m]; // 系统中当前n个进程对每种资源的已占有数量。
}; // 分配策略
if (alloc[i][*] + request[*] > claim[i][*]) {
error;
} else if (request[*] > available[*]) {
suspend process;
} else {
alloc[i][*] += request[*];
available[*] -= available[*];
/* 改变系统状态为 newstate */;
}
if (safe(newstate)) {
/* 进行资源分配 */;
} else {
/* 系统状态恢复成原始状态 */;
suspend process;
} // 检测系统状态是否安全
boolean safe(state s) {
int currentAvail[m];
currentAvail = available;
rest = { /* 当前所有的进程 */};
possible = true;
while (possible) {
/* 从 rest 中找一个进程 k 使得 claim[k][*] - alloc[k][*] 小于等于 currentAvail */;
if (/* 找到了 */) {
currentAvail += alloc[k][*]; // 找到的进程默认会执行完毕,然后释放占有的资源
rest -= {/* 进程 k */};
} else {
possible = false;
}
}
return rest == null;
}

死锁避免的优点:

1.不需要抢占和回归进程。

2.相比死锁预防限制较少。

缺点:

1.必须事先声明每个进程需要的最大资源数量。

2.多个进程必须无关。

3.系统资源数量固定。

4.占有资源的进程不能退出。

死锁检测

死锁检测(deadlock detection)通过周期性的执行一个算法检测当前是否存在死锁,通过检测是否存在循环等待现象。

一般检测的时机是在请求资源时,优点是可以尽早发现死锁、算法相对简单,缺点是需要占用较多的处理器时间。

一种常用的检测算法是,通过检查当前所有进程中某一个进程还需要的资源是否可以得到满足。如果可以,则假设该进程会执行完毕,将其目前占用的资源释放为空闲资源,否则终止算法;如果算法没有终止,标记该进程,重复该算法,直到所有进程都被检查。算法结束后,所有的进程都被标记,说明没有产生死锁;存在进程没有被标记,说明当前存在死锁。

当检测到死锁时,需要使用某一策略解除死锁。策略有:

1.取消所有和死锁相关进程。

2.将死锁相关进程状态回滚到某一没有死锁的时间点。

3.连续取消死锁相关进程中的某一个,直到不存在死锁。

4.连续抢占资源直到不存在死锁。

上述 3 和 4 对于选择具体的进程进行回滚或者被抢占采取的策略有:

目前为止消耗的处理器时间最少。

目前为止产生的输出最少。

预计剩下的时间最长。(这一点难以预测)

目前为止分配的资源总量最少。

优先级最低。

上述几种解决死锁的措施各有优缺点,OS 可以针对具体情况采用不同的措施。

哲学家就餐问题

一个圆桌旁边有5个座椅围成一圈,每两个座椅之间放入一把叉子。每个座椅上面有一个哲学家,哲学家每天只会进行思考和进餐两种行为,每个哲学家需要拥有左右两边的叉子才能进餐。在进餐的时候需要保证叉子的使用是互斥的、不发生死锁和饥饿问题。

/*
** 一次只允许4位哲学家入座进餐的解决方案
*/
semaphore fork[5] = {1};
semaphore room = 4;
int i; void philosopher(int i) {
while (true) {
think();
wait(room);
wait(fork[i]);
wait(fork[(i + 1) % 5]);
eat();
signal(fork[(i + 1) % 5]);
signal(fork[i]);
signal(room);
}
} void main() {
parbegin(philosopher(0),...philosopher(4));
}

另一种方案:

/*
** 使用管程解决哲学家就餐问题
*/
monitor diningController;
cond forkReady[5];
boolean fork[5] = {true}; void getForks(int pid) {
int left = pid;
int right = (++pid) % 5;
if (!fork[left]) {
cwait(forkReady[left]);
}
fork[left] = false;
if (!fork[right]) {
cwait(forkReady[right]);
}
fork[right] = false;
} void releaseForks(int pid) {
int left = pid;
int right = (++pid) % 5;
if (empty(forkReady[left]) {
fork[left] = true;
} else {
csignal(forkReady[left]);
}
if (empty(forkReady[right]) {
fork[right] = true;
} else {
csignal(forkReady[right]);
}
} void philosopher(int k) {
while (true) {
think();
getForks(k);
eat();
releaseForks(k);
}
}

OSIDP-并发:死锁和饥饿-06的更多相关文章

  1. 转:java高并发学习记录-死锁,活锁,饥饿

    死锁 两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去. 为什么会产生死锁: ① 因为系统资源不足. ② 进程运行推进的顺序不合适.    ③ ...

  2. java编程思想之并发(死锁)

    一个对象可以有 synchronized 方法或其他形式的加锁机制来防止别的任务在互斥还没有释放的时候就访问这个对象. 死锁 任务有可能变成阻塞状态,所以就可能发生这样的情况:某个任务在等待另一个任务 ...

  3. 什么是hashMap,初始长度,高并发死锁,java8 hashMap做的性能提升

    问题1:HashM安排的初始长度,为什么? 初始长度是 16,每次扩展或者是手动初始化,长度必须是 2的幂. 因为: index = HashCode(Key) & (length - 1), ...

  4. Java精通并发-死锁检测与相关工具详解

    关于死锁其实在之前https://www.cnblogs.com/webor2006/p/10659938.html的jvm学习中已经详细举过例子了,不过这里再来复习一下,另外是从并发这个专题领域的角 ...

  5. java集合之hashMap,初始长度,高并发死锁,java8 hashMap做的性能提升

    众所周知,HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry.这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干. HashMap ...

  6. Java多线程与并发——死锁与中断线程

    过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才有可能出现. 多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁. 死锁例子: package com.vince; /** ...

  7. SQL Server 并发死锁解决案例备忘

    SET @sql = ' SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; SET DEADLOCK_PRIORITY 10 BEGIN TRAN DE ...

  8. mysql for update 高并发 死锁研究

    mysql for update语句     https://www.cnblogs.com/jtlgb/p/8359266.html For update带来的思考 http://www.cnblo ...

  9. java 并发性和多线程 -- 读感 (二 线程间通讯,共享内存的机制)

    参考文章:http://ifeve.com/java-concurrency-thread-directory/ 其中的竞态,线程安全,内存模型,线程间的通信,java ThreadLocal类小节部 ...

  10. java 并发性和多线程 -- 读感 (一 线程的基本概念部分)

    1.目录略览      线程的基本概念:介绍线程的优点,代价,并发编程的模型.如何创建运行java 线程.      线程间通讯的机制:竞态条件与临界区,线程安全和共享资源与不可变性.java内存模型 ...

随机推荐

  1. CPU、内存的占用率

    要获取不包含百分比符号的内存占用率: #free -t | awk 'NR ==2 {print "Current Memory Utilization is: "$3/$2*10 ...

  2. 第15周作业--JDBC连接数据库

    编写一个应用程序,输入用户名和密码,访问test数据库中t_login表(字段包括id.username.password),验证登录是否成功.当登录成功后,将t_user表(id.name.sex. ...

  3. k8s volumes

    NFS: apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: registry.k8s.io/tes ...

  4. MSDTC突然停用了,后台数据无法更新

    由于前台电脑停电突然关机,导致重启后发现MSDTC无法更新数据,重新添加了link,只能查询,更新失败,报错:无活动事务 1.修改host设置,在C:\Windows\System32\drivers ...

  5. 网络很慢mtu设置

    [root@db-***** etc]# cat /etc/rc.local #!/bin/sh # # This script will be executed *after* all the ot ...

  6. 淘淘商城项目技术点-9:使用FTPClient及FtpUtil工具类将图片上传至ngnix图片服务器

    package com.taotao.controller; import com.taotao.common.utils.FtpUtil; import org.apache.commons.net ...

  7. 第14章 Windows管理规范

    第14章 Windows管理规范 我们一直期望但是又害怕写这一章.Windows管理规范(Windows Management Instrumentation,WMI)可能是微软提供给管理员使用最优秀 ...

  8. 发现C++程序中未释放的内存空间

    本篇先后介绍在windows中使用visual studio定位未释放的内存.在linux中使用valgrind定位未释放的内存. Windows+Visual Studio 2015 (企业版) 准 ...

  9. vite2.9 + vue3.2 打包部署到nginx上刷新页面404问题

    vite2.9 + vue3.2 打包部署到nginx上刷新页面404问题 在本地运行没问题,部署到服务器上,能正常访问,但是刷新之后页面404 原有的Nginx配置为: server { liste ...

  10. mariadb数据库用户管理(创建、赋权、)

    数据库查看当前用户 select user(): MariaDB [(none)]> select user(); +----------------+ | user() | +-------- ...