操作系统学习笔记(七) 死锁

一、资源

把需要排他性使用的对象称为资源。资源可以是硬件也可以是软件,比如打印机或者数据库中的一个加锁记录。

资源可以分为两类:可抢占资源和不可抢占资源。

可抢占资源:可以从拥有它的进程中抢占而不产生副作用。

不可抢占资源:不引起相关的计算失败的情况下,无法把它从占有它的进程处抢占过来。

抢占这个词,在进程和线程调度时就提到了这个概念,那时是进程或者线程可以抢占CPU,即抢占式调度。存储器也可以抢占,如内存换页。

一般来说,可抢占资源不会引起死锁,可以在进程间重新分配资源而得到解决。

二、死锁

死锁的概念:如果一个进程集合中,每个进程都在等待只能由该集合中其他进程才能引发的时间,那么该进程集合就死锁的。

死锁并不仅仅发生在资源上,资源死锁只是一种。

资源死锁的四个必要条件

(1)互斥条件。每个资源要么已经分配给了一个进程,要么就是可用的。

(2)占有和等待条件。已经得到了某个资源的进程可以再请求新的资源。

(3)不可抢占条件。已经分配给一个进程的资源不能被抢占,只能由占有它的进程显式地释放。

(4)环路等待条件。死锁发生时,系统中一定有由两个或以上的进程组成的一条环路,该环路中的每个进程都在等待着下一个进程所占有的资源。

可以自然地想到,可以用一个有向图来表示资源分配的情况。用圆形节点表示进程,方形表示资源。从资源节点到进程节点的有向边表示该资源被请求、并被进程占用。由进程到资源节点的有向边表示进程正在请求该资源,并且因为请求资源而导致进程被阻塞,处于等待该资源的状态。

这样,根据环路等待条件,一旦在某个时候有向图中出现了两个或两个以上进程组成的环路,就会导致死锁的发生。

下图是《现代操作系统》中的一个例子:

现在有三个进程A、B、C,它们需要R、S、T三个资源中的其中两个,具体参见图上方。操作系统可以选取任意一个就绪的进程执行。

我们可以看到,在第一种执行顺序中,每个进程都占有了一个资源,从而在请求第二个资源的时候,每个进程都进入了阻塞态,最后没有进程可以执行。同时注意到,资源图确实构成了环路。

在第二种执行顺序中,进程A率先获取了所有它需要的资源R和S,尽管C因为请求资源R而进入了阻塞态,但是A使用完资源后,释放了R和S,最终C可以继续执行下去,没有构成死锁。

处理死锁的策略:

(1)忽略该问题。一般称为鸵鸟算法,即躲起来视而不见。

(2)检测死锁并恢复。让死锁发生,检测它们是否发生,一旦发生死锁,就采取行动解决问题。

(3)仔细对资源进行分配,动态地避免死锁。也称为死锁避免。

(4)通过破坏引起死锁的四个必要条件,防止死锁的发生。也称为死锁预防。

三、死锁检测和恢复

采用这种办法的时候,系统并没有试图阻止死锁的发生,而是允许死锁发生,检测到死锁后,采取措施进行恢复。

检测:我们知道,死锁的一个必要条件是存在两个及以上进程组成的环路。通过检测有向图环路,是一种检测死锁存在的方法。

当每种类型存在多个资源时,检测可能会复杂许多。

恢复的方法:

(1)利用抢占恢复。死锁发生的必要条件,其中一个就是不可抢占。如果允许抢占,那么就可以破坏死锁条件。

(2)利用回滚:周期性对进程进行检查点检查,一旦发现了死锁,就回滚到一个较早的检查点上。

(3)通过杀死进程:这个方法是最显而易见的。杀死一个进程可以释放它占有的资源,如果仍然不行那么久继续杀死其他进程直到打破死锁。

四、死锁避免

1、资源轨迹图

如果直到了进程在各个阶段需要哪些资源,那么可以在图中进程标注。两个进程的交叠区域就是一个可能会造成死锁的区域。进程在图中只能向右或者向上前进,一旦进入了危险区,那么就有可能发生死锁。为了避免死锁,应当在合适的时间阻塞某个进程,使得运行避开这个区域。

2、银行家算法

在介绍银行家算法之前,先引入一个安全状态与不安全状态的概念。

如果没有死锁发生,并且即使所有的进程突然请求对资源的最大需求,也仍然存在某种调度次序,能使得每个进程运行完毕,则称该状态是安全的。

该图中,a状态下是安全的,因为按照b-e的顺序,整个过程能完成而不会产生死锁。只要系统仔细进行调度,就能够避免死锁。

