【操作系统-进程】进程的概念
0 补充
- 并发:指两个或多个事件在同一时间间隔内发生。
- 并行:指两个或多个事件在同一时刻发生。
【注】对于单处理机,在多道程序环境下,一段时间内,宏观上有多道程序在同时执行,而在每个时刻,单处理机仅能有一道程序执行。此时操作系统
是通过分时来实现并发性的,没有真正实现并行性。
1 进程
1.1 进程的组成(进程实体/进程映像)
一个进程实体(进程映像)由 PCB、程序段、数据段组成。进程是动态的,进程实体(进程映像)是静态的。进程是进程实体的运行过程,是系统进行资源分配、拥有资源和调度的一个基本单位。
程序和进程的区别:
项目 | 程序 | 进程 |
---|---|---|
组成 | 代码和数据 | 代码,数据和 PCB |
特点 | 永久的,静态的 | 暂时的,动态的 |
对应关系 | 程序代码经过多次创建可对应不同进程 | 一个进程可由系统调用方法被不同进程多次调用 |
- 进程控制块(PCB):PCB 是进程存在的唯一标志,当进程被创建时,操作系统为其创建 PCB,当进程结束时,会回收其 PCB。
项目 | 内容 |
---|---|
进程描述信息 | 进程标识符 PID,用户标识符 UID |
进程控制和管理信息 | 各硬件使用情况信息,进程当前状态 |
资源分配清单 | 使用的文件、内存区域、I/O设备 |
CPU 相关信息 | PSW、PC,用于实现进程切换 |
- 程序段:即程序的代码(指令序列)。
- 数据段:运行过程中产生的数据。
1.2 进程的状态
- 进程的状态:
进程的状态 | 描述 | CPU 占用 | 拥有资源 |
---|---|---|---|
创建态 | 进程正在被创建时,它的状态是“创建态”,在这个阶段操作系统会为进程分配资源、初始化 PCB | ~ | ~ |
就绪态 | 当进程创建完成后,便进入“就绪态”,处于就绪态的进程已经具备运行条件,但由于没有空闲 CPU,就暂时不能运行 | 否 | 是 |
运行态 | 如果一个进程此时在CPU上运行,那么这个进程处于“运行态” | 是 | 是 |
阻塞态 | 进程请求等待某个事件的发生,在这个事件发生之前,进程无法继续往下执行,此时操作系统会让这个进程下 CPU,并让它进入“阻塞态” | 否 | 否 |
终止态 | 进程请求操作系统终止该进程,此时操作系统会让该进程下 CPU,并回收内存空间等资源,最后还要回收该进程的 PCB | ~ | ~ |
- 进程的状态转换:
进程的状态转换 | 原语操作 | 何事件会引起状态转换? | 进程主动还是被动发起? |
---|---|---|---|
创建态-->就绪态 | 创建原语 | 用户登录、作业调度、提供服务、应用请求 | 主动/被动 |
就绪态-->运行态 | 切换原语 | 其他进程的时间片用完、更高优先级进程到达 | 被动(操作系统发起) |
运行态-->就绪态 | 切换原语 | 该进程的时间片用完、更高优先级进程到达 | 被动(操作系统发起) |
运行态-->阻塞态 | 切换原语 | 当前进程主动阻塞 | 主动 |
阻塞态-->就绪态 | 唤醒原语 | 等待的事件发生 | 被动(操作系统发起) |
运行态-->终止态 | 撤销原语 | 正常结束、异常结束、用户干预 | 主动/被动 |
阻塞态-->终止态 | 撤销原语 | 正常结束、异常结束、用户干预 | 主动/被动 |
就绪态-->终止态 | 撤销原语 | 正常结束、异常结束、用户干预 | 主动/被动 |
【注 1】假设一个单处理器系统,若同时存在 m 个进程,则 m 个进程不可能同时处于就绪态,但 m 个进程可能同时处于阻塞态(此时就绪队列为空),这时 CPU 将调度闲逛进程(idel)。
【注 2】系统中有 n 个进程,其中至少有一个进程运行,则就绪队列中最多有 n–1 个进程。
- 原语:原语的执行具有原子性,即执行过程只能一气呵成,期间不允许被中断,可以用“关中断指令”和“开中断指令”这两个特权指令实现原子性。
原语执行的一般过程:
- 更新 PCB 信息:(a)修改当前进程状态标志;(b)往 PCB 保存当前进程的运行环境;(c)根据 PCB 恢复欲切换进程的运行环境
- 将 PCB 插入对应的队列
- 分配/回收资源
原语操作 | 过程 |
---|---|
创建原语 | (1)申请空白 PCB;(2)为新进程分配资源;(3)初始化 PCB;(4)将 PCB 插入就绪队列 |
切换原语 | (1)保护进程运行现场;(2)PCB 状态信息改为就绪态或阻塞态;(3)将 PCB 插入对应资源的就绪队列或等待队列 |
唤醒原语 | (1)等待队列找到该进程;(2)PCB 状态信息改为就绪态,将 PCB 从等待队列中移出;(3)将 PCB 插入对应资源的等待队列 |
撤销原语 | (1)找到该 PCB;(2)若处于运行态,立即剥夺 CPU;(3)终止其所有子进程,将资源还给父进程或操作系统;(4)删除 PCB |
发送原语 | 略 |
接收原语 | 略 |
【注】切换原语(切换到阻塞态)和唤醒原语需一一对应。
1.3 进程的通信(IPC)
- 共享存储:划分一块共享空间,通过对这片共享空间进行读写操作实现进程通信,需要使用同步互斥工具(PV操作)。
方式 | 描述 |
---|---|
低级方式 | 基于数据结构的共享 |
高级方式 | 基于存储区的共享 |
- 消息传递:进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。
消息传递方式 | 描述 |
---|---|
直接通信方式 | 发送进程直接把消息发送给接收进程 |
间接通信方式 | 以“信箱”作为中间实体进行消息传递 |
间接通信方式:可以多个进程往同一个信箱 send 消息,也可以多个进程从同一个信箱中 receive 消息。
- 管道通信
管道文件:“管道”是一个特殊的共享文件,又名 pipe 文件,其实就是在内存中开辟一个大小固定的内存缓冲区。
项目 | 内容 |
---|---|
通信方式 | 半双工通信 |
进程访问 | 互斥进行 |
管道写满 | 写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程 |
管道读空 | 读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程 |
【注】多进程读一个管道的解决方案:一个管道允许多个写进程,一个读进程。
2 线程
2.1 线程与进程的比较
项目 | 引入进程和线程的操作系统 | 仅引入进程的操作系统 |
---|---|---|
状态 | 线程和进程都有创建态、就绪态、运行态、阻塞态、终止态 | 进程有创建态、就绪态、运行态、阻塞态、终止态 |
组成 | 线程 ID、线程控制块(TCB);进程 ID、进程控制块(PCB) | 进程 ID、进程控制块(PCB) |
调度 | 同一进程下的线程切换不引起进程切换,调度开销小;从一个进程中的线程切换到另一个进程中的线程引起进程切换(线程是调度的基本单位) | 一定引起进程切换,调度开销大(进程是调度的基本单位) |
系统资源 | 进程拥有系统资源,线程不拥有系统资源,但可以访问它从属于进程的系统资源(进程是资源分配的基本单位) | 进程拥有系统资源(进程是资源分配的基本单位) |
系统开销 | 创建、撤销、切换线程的开销比进程的小 | 创建、撤销、切换进程的开销大 |
独立性 | 每个进程都有自己独立的地址空间,而同一进程的不同线程共享同一地址空间 | 每个进程都有自己独立的地址空间 |
处理机 | 多个线程尅分配在多个处理机上 | 进程只能运行在一个处理机上 |
2.2 线程的实现
项目 | 用户级线程 | 内核级线程 |
---|---|---|
谁来完成管理? | 应用程序 | 操作系统内核 |
在哪完成切换? | 用户态 | 内核态 |
系统开销 | 开销小,效率高 | 开销大,效率慢 |
线程阻塞会发生什么? | 整个进程被阻塞,并发度低 | 同一进程中的其他线程还可以继续执行,并发度高 |
2.3 多线程模型
项目 | 一对一模型 | 多对一模型 | 多对多模型 |
---|---|---|---|
描述 | 在同一进程中,每个用户级线程各自映射到不同内核级线程(一对一) | 在同一进程中,多个用户级线程映射到一个内核级线程(多对一) | n 个用户级线程映射到 m 个内核级线程(n >= m)(多对多) |
占用的内核级线程 | 多,每个用户进程有与用户级线程同数量的内核级线程 | 少,一个进程只被分配一个内核级线程 | 一般,每个用户进程对应 m 个内核级线程 |
在哪完成切换? | 内核态 | 用户态 | 内核态 、用户态 |
系统开销 | 开销大,效率慢 | 开销小,效率高 | 一般 |
线程阻塞会发生什么? | 同一进程中的其他线程还可以继续执行,并发度高 | 整个进程被阻塞,并发度低 | ~ |
2.4 相关例题
下面是一道将进程与线程、互斥结合在一起的考题:
【例】进程 P1 和 P2 均包含并发执行的线程,部分伪代码描述如下所示:
// 进程 P1
int x=0;
thread1()
{
int a;
a=1; // 1
x+=1; // 2
}
thread2()
{
int a;
a=2; // 3
x+=2; // 4
}
// 进程 P2
int x=0;
thread3()
{
int a;
a=x; // 5
x+=3; // 6
}
thread4()
{
int b;
b=x; // 7
x+=4; // 8
}
需要互斥执行的操作是( )
A.a=1 与 a=2
B.a=x 与 b=x
C.x+=1 与 x+=2
D.x+=1 与 x+=3
【解】进程有独立的地址空间,因而进程 1 和进程 2 的 x 不是同一个 x;线程共享所属进程的地址空间,因而进程 1 的 x 被线程 1 和线程 2 所共享(x 是进程 1 的全局变量),进程 2 的 x 被线程 3 和线程 4 所共享(x 是进程 2 的全局变量)。在线程内定义的 a、b 变量均为线程自己所有,不共享到其他线程(局部变量)。
显然,语句 2、4 和语句 6、8 没有关系,不会发生互斥。语句 1 和 2 不会互斥,语句 5 和 6 也不会互斥,因为 a 和 b 是线程各自的局部变量。但是进程 1 内部的语句 2 和 4 会发生互斥,考虑到机器内部的执行过程,两个线程可能同时访问 x 所在的存储器,而两个线程对存储器同时进行写操作或读操作是不允许的。同理,进程 2 内部的语句 6 和 8 会发生互斥。只有 C 项符合题意。
3 调度
3.1 调度的层次
项目 | 要做什么? | 调度发生在? | 发生频率 | 对进程状态的影响 |
---|---|---|---|---|
作业调度(高级调度) | 从后备队列中选择合适的作业将其调入内存,并为其创建进程 | 外存-->内存(面向作业) | 低 | 无-->创建态-->就绪态 |
内存调度(中级调度) | 从挂起队列中选择合适的进程将其数据调回内存 | 外存-->内存(面向进程) | 中 | 挂起态-->就绪态(阻塞挂起-->阻塞态) |
进程调度(低级调度) | 从就绪队列中选择一个进程为其分配处理机 | 内存-->CPU | 低 | 就绪态-->运行态 |
【注】挂起和阻塞的区别:两种状态都是暂时不能获得CPU的服务,但挂起态是将进程映像调到外存去了,而阻塞态下进程映像还在内存中。
3.2 调度的时机
不能进行进程调度与切换的情况:
- 在处理中断的过程中
- 进程在操作系统内核程序临界区中
- 在原子操作(原语)过程中
【注】进程在普通临界区中是可以进行调度、切换的。
3.3 调度的方式
(届时将专门开一篇文章来整理)
- 非抢占式调度:又称非抢占方式。即,只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终止或主动要求进入阻塞态。
- 抢占式调度:又称抢占方式。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。
3.4 调度的算法
(届时将专门开一篇文章来整理)
- 先来先服务(FCFS)调度算法
- 短作业优先(SJF)调度算法
- 优先级调度算法
- 高响应比优先调度算法
- 时间片轮转调度算法
- 多级队列调度算法
4 同步和异步
4.1 临界资源和临界区
- 临界资源:一个时间段内只允许一个进程使用的资源称为临界资源。
- 进入区:检查是否可以进入临界区,若可以则上锁。
- 临界区(临界段):进程中访问临界资源的代码段。
- 退出区:负责解锁。
- 剩余区:其余代码部分。
4.2 同步
同步指的是进程之间的直接制约关系。
同步互斥机制应遵守的四大准则:
- 空闲让进:临界区空闲时,应允许一个进程访问。
- 忙则等待:临界区正在被访问时,其他要访问的进程需等待。
- 有限等待:进程不能一直等待临界区释放,否则出现“饥饿”现象。
- 让权等待:进不了临界区的进程要下处理机,以免进程陷入“忙等”状态。
【注 1】关于“让权等待”的问题,除了信号量方法能解决外,其他方案无法解决,因为这些方案都包含了 while 语句,进程会一直卡在 while 语句死等。
【注 2】注意区别“忙则等待”、“有限等待”和“让权等待”:违反“有限等待”是有人想进临界区,但一直进不了,一直死等;违反“让权等待”是有人进了临界区,其他人在干等这个人退出临界区;违反“忙则等待”是有人进了临界区,其他人也闯进来了。
4.3 互斥
互斥指的是进程之间的间接制约关系。
4.3.1 软件实现
方法 | 描述 | 缺点 |
---|---|---|
单标志检查法 | 在进入区只“检查”,不“上锁” | 违反“空闲让进” |
双标志先检查法 | 在进入区先“检查”后“上锁”,退出区“解锁” | 违反“忙则等待” |
双标志后检查法 | 在进入区先“上锁”后“检查”,退出区“解锁” | 违反“空闲让进”“有限等待” |
Peterson 算法 | 在进入区“主动争取-主动谦让-检查对方是否想进、己方是否谦让” | 违反“让权等待” |
如何分析出这些算法的缺点?注意到这些算法通常由“检查”和“上锁”两个操作组成,而这两个操作必须一气呵成地完成才能达到目的。因此,我们只需让这些操作不再一气呵成地完成,缺点也就自然分析出来了。
如何分析出这些算法是否可能引起饥饿?将算法的缺点分析出来,自然就知道会不会产生饥饿现象了。只有违反“有限等待”的算法可能导致饥饿现象,而违反“忙则等待”则是“喂得过饱”了。
来看下面实例:
(1)单标志检查法
// 全局变量
turn = 1;
// P1 进程
while (trun != 1); // 1(检查)
critical section;
turn = 2;
remainder section;
// P2 进程
while (turn != 2); // 2(检查)
critical section;
turn = 1;
remainder section;
- 缺点分析:交替执行语句 1 和 2 时,语句 1 的循环条件不满足,进程 1 进入临界区,语句 2 的循环条件满足,进程 2 等待。看起来没什么问题,但若进程 1 连续两次申请访问临界区时,turn 值为 2,依然进不去临界区,因此不满足“空闲让进”原则。
- 饥饿分析:不会产生饥饿。
(2)双标志先检查法
// 全局变量
flag[0] = false;
flag[1] = false;
// P1 进程
while (flag[2]); // 1(检查)
flag[1] = TRUE; // 2(加锁)
critical section;
flag[1] = FALSE;
remainder section;
// P2 进程
while (flag[1]); // 3(检查)
flag[2] = TRUE; // 4(加锁)
critical section;
flag[2] = FALSE;
remainder section;
- 缺点分析:交替执行语句 1、3、2、4,执行语句 1 和 3 时,由于 flag[1] 和 flag[2] 都为 false,因此会跳出循环,这就意味着两个进程都进入了临界区,违反了“忙则等待”原则。
- 饥饿分析:不会产生饥饿现象。
(3)双标志后检查法
// 全局变量
flag[0] = false;
flag[1] = false;
// P1 进程
flag[1] = TRUE; // 1(加锁)
while (flag[2]); // 2(检查)
critical section;
flag[1] = FALSE;
remainder section;
// P2 进程
flag[2] = TRUE; // 3(加锁)
while (flag[1]); // 4(检查)
critical section;
flag[2] = FALSE;
remainder section;
- 缺点分析:交替执行语句 1、3、2、4,执行语句 1 和 3 后,flag[1] 和 flag[2] 都为 true。执行语句 2,由于 flag[1] 为 true,因此进程 1 会一直卡在 while 循环;执行语句 4,由于 flag[2] 也为 true,因此进程 2 也会一直卡在 while 循环。两个进程无限等待下去,但是临界区又处于空闲状态,因而违反了“空闲让进”和“有限等待”原则。
- 饥饿分析:违反了“有限等待”原则,可能产生饥饿现象。
(4)Peterson 算法
// 注意 turn 为全局变量
// P1 进程
flag[1] = TRUE; // 1
turn = 2; // 2
while (flag[2] && turn == 2) // 3
critical section,
flag[1] = false;
remainder section;
// P2 进程
flag[2] = TRUE; // 4
turn = 1; // 5
while (flag[1] && turn == 1); // 6
critical section;
flag[2] = false;
remainder section;
- 缺点分析:交替执行语句 1、4、2、5、3、6,执行语句 1、4、2、5 后,flag[1] = TRUE,turn = 1,flag[2] = TRUE。执行语句 3,循环条件不满足,进程 1 进入临界区。执行语句 4,循环条件满足,进程 2 将一直等待下去,这违反了“让权等待”。因而除了“让权等待”没有遵守外,其他三个原则都遵守了。
- 饥饿分析:不会产生饥饿现象。
4.3.2 硬件实现
方法 | 描述 | 优点 | 缺点 |
---|---|---|---|
中断屏蔽方法 | 利用“开/关中断指令”实现 | 简单高效 | 只适用于单处理机,只适用于操作系统内核进程 |
TestAndSet 指令(TSL 指令) | 使用变量 old 记录原来 lock 的值,再将 lock 设为 true,最后不断检查临界区是否已被其他进程上锁 | 实现简单,适用于多处理机 | 不满足让权等待 |
Swap 指令 | 逻辑上同 TSL 指令 | 实现简单,适用于多处理机 | 不满足让权等待 |
【注】后两者是用硬件实现的,它把“上锁”和“检查”操作用硬件的方式变成了一气呵成的原子操作,但仍未解决让权等待的问题。
4.4 信号量
P 操作负责分配资源,没有资源的时候就等着(进入阻塞队列)。V 操作负责释放资源,在阻塞队列不为空的时候唤醒某个进程进入临界区。
- P 操作:使信号量 - 1。若信号量 < 0,则执行 P 操作的进程被阻塞,否则进程继续执行。
- V 操作:使信号量 + 1。若信号量 ≤ 0,则被 P 操作阻塞的进程被解除阻塞。
4.4.1 实现进程同步
一般信号量:初值一般为可用物理资源的总数,用于进程同步问题。若期望的消息尚未产生,则初值应设为 0;若期望的消息已存在,则初值应设为一个非零正整数。
- 信号量 > 0:表示某类可用资源的数量。
- 信号量 = 0:表示某类资源已经没有。
- 信号量 < 0:表示某类资源已经没有,还有因请求该资源而被阻塞的进程。
- 信号量 ≤ 0 的绝对值:表示等待该资源的进程数。
实现功能:执行完 A 后才能执行 B。因此,执行 A 后执行 V 操作,再执行 P 操作,然后执行 B。
信号量 S=0;
P1(){
A;
V(S);
}
P2(){
P(S);
B;
}
4.4.2 实现进程互斥
二元信号量:取值仅为“0”或“1”,用作实现互斥。初值一定为“1”,此时 P、V 操作需夹紧临界区。
实现功能:两个进程对临界资源的互斥访问。
信号量 S=1;
P1(){
P(S);
P1的临界区;
V(S);
}
P2(){
P(S);
P2的临界区;
V(S);
}
4.4.3 实现前驱关系
例如:
- P1--(信号量 a)-->P2
- P1--(信号量 b)-->P3
- P2--(信号量 c)-->P3
分析:
- P1(V(a))--(信号量 a)-->(P(a))P2
- P1(V(b))--(信号量 b)-->(P(b))P3
- P2(V(c))--(信号量 c)-->(P(c))P3
代码:
信号量 a, b, c = 0;
P1(){
...;
V(a); V(b);
}
P2(){
P(a);
...;
V(c);
}
P3(){
P(b); P(c);
...;
}
4.5 管程
管程的组成(类似于面向对象的类):
- 局部于管程的共享数据结构说明;
- 对共享数据结构进行操作的一组过程;
- 对局部于管程的共享数据设置初始值的语句;
- 管程有一个名字。
管程的基本特征:
- 每次仅允许一个进程在管程内执行某个内部过程。
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据。
- 局部于管程的数据只能被局部于管程的过程所访问。
- 互斥特性由编译器实现。
信号量与条件变量:
阻塞原因定义为条件变量,每个条件变量保存一个等待队列。它们的区别:
项目 | 信号量 | 条件变量 |
---|---|---|
操作 | P、V 操作 | signal、wait 操作 |
功能 | 实现进程的阻塞、唤醒 | 实现进程的阻塞、唤醒 |
是否有值? | 有值,反映剩余资源数 | 没有值,实现排队等待的功能 |
5 死锁
5.1 死锁产生的原因
- 对不可剥夺资源的竞争,可能引起死锁。
- 请求和释放资源的顺序不当。
- 信号量使用不当。
【注】死锁和饥饿的区别:
项目 | 死锁 | 饥饿 |
---|---|---|
产生原因 | 循环等待对方手里的资源 | 长期得不到需要的 I/O 设备,或长期得不到处理机 |
进程状态 | 阻塞态 | 阻塞态或就绪态 |
进程数量 | 两个或以上 | 一个 |
5.2 死锁的四个必要条件
- 互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁。
- 不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
- 请求并保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
- 循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
【注】发生死锁时一定有循环等待,但是发生循环等待时未必死锁。
由以上四个必要条件,可得以下常用结论:
- 若系统有 x 个进程,每个进程需要 y 个资源,则该系统必然不会发生死锁的最少资源是 x*(y-1) + 1 个。
- 若系统有 x 个资源,每个进程需要 y 个资源,则该系统可能发生死锁的最小进程数为 x / (y-1)。
【推导思路】系统有 x 个进程,每个进程已拥有 y-1 个资源,此时如果资源总数刚好为 x*(y-1) 个,意味着资源已用完,则一定发生死锁,此时再多加一个资源,令其中一个进程获得所需的最大资源,则死锁解除。即使每个进程所需资源数各不相同,一样可以按此思路分析。
5.3 死锁的处理策略
5.3.1 死锁预防(不允许死锁发生-静态策略)
项目 | 方法 | 缺点 |
---|---|---|
破坏互斥条件 | 把互斥资源改造为共享资源(如 SPOOLing 技术) | 可行性不高 |
破坏不剥夺条件 | (1)申请的资源得不到满足时,立即释放拥有的所有资源;(2)申请的资源被其他进程占用时,由操作系统协助剥夺 | 可能导致进程部分工作失效,系统开销大,可能引起饥饿 |
破坏请求并保持条件 | 运行前分配好所有需要的资源,之后一直保持 | 资源利用率高,可能引起饥饿 |
破坏循环等待条件 | 给资源编号,必须按编号递增的顺序请求资源 | 用户编程麻烦,不方便新增新设备,资源浪费 |
5.3.2 死锁避免(不允许死锁发生-动态策略)
安全性算法和银行家算法:
【例】已知资源可用数为 S(x,y,z),某时刻状态如下:
进程 | 最大需求 | 已拥有 | 最多还要 |
---|---|---|---|
A | (0,0,4) | (0,0,3) | (0,0,1) |
B | (1,7,5) | (1,0,0) | (0,7,5) |
C | (2,3,5) | (1,3,5) | (1,0,0) |
D | (0,6,4) | (0,0,2) | (0,6,2) |
E | (0,6,5) | (0,0,1) | (0,6,4) |
则 x、y、z 取以下值时系统处于安全状态?
(1)1,4,0;(2)0,6,2;(3)1,1,1;(4)0,4,7。
(1)【解】S(1,4,0) > A(0,0,1),S(1,4,0) > C(1,0,0),说明可以分配给 A 和 C。回收 A、C 资源后 S(1+0+1, 4+0+3, 0+3+5) = S(2,7,8)。
考虑 B、D、E 进程,S(2,7,8) > B(0,7,5),S(2,7,8) > D(0,6,2),S(2,7,8) > E(0,6,4),说明可以分配给 B、D 和 E。回收 B、D、E 资源后 S(2+1+0+0, 7+0+0, 8+0+2+1) = S(3,7,11)。
安全序列可以为 A、C、B、D、E,也可以为 C、A、D、B、E 等,因此处于安全状态。
(2)【解】S(0,6,2) > A(0,0,1),S(0,6,2) = D(0,6,2),说明可以分配给 A 和 D。回收 A、D 资源后 S(0+0+0, 6+0+0, 2+3+2) = S(0,6,7)。
考虑 B、D、E 进程,S(0,6,7) > D(0,6,2),说明可以分配给 D。回收 D 资源后 S(0+0, 6+0, 7+2) = S(0,6,9)。
考虑 B、E 进程,S(0,6,9) < B(0,7,5),S(0,6,9) < E(0,6,4),说明不可以分配给 B、E,因此系统处于不安全状态。
(3)【解】S(1,1,1) > A(0,0,1),S(1,1,1) > C(1,0,0),说明可以分配给 A 和 C。回收 A、C 资源后 S(1+0+1, 1+0+3, 1+3+5) = S(2,4,9)。
考虑 B、D、E 进程,S(2,4,9) < B(0,7,5),S(2,4,9) < D(0,6,2),S(2,4,9) < E(0,6,4),说明不可以分配给 B、D 和 E。因此处于不安全状态。
(4)【解】S(0,4,7) > A(0,0,1),说明可以分配给 A。回收 A 资源后 S(0+0, 4+0, 7+3) = S(0,4,10)。
考虑 B、C、D、E 进程,S(0,4,10) < B(0,7,5),S(0,4,10) < C(1,0,0),S(0,4,10) < D(0,6,2),S(0,4,10) < E(0,6,4),说明不可以分配给 B、C、D、E,因此处于不安全状态。
【注】如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)。
5.3.3 死锁检测和解除(允许死锁发生)
1. 死锁检测
(1)资源分配图的组成:
项目 | 解释 |
---|---|
进程结点 | 对应一个进程 |
资源结点 | 对应一类资源,一类资源可能有多个资源 |
进程结点到资源结点的边 | 进程申请一个资源(每条边代表一个) |
资源结点到进程结点的边 | 已经为进程分配了一个资源(每条边代表一个) |
(2)使用死锁检测算法简化资源分配图:
- 找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于系统中已有空闲资源数量。
- 消去它所有的请求边和分配边,使之称为孤立的结点。
- 继续对剩余点进行一系列简化,若能消去图中所有的边,则称该图是可完全简化的。
(3)死锁定理:如果某时刻系统的资源分配图是不可完全简化的,那么此时系统死锁。注意,并不是系统中所有的进程都是死锁状态,用死锁检测算法化简资源分配图后,还连着边的那些进程就是死锁进程。
2. 死锁解除
- 方法:资源剥夺法;撤销进程法;进程回退法。
- 如何决定“对谁动手”:进程优先级;已执行多长时间;还要多久能完成;进程已经使用了多少资源;进程是交互式的还是批处理式的。