死锁
定义:一组阻塞的进程持有一种资源,等待获取另一个进程所占有的一个资源
系统模型
1、两类实体:需求方与资源
(1)资源类型:R1、R2、……、Rm,CPU,memory space,I/O
(2)每个资源类型 Ri 有 Wi 实例
2、可重复使用的资源
(1)在一个时间只能一个进程使用,且不能被删除
(2)进程获取资源,后来释放由其他进程重用
(3)设备(物理资源):处理器,I/O通道,主、副存储器等
(4)数据结构(抽象):如文件,数据库,信号量等
(5)如果每个进程拥有一个资源并请求其他资源,死锁可能发生
3、消耗资源
(1)创建和销毁
(2)在 I/O 缓冲区的中断,信号,消息,信息
(3)如果接收消息阻塞可能会发生死锁
(4)可能少见的组合事件会引起死锁
4、一组顶点 V 和边 E 的集合:描述资源和进程间的分配和占用关系
(1)V:两种类型
(2)系统中的所有进程:P = {P1, P2,……, Pn}
(3)系统中的所有资源(4 种):R = {R1, R2,……, Rm}
(4)E:有两种类型
(5)资源请求边:进程 P 请求资源 R:Pi -> Rj
(6)资源分配边:资源 R 已分配给进程 P:Rj -> Pi
5、情况
(1)如果不包含循环:没有死锁
(2)如果包括循环: 如果每个资源类只有一个实例,那么死锁;如果每个资源类有几个实例,可能死锁
死锁特征
1、4 个条件
(1)互斥:在一个时间只能有一个进程使用资源
(2)持有并等待:进程保持至少一个资源,且正在等待获取其他进程持有的额外资源
(3)无抢占:一个资源只能被进程自愿释放(在进程完成任务之后)
(4)循环等待:存在等待进程集合 {P0, P1,……, Pn},P0 正在等待 P1 占用的资源,……,Pn 正在等待 P0 占用的资源
2、4 个条件是死锁出现的必要条件,而不是死锁出现的充分条件
死锁处理办法
1、死锁预防
2、死锁避免
3、死锁检测
4、死锁恢复
5、约束强度:死锁预防 > 死锁避免 > 死锁检测 > 死锁恢复
6、具体处理
(1)确保系统永远不会进入死锁状态
(2)运行系统进入了死锁状态,想办法恢复
(3)忽略这个问题,假装系统从来没有发生死锁,这种方法用于大多数操作系统
(4)判断死锁的开销比较大,同时设置一些约束让 OS 不进入死锁页,大大限制了 OS 的功能,出现死锁 OS 只能提供调试的手段帮助程序员解决问题
死锁预防
1、限制申请方式
(1)互斥
(2)持有并等待
(3)无抢占
(4)循环等待
2、互斤
(1)共享资源不是必须的,必须占用非共享资源
(2)无法实现
3、持有并等待
(1)必须保证当一个进程请求的资源,它不持有任何其他资源
(2)资源利用率低:可能发生机饿
4、无抢占
(1)如果进程占有某些资源,并请求其它不能被立即分配的资源,则释放当前正占有的资源
(2)被抢占资源添加到资源列表中
(3)只有当它能够获得旧的资源以及它请求新的资源,进程可以得到执行
5、循环等待:对所有资源类型进行排序,并要求每个进程按照资源的顺序进行申请
死锁避免
1、进程申请资源时,需要提供一些额外的先验信息
2、最简单、最有效的模式:要求每个进程声明它可能需要的每个类型资源的最大数目
3、限定提供与分配的资源数量,确保满足进程的最大需求
4、动态检查的资源分配状态,以确保永远不会有一个环形等待状态
5、系统资源分配的安全状态
(1)当一个进程请求可用资源,系统必须判断立即分配是否能使系统处于安全状态
(2)系统处于安全状态指:针对所有进程,存在安全序列
(3)安全状态是没有环形等待的状态,不安全状态就是有环形等待的状态,有环形等待不一定有死锁,所以死锁是不安全状态的一个子集
6、步骤
(1)序列 <P1, P2,……, Pn> 是安全的:针对每个 Pi,Pi 要求的资源 <= 当前可用资源 + 所有 Pj 持有资源(j < i)
(2)若 Pi 资源请求不能立即分配,则 Pi 等待所有 Pj 完成
(3)Pi 完成后,Pi+1 可以得到所需要资源,执行、释放所分配的资源、终止
(4)以此类推,最终整个序列的所有 P 都能获得所需资源
银行家算法(不使用)
1、死锁避免的算法
2、前提
(1)有多个进程,有多个资源的实例
(2)每个进程都必须能最大限度地利用资源
(3)当一个进程请求一个资源,就不得不等待
(4)当一个进程获得所有的资源,就必须在一段有限的时间释放它们
(5)需要提前知道每个线程,所需最大资源数
3、数据结构
(1)n = 线程数量
(2)m = 资源类型数量
(3)Max(总需求量):n * m 矩阵,若 Max[i, j] = k,则线程 Pi 最多请求 k 个 Rj 类型资源实例
(4)Available(剩余空闲量):长度为 m 的向量,若 Available[j] = k,则线程有 k 个 Rj 类型资源可用
(5)Allocation(已分配量):n * m 矩阵,若 Allocation[i, j] = k,则线程 Pi 当前分配了 k 个 Rj 资源类型实例
(6)Need(未来需要量):n * m 矩阵,若 Need[i,j] = k,则线程 Pi 可能需要至少 k 个 Rj 资源类型实例完成任务
(7)Need[i,j] = Max[i, j] - Allocation[i, j]
4、安全状态判断
(1)初始化:Work = Available:当前资源剩余空闲量,Finish [i] = false for i:线程 i 是否结束,Work、Finish 分别是长度为 m、n 的向量
(2)找出满足:Finish [i] = false && Needi <= Worki 的进程 i,找到转(3),没有找到转(4)
(3)线程 i 没有结束,且资源需求量小于当前剩余空闲资源量,则回收线程 i 的资源:Work = Work + Allocationi,Finish[i] = true,转到(2)
(4)所有进程的 Finish 为 true,表明系统处于安全状态
5、算法
(1)初始化:Requesti:线程 Ti 的资源请求向量,Requesti[j]:线程 Ti 请求资源 Rj 的实例
(2)如果 Requesti <= Need[i],转到(3),否则,拒绝资源申请,因为线程已经超过了其最大要求
(3)如果 Requesti <= Available,转到(4),否则,Ti 必须等待,因为资源不可用
(4)通过安全状态判断来确定是否分配资源给 Ti:生成一个需要判断状态是否安全的资源分配环境
Available = Available - Reguesti;
Allocation[i] = Allocation[i] + Requesti;
Need[i] = Need[i] - Requesti;
死锁检测(不使用)
1、运行机制
(1)允许系统进入死锁状态
(2)维护系统的资源分配图
(3)定期调用死锁检测算法,搜索图中是否存在死锁
(4)出现死锁时,用死锁恢复机制进行恢复
2、数据结构
(1)Available:长度为 m 的向量,每种类型可用资源的数量
(2)Allocation:一个 n * m 矩阵,当前分配给各个进程每种类型资源的数量,进程 Pi 拥有资源 Rj 的 Allocation[i, j] 个实例
3、算法
(1)初始化:Work = Available:当前资源剩余空闲量,Finish [i] = false for i:线程 i 是否结束,Work、Finish 分别是长度为 m、n 的向量
(2)找出满足:Finish [i] = false && Requesti <= Worki 的进程 i,找到转(3),没有找到转(4)
(3)线程 i 没有结束,且资源需求量小于当前剩余空闲资源量,则回收线程 i 的资源:Work = Work + Allocationi,Finish[i] = true,转到(2)
(4)所有进程的 Finish 为 false,若存在 Finish[i] == false,表示系统处于死锁状态
4、缺点
(1)时间复杂度 O(m * n2),时间开销大
(2)死锁检测的时间和周期选择依据:死锁多久可能会发生、多少进程需要被回滚、资源图可能有多个循环
(3)难于分辨造成死锁的关键进程
死锁恢复
1、终止所有的死锁进程:比较暴力,能解决问题但是没有实际意义
2、在一个时间内终止一个进程直到死锁解除
3、考虑因素
(1)进程的优先级
(2)进程运行了多久以及需要多少时间才能完成
(3)进程占用的资源
(4)进程完成需要的资源
(5)多少进程需要被终止
(6)进程是交互的还是批处理的
4、终止策略
(1)终止:以最小成本,选择一个受害者
(2)回滚:返回到一些安全状态,重启进程到安全状态
(3)饥饿:同一进程可能一直被选作受害者,包括回滚的数量
进程通讯
1、IPC:Inter Process Communication
2、进程通信是进程进行通信和同步的机制
3、基本操作
(1)发送操作:send(message)
(2)接收操作:receive(message)
4、流程
(1)在通信进程间建立通信链路
(2)通过 send / receive 交换消息
5、实现
(1)物理:如,共享内存,硬件总线
(2)逻辑:如,逻辑属性
直接通信
1、两个进程通过共享信道直接发消息
2、进程必须正确的命名对方
(1)send(P, message):发送信息到进程 P
(2)receive(Q, message):从进程 Q 接受消息
3、通信链路的属性
(1)自动建立链路
(2)一条链路恰好对应一对通信进程
(3)每对进程之间只有一个链接存在
(4)链接可以是单向的,但通常为双向的
4、需要 OS 支持打破隔离
间接通信
1、进程先把消息发到内核里,内核再把消息发给另一个进程
2、通过 OS 维护的消息队列,实现进程间的消息接收和发送
(1)每个消息队列都有一个唯一的标识
(2)只有共享了相同消息队列的进程,才能够通信
3、通信链路的属性
(1)只有共享了相同消息队列的进程,才建立连接
(2)连接可以是单向或双向
(3)消息队列可以与多个进程相关联
(4)每对进程可以共享多个消息队列
4、通信流程
(1)创建一个新的消息队列
(2)通过消息队列发送和接收消息
(3)销毁消息队列
5、基本通信操作
(1)send(A, message):发送消息到队列 A
(2)receive(A, message):从队列 A 接受消息
6、只关注发送、接收的目的地,不用管怎么发送
阻塞与非阻塞通信
1、进程通信可划分为阻塞(同步)或非阻塞(异步)
2、阻塞通信
(1)阻塞发送:发送者在发送消息后进入等待,直到接收者成功收到
(2)阻塞接收:接收者在请求接收消息后进入等待,直到成功收到一个消息
3、非阻塞通信
(1)非阻塞发送:发送者在消息发送后,可立即进行其他操作
(2)非阻塞接收:没有消息发送时,接收者在请求接收消息后接收不到任何消息
通信链路缓存
1、不论发送方 / 接收方临时不能处理的缓存信息,为了避免发送方和接收方的不匹配(发送、接收过快),设置缓存来提高效率,缓存会存在选择容量的问题
2、3 种方式
(1)0 容量:发送方必须等待接收方
(2)有限容量:通信链路缓冲队列满时,发送方必须等待
(3)无限容量:发送方不需要等待
常用 IPC 手段
1、信号
2、管道
3、消息队列
4、共享内存
信号
1、进程间的软件中断通知和处理机制
2、信号的接收处理
(1)捕获(catch):执行进程指定的信号处理函数被调用
(2)忽略(Ignore):执行操作系统指定的缺省处理,如:进程终止、进程挂起等
(3)屏蔽(Mask):禁止进程接收和处理信号,可能是暂时的(当处理同样类型的信号)
3、不足:传送的信息量小,只有一个信号类型
4、实现
(1)进程在系统调用接口,注册信号处理函数
(2)内核分发信号到进程的信号处理函数
(3)进程执行信号处理函数
管道
1、进程间基于内存文件的通信机制
(1)子进程从父进程继承文件描述符
(2)缺省文件描述符:0 stdin,1 stdout,2 stderr
2、进程不知道 / 不关心另一端
(1)可能从键盘、文件、程序读取
(2)可能写入到终端、文件、程序
3、与管道相关的系统调用
(1)读管道:read(fd, buffer, nbytes)
(2)scanf() 是基于(1)实现的
(3)写管道:write(fd, buffer, nbytes)
(4)printf() 是基于(3)实现的
(5)创建管道:pipe(rgfd)
(6)rgfd:是2个文件描述符组成的数组
(7)rgfd[0]:是读文件描述符
(8)rgfd[1]:是写文件描述符
消息队列
1、由 OS 维护的,以字节序列为基本单位的,间接通信机制
(1)每个消息(Message)是一个字节序列
(2)相同标识的消息组成一个消息队列(Message Queue)按先进先出顺序管理
2、消息队列的系统调用
(1)msgget(key, flags):获取消息队列标识
(2)msgsnd(QID, buf, size, flags):发送消息
(3)msgrcv(QID, buf, size, type, flags):接收消息
共享内存
1、把同一个物理内存区域,同时映射到多个进程的内存地址空间的通信机制
2、进程
(1)每个进程都有私有内存地址空间
(2)每个进程的内存地址空间需明确设置共享内存段
3、线程:同一进程中的线程总是共享相同的内存地址空间
4、优点:速度最快,传输量最大,共享数据方便,一个进程写另外一个进程立即可见,没有系统调用干预,没有数据复制
5、缺点:不提供同步,必须用额外的同步机制来协调数据访问
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战