第二章 进程与线程
2.1 进程与线程
2.1.1 进程的概念和特征
程序: 静态的,就是存放在磁盘里的可执行文件,就是一系列得指令集合
进程: 动态的,是程序得一次执行过程
PID: 当进程被创建时,操作系统会为该进程分配一个唯一的,不重复得“身份证号”----PID
PCB: 操作系统需要对各个并发运行得进程进行管理,但凡管理时所需要得信息,都会放在PCB中,PCB是一个专门得数据结构,称为进程控制块;
PCB是进程存在得唯一标志,当进程被创建时,操作系统为其创建PCB,当操作系统结束时,会回收其PCB。
进程实体的组成:
1.PCB——操作系统用的
2.程序段——进程自己用的
3.数据段——进程自己用的
引入进程实体得概念后,进程的定义为:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
调度: 一个进程被调度,就是指操作系统决定让这个进程上CPU运行。
进程的特征:
- 动态性:进程是程序的一次执行过程
- 并发性:内存中有多个进程实体,各进程可并发执行
- 独立性:进程是能独立运行,独立获得资源,独立接受调度的基本单位
- 异步性:各进程按各自独立的,不可预知的速度向前推进
- 结构性:进程由程序段,数据段,PCB组成
2.1.2 进程的状态与转换
创建态: 进程正在被创建,申请一段内存存储PCB。
就绪态: 进程创建完成,已经具备处理的条件,只需在处理机上处理。
运行态: 进程正在处理机上运行
阻塞态: 进程正在等待某一事件而暂停运行
终止态: 进程执行exit系统调用,请求操作系统终止该进程
运行态—阻塞态:是一种进程自身做出的主动行为
阻塞态—就绪态:一种被动行为
运行态—就绪态:时间片到,或处理及被占
注意:
- 不能由阻塞态直接转换为运行态,也不能由就绪态直接转换为阻塞态
- 单CPU下,同一时刻只会有一个进程处于运行态,多核CPU下,有多个进程处于运行态
- 进程PCB中,会有一个变量state表示进程当前的状态
2.1.3 进程的组织
链式方式: 多种链式指针指向不同状态的进程‘
1.执行指针:指向当前处于运行态的进程
2.就绪队列指针:指向当前处于就绪态的进程
3.阻塞队列指针:指向处于阻塞态的进程(可细分为不同原因的队列)
索引方式: 根据进程状态不同,建立几张索引表;操作系统持有指向各个索引表的指针
2.1.4 进程控制
进程控制: 是对系统中的所有进程实施有效的管理,它具有创建新进程,撤销已有进程,实现进程状态转换等功能。
原语: 进程控制用的程序段称为原语;原语是一种特殊的程序,它的执行具有原子性,该段程序的运行必须一气呵成,不可中断:进程状态的转换分为两步:将state设为1;放入其他队列。若中断,则有可能导致操作系统中的某些关键数据结构信息不统一的情况,会影响操作系统进行别的管理工作。
原子性执行方法: 用”关中断指令“和”开中断指令“两个特权指令实现;CPU执行了关中断指令后,就不再例行检查中断信号,直到执行开中断指令之后才会恢复检查。
1.进程的创建:
- 申请空白PCB。
- 为新进程分配所需资源。
- 初始化PCB。
- 将PCB插入就绪队列【创建态变为就绪态】
引起进程创建的事件:用户登录。作业调度。提供服务。应用请求。
2.进程的终止:
- 从PCB集合中找到终止进程的PCB
- 若进程正在运行,立即剥夺CPU,将CPU分配给其他进程
- 终止其所有子进程(进程之间关系是树形结构)
- 将该进程拥有的所有资源归还给父进程或操作系统
- 删除PCB
引起进程终止的事件:
正常结束——进程自己请求终止(exit系统调用)
异常结束——整数除以0,非法使用特权指令
外界干预——Ctrl+Alt+delete,用户选择杀掉进程
3.进程的堵塞和唤醒:
阻塞:(运行-阻塞)
- 找到要阻塞的进程对应的PCB
- 保护进程运行现场,将PCB状态信息设置为”阻塞态“,暂时停止进程运行
- 将PCB插入相应事件的等待队列
引起进程阻塞的事件:需要等待系统分配某种资源;需要等待相互合作的其他进程完成工作
唤醒:(阻塞-就绪)
- 在事件等待队列中找到PCB
- 将PCB从等待队列移除,设置进程为就绪态
- 将PCB插入就绪队列,等待被调度
引起进程唤醒的事件:等待事件发生(因何事阻塞,就应由何事唤醒)
4.进程的切换:
- 将运行环境信息存入PCB
- PCB移入相应队列
- 选择另一个继承执行,并更新其PCB
- 根据PCB恢复新进程所需的运行环境
2.1.5 进程的通信
进程通信: 两个进程之间产生数据交互
进程是分配系统资源的单位,因此各进程拥有的内存地址空间相互独立。
为了保证安全,一个进程不能直接访问另一个进程的地址空间,需借助操作系统内核的地址空间。
1.共享存储:在通信的进程之间存在一块可直接访问的共享空间。
- 为避免出错,各个进程对共享空间的访问应该是互斥的
- 各个进程可使用操作系统内核提供的同步互斥工具(P,V操作)
基于存储区的共享:操作系统在内存中划出一块共享存储区,数据的形式,存放位置都由通信进程控制,而不是操作系统。这个共享方式速度很快,是一种高级通信方式
基于数据结构的共享: 比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢,限制多,是一种低级通信方式。
2.消息传递: 进程间的数据交换以格式化的消息为单位。进程通过操作系统提供的”发送消息/接收消息“两个原语进行数据交换。
格式化的消息: 消息头:发送进程ID。接受进程ID。消息长度等格式化消息;消息体。
两种方式:
1).直接通信方式:消息发送进程要指明接收进程的ID。
——将一个进程发送的原语挂在接收进程的消息缓冲队列上;接受进程从消息队列中找到发送原语,然后复制到自己进程地址空间中
2).间接通信方式:通过”信箱“间接地通信。
——在内存中申请若干邮箱,进程P发送原语,往信箱A发送消息。进程Q接受原语,从信箱A中接受消息,复制到自己的进程地址空间中。
3.管道通信: 用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件(pipe文件)【单向,先进先出】。
(与共享存储区别:类似于普通链表与队列的区别)
- 管道只能采用半双工通信,某一时间段只能实现单向的传输,如果要实现双向同时通信,则需要设置两个管道
- 各进程要互斥地访问管道
- 当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。
- 当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
- 管道中的数据一旦被读出,就彻底消失,因此。当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:1.一个管道允许多个写进程,一个读进程。2.允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据。
2.1.6 线程和多线程模型
传统的进程是程序执行流的最小单位。
引入线程后,线程成为了程序执行流的最小单位。
线程: 是一个基本的CPU执行单元,也是程序执行流的最小单位。
引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务;进程只作为除CPU之外的系统资源的分配单元。
引入线程机制后,有什么变化:
1.资源分配,调度:传统进程机制中,进程是资源分配,调度的基本单位;引入线程后,进程是资源分配的基本单位,线程是调度的基本单位。
2.并发性:传统进程机制中,只能进程间并发;引入线程后,各线程间也能并发,提升了并发度。
3.系统开销:传统的进程间并发,需要切换进程的运行环境,系统开销很大;线程间并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小;引入线程后,并发所带来的系统开销减少。
线程的属性:
- 线程是处理机调度的单位。
- 多CPU计算机中,各个线程可占用不同的CPU
- 每个线程都有一个线程ID,线程控制块(TCB)
- 线程也有就绪,阻塞,运行三种基本状态
- 线程几乎不拥有系统资源
- 同一进程的不同线程间共享进程资源
- 由于共享内存地址空间,同一进程中的线程间通信甚至无需系统干预
- 同一进程中的线程切换,不会引起进程切换
- 不同进程中的线程切换,会引起进程切换
- 切换同进程内的线程,系统开销很小
- 切换进程,系统开销较大
线程的实现方式:
1.用户级线程:(此时最小单位仍是进程,不是线程)
线程的管理工作由谁完成?——应用程序通过线程库完成的
线程切换是否需要CPU变态——不需要
操作系统是都能意识到用户级线程的存在?——不能意识,只有用户才能感受到用户级线程的存在
优点: 用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。
缺点: 当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并发运行。
2.内核级线程:(最小单位是线程)
线程的管理工作由谁完成?——是由操作系统完成的
线程切换是否需要CPU变态——需要
操作系统是都能意识到用户级线程的存在?——可以
优点: 当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点: 一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
多线程模型:
1.一对一模型: 一个用户级线程映射到一个内核级线程,每个用户进程有与用户级线程同数量的内核级线程
优点: 当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点: 一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
2.多对一模型: 多个用户级线程映射到一个内核级线程。且一个进程只被分配一个内核级线程
优点: 用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。
缺点: 当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并发运行。
操作系统只看得见内核级线程,因此只有内核级线程才是处理机分配的单位
3.多对多模型: n个用户级线程映射到m个内核级线程(克服了多对一模型并发度不高的缺点,游又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点)n>=m;
优点: 集二者之长。
线程的状态与转换:
执行状态,就绪状态,阻塞状态;转换与进程转换一致。
线程的组织与控制:
进程——PCB——PID;
线程——TCB——TID;
- 线程标识符:TID
- 程序计数器PC:线程目前执行到哪里
- 其他寄存器:线程运行的中间结果
- 堆栈指针:堆栈保存函数调用信息,局部变量等
- 线程运行状态:运行/就绪/阻塞
- 优先级:线程调度,资源分配的参考
线程切换时要保存/恢复——PC,其他寄存器,堆栈指针。
线程表: 可将多个TCB组织成一张线程表。
2.2 处理机调度
2.2.1 调度的概念
一堆任务要处理,但资源有限,没办法同时处理,就需要确定某种规则来决定处理这些任务的顺序,这就是调度研究的问题。
1.高级调度(作业调度):按一定原则从外存的作业后备队列种挑选一个作业调入内存,并创建进程。每个作业只调入一次,调出一次。作业调入时会建立PCB,调出时才撤销PCB。
2.低级调度(进程/处理机调度):按照某种策略从就绪队列中选取一个进程,将处理机分配给它.
进程调度是操作系统中最基本的一种调度,频率很高。
内存不够时,可将某些进程的数据调出外存,等内存空闲时重新调入内存。
暂时调到外存等待的进程状态为挂起状态。被挂起的进程PCB会被组织成挂起队列
3.中级调度(内存调度):按照某种策略决定将哪个处于挂起状态的进程重新调入内存
2.2.2 调度的目标
1.CPU利用率: CPU忙碌时间占总时间的比例
2.系统吞吐量: 单位时间内完成作业的数量
3.周转时间: 是指从作业被提交给系统开始,到作业完成为止的这段时间间隔
包括三部分:作业在外存后备队列上等待作业调度的时间,进程在就绪队列上等待进程调度的时间,进程在CPU上执行的时间,进程等待I/O操作完成的时间。
带权周转时间=作业周转时间/实际作业运行时间
对于周转时间相同的两个作业,实际运行时间长的作业在相同时间内被服务的时间更多,带权周转时间更小,用户满意度更高。
对于实际运行时间相同的两个作业,周转时间短的带权周转时间更小,用户满意度更高。
4.等待时间: 进程处于等待处理机状态时间之和,等待时间越长,用户满意度越低。
作业在后备队列等待被服务;作业调入内存后,建立对应的进程,这个进程会被CPU服务,也有等待被服务的时间。
对于进程来说,等待时间就是指进程建立后等待被服务的时间之和,在等待I/O完成的期间其实进程也是在被服务的,所以不计入等待时间。
对于作业来说,不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待的时间。
5.响应时间: 是指从用户提交请求到首次产生相应所用的时间。
2.2.3 调度的实现
进程在操作系统内核程序临界区中不能进行调度与切换。
临界资源: 一个时间段内只允许一个进程使用的资源。各进程需要互斥地访问临界资源。
临界区: 访问临界资源的那段代码
内核程序临界区一般是用来访问某种内核数据结构的,比如就绪队列。
进程调度的方式:
1.非剥夺调度方式/非抢占方式: 只允许进程主动放弃处理机,在运行过程中即便又更紧迫的任务,当前进程依然会继续使用处理机,直到该进程终止或主要要求进入阻塞态。
【实现简单,系统开销小但无法及时处理紧急任务,适合早期批处理系统】
2.剥夺调度方式/抢占方式: 当一个进程正在处理机上执行,如果有一个更重要的进程需要使用处理机。则立即暂停正在执行的进程,将处理机分配给更紧迫的进程。
【具有优先性,可实现让各进程按时间片轮流执行的功能,适合分时操作系统,实时操作系统】
进程的切换与过程:
狭义的进程调度: 是指从就绪队列中选中一个要运行的进程(可以是刚刚被暂停的进程)。
进程切换: 指一个进程让出处理机,另一个进程占用处理机(必须是另一个进程)
广义的进程调度: 包含了选择一个进程和进程切换两个步骤
进程切换过程:对原来运行进程各种数据的保存;对新的进程各种数据的恢复。
注意: 进程切换是有代价的,因此如果过于频繁进行进程调度,切换,必然会使整个系统的效率降低,使系统大部分时间都花在了进程切换上,真正用于执行过程时间减少。
调度程序/调度器: 被调度程序选中上CPU运行;进程时间用完,下CPU;
什么事件会触发“调度程序”: 创建新进程,进程退出,运行进程阻塞,I/O中断发生。
非抢占式调度策略,只有运行进程阻塞或退出才能触发调度程序工作;
抢占式调度策略,每个时钟中断或k个时钟中断会触发调度程序工作;
不支持内核级线程的操作系统,调度程序的处理对象是进程;
支持内核级线程的操作系统,调度程序的处理对象是内核线程;
闲逛进程: 没有其他就绪进程时,运行闲逛进程;
特性:优先级最低;可以是0地址指令,占一个完整的指令周期;能耗低。
2.2.4 调度算法
1.先来先服务:
2.短作业优先:
FCFS算法:选择一个等待时间最长的作业,不考虑作业的运行时间,因此导致了对短作业的不友好。
SJF算法:选择一个执行时间最短的作业,不考虑作业的等待时间,导致了对长作业不友好的问题,甚至造成饥饿。
3.高响应比优先算法: 非抢占式的调度算法,只有当前运行的进程主动放弃CPU时,才需要进行调度,调度时计算所有就绪进程的响应比,选响应比最高的进程上CPU。
4.时间片轮转: (仅用于进程调度)
如果时间片太大,使得每个进程都可以在一个时间片内就可完成,则时间片轮转调度算法退化为先来先服务调度算法,并且会增大进程响应时间,因此时间片不能太大;另一方面,进程调度,切换是有时间代价的,因此如果时间片太小,会导致进程切换过于频繁,系统会花大量时间来处理进程切换,从而导致实际用于进程执行的时间比例减少,所以时间片也不能太小。
5.优先级调度算法:
- 就绪队列未必只有一个,可以按照不通过优先级来组织。另外,也可以把优先级高的进程排在更靠近对头的位置。
- 根据优先级是否可以动态改变,可将优先级分为静态优先级和动态优先级两种;
- 静态优先级: 创建进程时确定,之后一直不变。
- 动态优先级: 创建进程时有一个初始值,之后会根据情况动态地调整优先级。
- 系统进程优先级>用户进程
- 前台进程优先级>后台进程
- 操作系统更偏好I/O型进程(相对的是计算型进程/CPU)
至此:
- FCFS算法的优点是公平
- SJF算法的优点是能尽快处理完短作业
- 时间片轮转可以让各个进程得到及时响应
- 优先级调度算法可以灵活地调整各种进程被服务的机会
6.多级反馈队列
2.3 同步与互斥
2.3.1 基本概念
同步: 直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。进程间的直接制约关系就是源于它们之间的相互合作
临界资源: 一个时间段内只允许一个进程使用的资源称为临界资源
互斥: 对临界资源的访问,必须互斥地进行
对临界资源的互斥访问,逻辑上分为四个部分:
1.进入区:负责检查是否可进入临界区,若可进入,则应设置正在访问临界资源的标志,以阻止其他进程同时进入临界区
2.临界区:访问临界资源的那段代码
3.退出区:负责解除正在访问临界资源的标志。
4.剩余区:做其他处理
实现互斥访问,保证整体性能:
1.空闲让进:临界区空闲时,可进。
2.忙则等待:已有进程,必须等待
3.有限等待:对请求访问的进程,应保证在有限时间内进入临界区
4.让权等待:进程不能进入临界区,应立即释放处理机,防止进程忙等待。
2.3.2进程互斥实现方法
一.软件实现方法:
1.单标志法: 两个进程在访问完临界区后会把使用临界区的权限转交给另一进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予。
可以实现——同一时刻最多只允许一个进程访问临界区。
缺点:违反了空闲让进原则
2.双标志先检查法: 设置一个布尔型数组,数组中各个元素用来标记各进程想进入临界区的意愿。每个进程在进入临界区之前先检查当前有没有别的进程想进入临界区,如果没有,则把自己对应的标志设为true,之后开始访问临界区。
缺点:违反了忙则等待原则(并发进程)
3.双标志后检查法: 双标志先检查法的改版,前一个是先检查后上锁,该算法是先上锁后检查,避免两个进程同时进入临界区。
缺点:违背了空闲让进和有限等待原则,有可能产生饥饿
4.Peterson: 结合双标志法和单标志法,若双方都想进入临界区,进程可表示谦让,让另一个先运行
缺点:未遵循让权等待原则
二.硬件实现方法
1.中断屏蔽方法: 利用“开/关中断指令”实现(类似于原语)
优点:简单,高效
缺点:不适用于多处理机;只适用于操作系统内核进程,不适用于用户进程。
2.TestAndSet指令: TS指令/TSL指令;用硬件实现的,执行的过程不允许被中断,只能一气呵成。
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。
3.Swap指令: Exchange/XCHG指令;用硬件实现的,执行过程不允许被中断,一气呵成。
优点: 实现简单,无需像软件实现那样严格检查是否会有逻辑漏洞;适合于多处理机环境
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。
2.3.3 互斥锁
解决临界区最简单的工具就是互斥锁;一个进程在进入临界区时获得锁,退出临界区时释放锁。函数acquire()获得锁,而函数release()释放锁。
每个锁又一个布尔变量available,表示锁是否可用。如果锁是可用的,调用acquire()会成功,且锁不再可用。当一个进程企图获得不可用的锁时,会被阻塞,直到锁被释放。
acquire(){
while(!available)
; //忙等待
available = false; //获得锁
}
release(){
available = true; //释放锁
}
acquire()和release()的执行必须是原子操作,因此互斥锁通常采用硬件机制来实现。
互斥锁的主要缺点是忙等待,当有一个进程在临界区中,任何其他进程在进入临界区时必须连续循环调用acquire()。当多个进程共享同一个CPU时,就浪费了CPU周期。因此,互斥锁通常用于多处理器系统,一个线程可以在以一个处理器上等待,不影响其他线程的执行。
自旋锁: 需要连续循环忙等的互斥锁。(TSL,SWAP,单标志法)
2.3.4 信号量
用户进程可以通过使用操作系统提供的一对原语来对信号量进程操作,从而很方便的实现了进程互斥,进程同步
信号量其实就是一个变量,可以用一个信号量来表示系统中某种资源的数量。
一.整型信号量
用一个整数型的变量作为信号量:只有三种操作——初始化,P操作,V操作。
缺点: 不满足“让权等待”原则,会发生“忙等”。
二.记录型信号量
用记录型数据结构表示的信号量
S.value =0——资源恰好分配完
S.value =-1——有一个进程在等待
信号量机制实现进程互斥:
1.分析并发进程的关键活动,划定临界区
2.设置互斥信号量mutex,初值为1
3.在进入区P——申请资源
4.在退出区V——释放资源
注意:
- 对不同的临界资源需要设置不同的互斥信号量。
- P,V操作必须成对出现;缺少P就不能保证临界资源的互斥访问,缺少V就会导致资源永不被释放,等待进程用不被唤醒。
信号量机制实现进程同步:
1.分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作
2.设置同步信号量S,初始为0
信号量机制实现前驱关系:
生产者-消费者问题:
多生产者,多消费者问题:
结论:即使不设置专门的互斥变量mutex,也不会出现多个进程同时访问盘子的现象;
因为:缓冲区的大小为1,同步信号量最多只有一个是1,因此任何时刻,最多只有一个进程的p操作不会被阻塞,并顺利地进入临界区。
若缓冲区大小为2,则必须设置互斥信号量mutex;
吸烟者问题:
读者-写者问题:
结论:在这种算法中,连续进入的多个读者可以同时读文件;写者和其他进程不能同时访问文件;写着不会饥饿,但也并不是真正的“写优先”,而是相对公平的先来先服务原则(读写公平法)
哲学家进餐问题:
如果五个哲学家并发地拿起了自己左手边的筷子——每位哲学家循环等待右边的人放下筷子(阻塞),发生“死锁”。
3.仅当一个哲学家左右两只筷子都可用时才允许他抓起筷子
2.3.5 管程
管程: 一种特殊的软件模块,解决进程同步与互斥问题,有这些部分构成:
- 局部于管程的共享数据结构说明
- 对该数据结构进行操作的一组过程
- 对局部于管程的共享数据设置初始值的语句
- 管程有一个名字
管程的基本特征:
- 局部于管程的数据只能被局部于管程的过程所访问
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据
- 每次仅允许一个进程在管程内执行某个内部过程。
2.4 死锁
2.4.1 基本概念
死锁: 并发环境下,各进程因竟争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的现象
什么时候会发生死锁?
- 对系统资源的竞争
- 进程推进顺序非法,请求和释放资源的顺序不当,也同样会导致死锁
- 信号量的使用不当也会造成死锁
总之——对不可剥夺资源的不合理分配,可能造成死锁。
死锁的处理策略?
- 预防死锁
- 避免死锁
- 死锁的检测和解除
2.4.2 死锁预防
1.破坏互斥条件:
互斥条件: 只有对必须互斥使用的资源的争抢才会导致死锁
——SPOOLing技术
2.破坏不剥夺条件
不剥夺条件: 进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
方案一:当某个进程请求新的资源得不到满足时,它必须立即释放保持的所有资源,待以后需要时再重新申请。
方案二:当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。(需考虑进程优先级)
缺点:
- 实现其他比较复杂
- 释放已获得的资源可能会造成前一段工作的失效。
- 反复地申请和释放资源会增加系统开销,降低系统吞吐量。
- 若采用方案一,如果一致发生这种情况,会导致进程饥饿
3.破坏请求和保持条件
请求和保持条件: 进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已占有的资源保持不放。
采用静态分配方法: 进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不让它投入运行,一旦投入运行之后,这些资源就一致归他所有,该进程就不会再请求别的任何资源
缺点: 有些资源需要用很短的时间,如果一致占有该资源,会导致严重的资源浪费,资源利用率极低,也可能导致某些进程饥饿。
4.破坏循环等待条件:
循环等待条件: 存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
采用循序资源分配法: 首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源一次申请完。
原理分析: 一个进程只有占有小编号的资源时,才有资格申请更大编号的资源。已持有大编号的资源进程不可能逆向地回来申请小编号的资源,从而不会产生循环等待的现象。
缺点:
- 不方便增加新的设备,可能需要重新编号。
- 进程实际使用资源的顺序可能和编号递增顺序不一致,会导致资源浪费
- 必须按照规定次序申请资源,用户编程麻烦
2.4.3 死锁避免
安全序列: 指如果系统按照这种序列分配资源,则每个进程都能顺利完成,只要能找出一个安全序列,系统就是安全状态。安全序列可能有多个
如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。就意味着之后可能所有进程都无法顺利的执行下去。如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,但我们要考虑最坏情况。
如果系统处于安全状态,就一定不会发生死锁,如果系统进入不安全状态,就可能发生死锁。
银行家算法:
核心思想: 在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。
安全性算法: 根据资源总数,进程最大需求,已分配资源数,最多还需要数,以及剩余可用资源。(多种资源用向量表示资源数)。循环判断检查每个进程是否可以加入安全序列,加入安全序列的进程会归还资源数,最终得到一个安全序列
2.4.4 死锁检测和解除
死锁的检测: