死锁
资源类型: R 1 , R 2 , . . . , R m R_1, R_2, ..., R_m R1,R2,...,Rm,每类资源 R i R_i Ri 有 W i W_i Wi 个实例
进程访问资源:请求(申请空闲资源)、使用(占用资源)、释放(资源状态由占用变空闲)
死锁:由于竞争资源或者通信关系,两个或更多线程在执行中出现永远相互等待只能由其他进程引发的事件。
必要条件:
- 互斥:任一时刻只能有一个进程使用一个资源实例
- 请求并保持:进程保持至少一个资源时,并请求其他进程持有的资源
- 非抢占:资源只能等待进程使用完自愿释放
- 循环等待:等待进程集合 P 0 , P 1 , . . . , P N {P_0, P_1, ..., P_N} P0,P1,...,PN, P 0 P_0 P0 等待 P 1 P_1 P1 所占用的资源, P 1 P_1 P1 等待 P 2 P_2 P2 所占用的资源 … P N P_N PN 等待 P 0 P_0 P0 所占用的资源
1. 死锁预防
死锁预防:破坏死锁必要条件,确保系统永远不会进入死锁状态
- 互斥:把互斥的共享资源封装成可同时访问
- 请求和保持:进程在开始运行时,一次性申请所有需要的资源
- 非抢占:进程请求不能立即分配的资源,则释放已占有资源
- 循环等待:对资源排序,要求进程按顺序请求资源
2. 死锁避免
死锁避免:利用额外的先验信息,在分配资源的时候判断是否会出现死锁,只有在不会出现死锁时分配资源。
- 要求进程声明需要资源的 最大 数目
- 限定 提供 与 分配 的资源数量,确保满足进程的 最大 需求
- 动态检查 资源的分配状态,确保不会出现环形等待
银行家算法:
线程数量为 n n n,资源类型数量为 m m m,最大需求 n × m n×m n×m 矩阵 Max
,已分配 n × m n×m n×m 矩阵 Allocation
,未来需求 n × m n×m n×m 矩阵 Need
,其中 Need[i, j] = Max[i, j] - Allocation[i, j]
当前可用资源向量Available
,Requesti
代表线程 T i Ti Ti 请求资源向量
如果 Requesti > Need[i]
,则超过线程 T i Ti Ti 最大需求,拒绝资源申请;
如果 Requesti > Available
,则资源不可用,线程 T i Ti Ti 等待,否则尝试把资源分配给线程 T i Ti Ti,并修改下列数据额结构的数值:
Available = Available - Need[i]
Allocation[i, j] = Allocation[i, j] + Requesti
Need[i] = Need[i] - Requesti
系统执行安全性算法,检查此次分配后,系统是否处于安全状态。若安全,则执行资源分配给线程 T i Ti Ti,否则恢复原来的资源分配状态,线程 T i Ti Ti 等待。
安全性算法:
- 初始时安全序列为空
- 从
Need
矩阵找到符合下面条件的行:该行对应的线程不在安全序列中且该行小于等于Available
向量,把对应的线程加入到安全序列,如果找不到则系统处于不安全状态 - 线程 T i T_i Ti 进入安全序列后,顺利执行完毕并释放所持有的资源
Available = Available + Allocation[i]
例题:
某系统有 R 1 R1 R1、 R 2 R2 R2 和 R 3 R3 R3 共三种资源,在 T 0 T0 T0 时刻 T 1 T1 T1、 T 2 T2 T2、 T 3 T3 T3 和 T 4 T4 T4 这四个线程对资源的占用和需求情况如下表,此时系统的系统总资源为 (9, 3, 6)
和可用资源向量 Available(2, 1, 2)
,试问:
如果此时线程 T 1 T1 T1 和 T 2 T2 T2 均发出资源请求向量 Request(1, 0, 1)
,为了保证系统的安全性,如何分配资源给这两个线程?说明所采用策略的原因。
解:
若此时 T 1 T1 T1 发出资源请求 Request(1, 0, 1)
,按照银行家算法检查:
Request(1, 0, 1) <= Need(2, 2, 2)
Request(1, 0, 1) <= Available(2, 1, 2)
试分配并修改相应数据结构,由此形成的线程 T 1 T1 T1 请求资源后的资源分配情况如下
利用安全性算法检查系统是否安全 ,可用资源向量 Available(1, 1, 1)
已不能满足任何线程,进入不安全状态,此时系统不能讲资源分配给线程 T 1 T1 T1
若此时 T 2 T2 T2 发出资源请求 Request(1, 0, 1)
,按照银行家算法检查:
Request(1, 0, 1) <= Need(2, 0, 2)
Request(1, 0, 1) <= Available(2, 1, 2)
试分配并修改相应数据结构,由此形成的线程 T 2 T2 T2 请求资源后的资源分配情况如下
利用安全性算法检查系统是否安全,可用资源向量 Available(1, 1, 1)
仍可满足线程 T 2 T2 T2 的资源请求,最终可得安全序列 { T 2 , T 3 , T 4 , T 1 } \{ T2, T3, T4, T1 \} {
T2,T3,T4,T1}
死锁是线程提出资源请求且全部线程进入阻塞状态。如果两个申请立即得到满足,系统处于不安全状态但并没有立刻进入死锁状态,因为此时所有的线程没有提出新的资源请求,全部线程均没有因资源请求没得到满足而进入阻塞状态。
进程通信
进程通信(IPC):进程进行通信和同步的机制。进程之间建立通信链路,使用 send/receive
原语交换信息。
- 信号量:用于进程间互斥同步,比如一个进程要对一个共享区域进行写操作,这时不想别的进程对其进行写操作,那么就获取信号量,写完再释放,别的进程就可以读/写操作了。
- 管道通信:管道是用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件。管道只能采用半双工通信,即某一时刻只能单向传输。
pipe()
函数创建管道,设置stdout
为 管道写端,设置stdin
为管道读端。管道没有名字,所以只能由有亲缘关系的进程使用,如父子进程之间传递信息。
- 共享内存:同一个物理内存区域同时映射到多个进程的地址空间的通信机制。进程通过对该片物理内存进行读/写操作交换信息,需要 使用同步互斥根据控制读/写操作。
- 消息队列:消息队列是由操作系统维护的以字节序列为基本单位的间接通信机制。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
-
套接字:常用于网络编程,可以用于不同主机之间的网络通信,也可以用于同一主机的不同进程之间的通信。
-
信号:通知某时间发生了,如SIGPIPE,这个是在socket编程的时候,send数据给对方的时候,对方非正常关闭时,操作系统会告诉进程这个信号,即对方已经异常退出。