该图中,a状态是安全的,但是b中把资源先分配给了A进程1个,导致后面可能会出现。注意,不安全状态并不一定是死锁,b中的状态,依然可以运行一段时间,B进程甚至可以运行完成,直到没有空闲资源才会发生死锁。如果A能主动释放资源,可能也不会出现死锁。

安全状态与不安全状态的区别是,从安全状态出发,系统能够保证所有的进程都能完成;而从不安全状态出发,没有这样的保证。

单个资源的银行家算法:对每一个请求进行检查,检查如果满足了这一需求是否会达到安全状态。如果能,那么满足该需求;如果不能,就推迟对这一请求的满足。

例如,上面图中,银行家可以只满足B客户的需求,拖延其他客户的需求,因而可以让B先完成,然后释放B的4个资源。有了这4个资源,就可以给B或者D分配所需要的资源。

不安全状态不一定产生死锁,不过如果一旦一个进程需要最大资源,就会导致死锁,银行家算法避免这种风险。

银行家算法可以推广到多个资源的情况,此时可以写成矩阵的形式,每次判断一行是否满足,即一个进程的多个资源都进行检查。

需要注意到,死锁避免是非常困难的,无论是资源轨迹图还是银行家算法,都需要事先知道进程运行的过程中需要的最大资源数,这几乎是不可能实现的!

五、死锁预防

死锁避免可以认为是在程序执行中动态地避免死锁发生,而死锁预防可以说是静态的方式,杜绝死锁发生的可能性。

只要能破坏死锁发生的四个必要条件之一,那么死锁就不会发生。

1、破坏互斥条件

尽量使得资源不被某个进程独占。比如打印机的假脱机打印,就是尽量避免一个进程独占打印机,而是把要打印的文件存入一个假脱机目录,然后通过一个守护进程管理打印机进行打印。

2、破坏占有和等待条件

即禁止已经持有资源的进程再等待其他资源。

一种方式是,在进程开始执行前请求所需的全部资源,如果不能满足,那么就不分配资源,进行等待。这种方式的问题在于,类似银行家算法,事先不知道需要多少资源,而且资源利用率不高。

另一种方式就是,当一个进程请求资源时,先暂时释放其当前所占用的所有资源,然后再尝试一次获取所需的全部资源。

3、破坏不可抢占条件

这个比较好理解,允许资源抢占即可。当然,有的时候资源应当是不可抢占的。

4、破坏环路等待条件

一种方法是对资源进行编号,进程在任何时候都可以请求资源,但是所有的请求必须按照资源编号的顺序(升序)提出。

六、其他问题

1、两阶段加锁

这是针对数据库的一种方法,第一阶段对所有需要更新的记录进行加锁,一旦某个记录已经被加锁,就释放之前的锁,从头进行重试。只有当第一阶段所有获取锁的行为都成功,才进行第二阶段的更新,否则放弃所有的锁。

2、通信死锁

这种情况其实是很常见的。当一个进程A向B发送信息后挂起,需要B进程的回复唤醒时,如果请求信息丢失,A就会被阻塞以等待回复,B会阻塞等待一个向其发送命令的请求,因而发生死锁。

通信死锁不涉及资源,不能通过合理调度资源来避免。一般通信协议会解决这种问题,包括超时重传等技术。

3、活锁

轮询(忙等待)的方式,在有时是有效的,因为挂起进程等待的开销很大。考虑如下程序:

enter_region()通过轮询获取资源,假设A获得了资源1,B获得了资源2,那么两个进程都不会阻塞,而是不停地进行轮询以获取资源。两个进程总是运行完系统分配的时间片,没有阻塞但是不会取得进展。

4、饥饿

饥饿的概念,其实与死锁和活锁差别比较大。考虑进程调度,基于优先级的调度中,如果总是有高优先级的进程就绪,那么一个低优先级的进程可能长时间无法上CPU运行。这就是饥饿现象,可以考虑通过动态优先级机制,可以动态提高长时间得不到运行的进程的优先级,从而使它可以运行。

时间: 2024-10-13 00:07:15

操作系统学习笔记(七) 死锁的相关文章

Linux 操作系统学习笔记

一,unix 1.unix 特点 伸缩性强,开放性好, 2.基本原则 所有对象,硬件都是文件 配置数据以文本形式保存 短小的单目的程序构成 多个程序合作完成复杂任务 3.gnu 基本原则是共享,建立自由开放的unix系统 1984年 richard stallman 发起 基本体系是micro kernel 4.gpl Copyleft 原作者所有权 5.linux起源 Linustorvalds, 自由的类unix操作系统, 遵循gnu和gpl 6.linux 可以实现unix功能 遵循开源许

