一、死锁
死锁:一组阻塞的进程(两个或多个),持有一种资源,等待获取另一个进程所占有的资源,而导致谁都无法执行。
可重复使用的资源:
- 在一个时间只能一个进程使用,且不能被删除。
- OS避免杀死拥有资源的进程;进程使用资源后要释放,其他进程可重用;
- 有物理资源(cpu, I/O通道,主和副存储器),也有抽象的资源(设备和数据结构,如文件,数据库和信号量);
- 如果每个进程拥有一个资源并请求其他资源,可能导致死锁
资源分配图
死锁的必要条件
- 互斥:任何时刻只能有一个进程使用一个资源实例
- 持有并等待:进程保持至少一个资源,并正在等待获取其他进程持有的资源
- 无抢占,一个资源只能被进程自愿释放
- 循环等待,形成闭环
二、死锁处理方法
1、死锁预防(dead-lock prevention )
打破死锁出现的条件 ,确保系统永远不会进入死锁状态
- 互斥:互斥的共享资源封装成可同时访问。占用非共享资源 会增加不确定性 不推荐
- 占用并等待:保证当一个进程请求资源时,不持有任何其他的资源;all or nothing 需要进程请求并分配其所有资源。资源利用率低,可能饥饿
- 无抢占:允许抢占占有某些资源的进程。如进程请求不能立即分配的资源,则释放已占有资源,只在能够同时获得所有需要资源时,才执行分配操作
- 循环等待:对所有资源类型进行排序,并要求每个进程按照资源的顺序进行申请,会出现资源利用不够
2、死锁避免( deadlock avoidance )
当进程运行过程中,根据申请资源的情况判断会不会死锁,如果会就不给资源。
最简单有效就是要求每个进程声明它需要的每个类型资源的最大数目,资源在分配中根据最大数目限定提供与分配的资源数目,死锁避免算法要动态检查资源的分配状态来避免环形等待。
环形等待不一定会死锁。分为安全状态,不安全状态
3、死锁检测和恢复(Deadlock Detection & Recovery)
在检测到运行系统进入死锁状态后,进行恢复
4、由应用程序处理死锁
通常操作系统忽略死锁,大多数操作系统(包括UNIX)的做法
三、银行家算法
1、有多个资源实例 ;每个进程必须能最大限度地利用资源 ;找到理想执行时序,找到就认为是安全的
类比:银行家——操作系统;资金——资源;客户——申请资源的线程
2、银行家算法:数据结构
- n=线程数量 m=资源类型数量(一个线程可能需要多种资源)
- Max 总需求量:n*m矩阵 max[i, j]=k 进程Pi最多需要资源类型Rj的k个实例
- Available 剩余空闲量:长度为m的向量,如果available[j]=k 则有k个类型Rj的资源实例可用
- Allocation 已分配量:n*m矩阵 allocation[I, j]=k 进程Pi已经分配了资源类型Rj的k个实例
- Need 未来需要量: n*m矩阵 need[i, j]=k 进程Pi可能需要资源类型Rj的k个实例
- Max[i, j]=need[I,j]+allocation[i,j]
3、安全状态判断
四、进程通信概念
1、进程通信( IPC: inter process communication)
- 进程通信:是进程进行通信和同步的机制
- IPC facility 提供2个操作: send(message)、receive (message)
- 进程P和Q想通信,需要: 在它们之间建立通信链路 ,通过send/receive交换消息
- 通信链路的实现: 物理(例如,共享内存,硬件总线)、 逻辑(如逻辑属性)
2、直接通信
为了实现直接通信,要有发送和接收的ID ;进程必须正确地命名对方 ;
- send( P, message) - 发送信息到进程P ;receive( P, message) - 接收进程P的信息
- 通信链路的属性 :自动建立链路 ;一条链路对应一对通信进程 ;每对进程之间只有一个链接存在 ;链接可以是单向的,但通常为双向的
2、间接通信
为了实现间接通信,要发送到共享区,发送方和接收方都不关注具体的另一方是谁
每个消息队列都有一个唯一的ID;只有共享了相同消息队列的进程,进程才能通信
- 通信链路的属性:只有共享了相同消息队列的进程,才建立链路;链接可以与许多进程相关联;链接可以是单向或双向
- 通信流程: Ⅰ、建立一个新的消息队列 ;Ⅱ、通过消息队列发送和接收消息 ;Ⅲ、销毁消息队列
- 基本通信操作: send(A, message) – 发送消息到队列A ;receive(A, message) - 从队列A接受消息
3、阻塞与非阻塞通信
进程通信可划分为阻塞(同步)和非阻塞(异步)
- 阻塞通信:阻塞发送、阻塞接收
- 非阻塞通信:非阻塞发送、非阻塞接收
4、通信链路缓冲
队列的消息被附加到链路的3种方式
- 0容量—0 messages 发送方必须等待接收方
- 有限容量—n messages 的有限长度,发送方在队列满的时候要等待
- 无限容量—理想状况,不用等
六、信号和管道
1、信号(Signal)
定义:信号是进程间的软件中断通知和处理机制,如:SIGKILL,SIGSTOP等
信号的接收处理
- 捕获 catch : 指定信号处理函数被调用
- 忽略 ignore: 依靠操作系统的默认操作 , 如进程终止、进程挂起
- 屏蔽 mask:禁止进程接收和处理信号(可能是暂时的,当处理同样类型的信号)
不足:不能传输要交换的任何数据
优点:效率很高,异步。一般处理完后会回到被打断的进程。
2、管道
管道是进程间基于内存文件的通信机制。子进程从父进程继承包括文件描述符等的资源。进程不知道另一端:可能从键盘、文件、程序读取;可能写入终端、文件、程序。
缺省文件描述符:0 stdin, 1 stdout, 2 stderr
与管道相关的系统调用:
- 读管道:read(fd,buffer,nbytes) 如scanf()是基于它实现的
- 写管道:write(fd,buffer,nbytes) 如printf()是基于它实现的
- 创建管道:pipe(rgfd) rgfd是2个文件描述符组成的数组。rgfd[0]是读文件描述符;rgfd[1]是写文件描述符
管道示例
UNIX想灵活组合小程序,一个的输出是另一个的输入,用竖线|来表示把ls的输出stdout重定向,通过一个类似buffer的管道作为stdin传输给more。More以为是从i/o给它的,实际是ls给它的。进程不关心。
七、消息队列和共享内存
1、消息队列
管道必须有父进程,数据是字节流,没有数据结构。消息队列可以多个不相干的进程来传递数据,而且message作为一个字节序列存储,message quenues是消息数组。是一个有意义的结构化。 按FIFO/FILO管理
消息队列是由操作系统维护的以字节序列为基本单位的间接通信机制。每个消息是一个字节序列;相同标识的消息组成按先进先出顺序组成一个消息队列。
消息队列的系统调用
- msgget(key,flags)获取消息队列标识
- msgsnd(QID,buf,size,flags)发送消息
- msgrcv(QID,buf,size,type,flags)接收消息
- msgctl(...)消息队列控制
2、共享内存
共享内存是把同一个物理内存区域同时映射到多个进程的内存地址空间的通信机制。
每个进程都有私有内存地址空间;每个进程的内存地址空间需明确设置共享内存段;
同一进程中的线程总是共享相同的内存地址空间
优点:快速、方便地共享数据;
缺点:必须用额外的同步机制来协调数据访问
共享内存是最快的方法,一个进程写另一个进程立即可见,没有系统调用干预,没有数据复制。
共享内存系统调用
- shmget(key,size,flags)创建共享段
- shmat(shmid,*shmaddr,flags)把共享段映射到进程地址空间
- shmdt(*shmaddr)取消共享段到进程地址空间的映射
- shmctl(...)共享段控制
需要信号量等机制协调共享内存的访问冲突。