第11章:死锁和进程通信
操作系统
11.1 死锁概念
- defs:死锁是进程之间由于共享资源所导致的一种无限期等待的情况
- 由于竞争资源或者通信关系,两个或更多线程在执行中出现永远相互等待,只能由其他进程引发的事件
进程访问资源的流程
- 资源类型R1,R2,...,Rm
- 每类资源Ri有Wi个实例
- 进程访问资源的流程
资源分类
- 可重用资源(Reusable Resource)
- 资源不能被删除且在任何时刻只能有一个进程使用
- 进程释放资源后,其他进程可重用
- 可重用资源示例
- 硬件:处理器、I/O通道、主和副存储器、设备等
- 软件:文件、数据库和信号量等数据结构
- 可能出现死锁
- 消耗资源(Consumable resource)
出现死锁的必要条件
- 互斥
- 持有并等待
- 进程保持至少一个资源,并正在等待获取其他进程特有的资源
- 非抢占
- 循环等待
11.2 死锁处理方法
- 死锁预防(Deadlock Prevention)
- 死锁避免(Deadlock Avoidance)
- 在使用前进行判断,只允许不会出现死锁的进程请求资源
- 死锁检测和恢复(Deadlock Detection & Recovery)
- 由应用进程处理死锁
(一)死锁预防:限制申请方式
- 预防是采用某种策略,限制并发进程对资源的请求,使系统在任何时刻都不满足死锁的必要条件
- 互斥
- 持有并等待
- 进程请求资源时,要求它不持有任何其他资源
- 仅允许进程在开始执行时,一次请求所有需要的资源
- 资源利用率低
- 非抢占
- 如进程请求不能立即分配的资源,则释放已有资源
- 只在能够同时获得所有需要资源时,才执行分配操作
- 循环等待
(二)死锁避免
- 利用额外的先验信息,在分配资源时判断是否会出现死锁,只在不会死锁时分配资源
- 要求进程声明需要资源的最大数目
- 限定提供与分配的资源数量,确保满足进程的最大需求
- 动态检查资源分配状态,确保不会出现环形等待
系统资源分配的安全状态
- 当进程请求资源时,系统判断分配后是否处于安全状态
- 系统处于安全状态
- 序列是安全的
- Pi要求的资源<=当前可用资源+所有Pj持有资源,其中j
- 如Pi的资源请求不能立即分配,则Pi等待所有Pj完成
- Pi完成后,P i+1 可得到所需资源,执行并释放所分配的资源
- 最终整个序列的所有Pi都能获得所需资源
安全状态与死锁的关系
- 系统处于安全状态,一定没有死锁
- 系统处于不安全状态,可能出现死锁
11.3 银行家算法
- 银行家算法是一个避免死锁产生的算法,以银行借贷发配策略为基础,判断并保证系统处于安全状态
银行家算法:数据结构
- n = 线程数量, m = 资源类型数量
- Max(总需求量):n×m矩阵
- 线程Ti最多请求类型Rj的资源 Max[i,j]个实例
- Available(剩余空闲量):长度为m的向量
- 当前有Available[i]个类型Rj的资源实例可用
- Allocation(已分配量):n×m矩阵
- 线程Ti当前分配了 Allocation[i,j]个Rj的实例
- Need(未来需要量):n×m矩阵
- Need[i,j] = Max[i,j] - Allocation[i,j]
银行家算法:安全状态判断
1. Work 和 Finish 分别是长度为m和n的向量初始化:
Work = Available //当前资源剩余空闲量
Finish[i] = false for i:1,2,...,n //线程i没结束
2. 寻找线程Ti:
(a)Finish[i] = false //接下来找出Need比Work小的线程i
(b) Need[i] <= Work
没有找到满足的条件Ti,转4
3. Work = Work + Allocation[i] //线程i的资源需求量小于当前剩余空闲资源量,所以配置给它再回收
Finish[i] = true
转2
4. 如所有线程Ti满足Finish[i] == true,则系统处于安全状态 //所有线程的Finish为True,表明系统处于安全状态
银行家算法
初始化:Requesti 线程Ti的资源请求向量
Requesti [j] 线程Ti请求资源Rj的实例
循环:
1. 如果Requesti <= Need[i],转到2。否则拒绝资源申请,因为线程已经超过了其最大要求
2. 如果Requesti <= Available,转到3。否则,Ti必须等待,因为资源不可用
3. 通过安全状态来确定是否分配资源给Ti:
生成一个需要判断状态是否安全的资源分配环境
Available = Available - Requesti;
Allocation[i] = Allocation[i] + Requesti;
Need[i] = Need[i] - Requesti;
调用安全状态判断
如果返回结果是安全,将资源分配给Ti
如果返回结果不安全,系统会拒绝Ti的资源请求
11.4 死锁检测
- 允许系统进入死锁状态
- 维护系统的资源分配图
- 定期调用死锁检测算法来搜索图中是否存在死锁
- 出现死锁时,用死锁恢复机制进行恢复
死锁检测:数据结构
- Available:长度为m的向量
- Allocation:一个 n×m矩阵
- 当前分配给各个进程每种类型资源的数量
- 进程Pi拥有资源Rj的Allocation[i,j]个实例
死锁检测算法
1. Work 和 Finish 分别是长度为m和n的向量初始化:
Work = Available //Work为当前资源剩余空闲量
Allocation[i] > 0 时,Finish[i] = false //finish为线程是否结束
否则,Finish[i] = true
2. 寻找线程Ti满足:
(a)Finish[i] = false //寻找没有结束的线程,且此线程将需要的资源量小于当前空闲资源量
(b) Requesti <= Work
没有找到满足的条件Ti,转4
3. Work = Work + Allocation[i] //把找到的线程拥有的资源释放回当前空闲资源中
Finish[i] = true
转2
4. 如某个 Finish[i] == false,则系统处于死锁状态 //如果有Finish为false,表明系统处于死锁状态
- 算法需要O(m×n^2)操作检测是否系统处于死锁状态
死锁检测算法的使用
死锁恢复:进程终止
- 终止所有的死锁进程
- 一次只终止一个进程直到死锁消除
- 终止进程的顺序应该是
- 进程的优先级(由低到高)
- 进程已运行时间以及还需运行时间(保留运行时间较长者)
- 进程已占用资源
- 进程完成需要的资源
- 终止进程数目(越少越好)
- 进程是交互还是批处理(优先让用户交互的进程执行下去)
死锁恢复:资源抢占
11.5 进程通信概念
进程通信(IPC, Inter-Process Communication)
- 进程通信是进程进行通信和同步的机制
- IPC提供两个基本操作
- 发送操作:send(message)
- 接收操作:receive(message)
- 进程通信流程
- 在通信进程间建立通信链路
- 通过send/receive交换信息
- 进程链路特征
- 物理(如,共享内存、硬件总线)
- 逻辑(如,逻辑属性)
直接通信
- 进程必须正确的命名对方
- send(P, message) - 发送信息到进程P
- receive(Q, message) - 从进程Q接收信息
- 通信链路属性
- 自动建立链路
- 一条链路恰好对应一对通信进程
- 每对进程之间只有一个链接存在
- 链接可以是单向的,但通常是双向的
间接通信
- 通过操作系统维护的消息队列实现进程间的消息接收和发送
- 每个消息队列都有一个唯一的标识
- 只有共享了相同的消息队列的进程,才能够通信
- 通信链路的属性
- 只有共享了相同的消息队列的进程,才建立连接
- 连接可以是单向或双向
- 消息队列可以与多个进程相关联
- 每对进程可以共享多个消息队列
- 通信流程
- 创建一个新的消息队列
- 通过消息队列发送和接收消息
- 销毁消息队列
- 基本通信操作
- send(A, message) - 发送消息到队列A
- receive(A, message) - 从队列A接收消息
阻塞与非阻塞通信
- 进程通信可划分为阻塞(同步)或非阻塞(异步)
- 阻塞通信
- 阻塞发送
- 阻塞接收
- 接收者在请求接收消息后进入等待,直到成功收到一个消息
- 非阻塞通信
- 非阻塞发送
- 非阻塞接收
- 没有消息发送时,接收者在请求接收消息后,接收不到任何消息
通信链路缓冲
11.6 信号量和管道
(一)信号
信号
- 进程间的软件中断通知和处理机制
- 如,SIGKILL, SIGSTOP, SIGCONT等
信号的接收处理
- 捕获(Catch)
- 忽略(Ignore)
- 屏蔽(Mask)
不足
(二)管道
管道(pipe)
- 进程间基于内存文件的通信机制
- 子进程从父进程继承文件描述符
- 缺省文件描述符:0 stdin , 1 stdout, 2 strerr
- 进程不知道(或不关心)另一端
- 可能从键盘、文件、程序读取
- 可能写入到终端、文件、程序
与管道相关的系统调用
- 读管道:read(fd, buffer, nbytes)
- 创建管道:pipe(rgfd)
- rgfd是2个文件描述符组成的数组
- rgfd[0]是读文件描述符
- rgfd[1]是写文件描述符
11.7 消息队列和共享内存
(一)消息队列
- 消息队列是由操作系统维护的以字节序列为基本单位的间接通信机制
- 每个消息(message)是一个字节序列
- 相同标识的消息按先进先出顺序组成一个消息队列(Message Queues)
消息队列的系统调用
- msgget(QID, buf, size, flags)
- msgrcv(QID, buf, size, type, flags)
- 接收消息
- type:指定了函数从队列中所取的消息的类型。函数将从队列中搜索类型与之匹配的消息并将之
返回。不过这里有一个例外。如果mtype 的值是零的话,函数将不做类型检查而自动返回
队列中的最旧的消息。
- flags:控制函数行为的标志,取值可以是:0, 表示忽略;
- msgctl(...)
(二)共享内存
- 共享内存是把同一个物理内存区域同时映射到多个进程的内存地址空间的通信机制
进程
- 每个进程都有私有地址内存空间
- 每个进程的内存地址空间需要明确设置于共享内存段
线程
优点
不足
特点
- 最快的方法
- 一个进程写另一个进程立即可见
- 没有系统调用干预
- 没有数据复制
- 不提供同步
共享内存的系统调用