Linux System Programming 学习笔记(七) 线程

1. Threading is the creation and management of multiple units of execution within a single process 二进制文件是驻留在存储介质上,已被编译成操作系统可以使用,准备执行但没有正运行的休眠程序 进程是操作系统对 正在执行中的二进制文件的抽象:已加载的二进制.虚拟内存.内核资源 线程是进程内的执行单元 processes are running binaries, threads are the smal

计算机操作系统学习笔记_2_进程管理 --进程与线程(上)

h3.western { font-family: "Liberation Sans",sans-serif; }h3.cjk { font-family: "微软雅黑"; }h3.ctl { font-family: "AR PL UMing CN"; }h2.western { font-family: "Liberation Sans",sans-serif; font-size: 16pt; }h2.cjk { fon

第十七篇:博采众长--初探WDDM驱动学习笔记(七)

基于WDDM驱动的DirectX视频加速重定向框架设计与实现 现在的研究生的论文, 真正质量高的, 少之又少, 开题开得特别大, 动不动就要搞个大课题, 从绪论开始到真正自己所做的内容之间, 是东拼西凑地抄概念, 抄公式, 达到字数篇幅的要求, 而自己正真做了什么, 有哪些实际感受, 做出的内容, 相比前面的东拼西凑就几点内容, 之后就草草结束, 步入感谢的段落. 原因不光只有学生自己, 所谓的读研, 如果没有一个环境, 学生有再大的愿望, 再强的毅力, 到头来也只是空无奈. 有些导师要写书,

马哥学习笔记七——LAMP编译安装之MYSQL

1.准备数据存放的文件系统 新建一个逻辑卷,并将其挂载至特定目录即可.这里不再给出过程. 这里假设其逻辑卷的挂载目录为/mydata,而后需要创建/mydata/data目录做为mysql数据的存放目录. 2.新建用户以安全方式运行进程: # groupadd -r mysql # useradd -g mysql -r -s /sbin/nologin -M -d /mydata/data mysql # chown -R mysql:mysql /mydata/data 3.安装并初始化my

Lua学习笔记(七):迭代器与泛型for

1.迭代器与闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素. 迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是他知道来自于哪里和将要前往哪里.闭包提供的机制可以很容易实现这个任务.记住:闭包是一个内部函数,它可以访问一个或者多个外部函数的外部局部变量.每次闭包的成功调用后这些外部局部变量都保存他们的值(状态).当然如果要创建一个闭包必须要创建其外部局部变量.所以一个典型的闭包的结构包含

python学习笔记七:条件&循环语句

1.print/import更多信息 print打印多个表达式,使用逗号隔开 >>> print 'Age:',42 Age: 42   #注意个结果之间有一个空格符 import:从模块导入函数 import 模块 from 模块 import 函数 from 模块 import * 如果两个模块都有open函数的时候, 1)使用下面方法使用: module1.open()... module2.open()... 2)语句末尾增加as子句 >>> import ma

swift学习笔记(七)自动引用计数

与Object-c一样,swift使用自动引用计数来跟踪并管理应用使用的内存.当实例不再被使用时,及retainCount=0时,会自动释放是理所占用的内存空间. 注:引用计数仅适用于类的实例,因为struct和enumeration属于值类型,也就不牵涉引用,所以其存储和管理方式并不是引用计数. 当一个实例被初始化时,系统会自动分配一定的内存空间,用于管理属性和方法.当实例对象不再被使用时,其内存空间被收回. swift中的引用类型分为三种,即Strong强引用,weak弱引用和无主引用unw

计算机操作系统学习笔记_1_操作系统概述

操作系统概述 一.操作系统的概念.特征.功能和提供的服务 1.操作系统的概念     操作系统是计算机系统中最重要.最基本的系统软件,操作系统位于硬件和用户程序之间.    对于用户来讲:它能向用户提供使用计算机的接口;    从资源管理角度来看:它能管理计算机软硬件资源,提高其利用率;    再者,利用虚拟机技术(如WMWare,VirtualBox,Java虚拟机等),扩展了计算机的功能和使用范围.     因此,操作系统的定义为:操作系统是控制和管理计算机软.硬件资源,以尽可能合理.高效的

Swift学习笔记七:闭包

闭包可以 捕获 和存储其所在上下文中任意常量和变量的引用. Swift 会为您管理在 捕获 过程中涉及到的内存操作. 在 函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: 1. 全局函数是一个有名字但不会捕获任何值的闭包 2. 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包 3. 闭包表达式是一个可以捕获其上下文中变量或常量值的没有名字的闭包 一.闭包表达式 闭包函数类似于Objective-C中的block.下面我们用事实说话: let counts =