文章目录
- 第二章 进程管理
- 01-进程的概念、组成、特征
- 02-进程的状态与转换
- 03-进程控制
- 04-进程通信
- 05-线程多线程模型
- 06-处理机调度的概念、层次
- 07-进程调度的时机、切换与过程、进程调度算法
- 08-调度算法评价指标
- 09-调度算法
- 10- 其他调度算法
- 11-进程同步和进程互斥
- 12-进程互斥的软件实现方法
- 13-进程互斥的硬件实现方法
- 14-信号量机制
- 15-信号量机制实现进程之间互斥、同步、前驱关系
- 16-生产者消费者问题
- 17-多生产者多消费者的问题
- 18-吸烟者问题
- 19-读者_写者问题
- 20 -哲学家进餐问题
- 21-管程
- 22-死锁概念
- 23-预防死锁
- 24-避免死锁(**银行家算法*
- 25-死锁的检测和解除
第二章 进程管理
01-进程的概念、组成、特征
概念
程序:静态的就是指令集和放在那,多次执行就是多个进程
进程:是动态的,是程序的一次执行过程
多个程序运行的时候PCB不同,
组成1PCB(给操作系统使用的)
每新建一个进程就会分配一个唯一PID
所属的用户ID(UID)、分配资源,各个资源的使用情况。
这些信息都被保存在一个数据结构PCB中,叫进程控制块(Process Control Block)
操作系统需要对各个并发运行的进程进行管理,管理过程中需要的信息都在PCB里面
比如上面说的还有处理机相关的PSW(切换内核用户态的值)(用来实现进程切换
)等值
LINUX的PCB是一个struct task_struct结构体(1900多行)
组成2 程序段(给程序自己使用的)
指令序列:程序的代码
组成3 数据段(给程序自己使用的)
运行过程中产生的各种数据
特征
- 动态性 进程时程序的一次执行过程,动态的产生变化消亡
- 并发性 可以多个进程并发运行
- 独立性 进程能独立运行,独立获得资源,端丽接收调度
- 异步性 各个进程按照不可预知的速度向前推进,操作系统要提供‘进程同步机制’来解决异步问题‘
- 结构性 上述的三部分
02-进程的状态与转换
2.1-进程的五状态丁字裤模型
PCB中的state变量
-
创建态
进程被创建期间,操作系统会分配资源、完成对PCB输出化
-
就绪态
创建完成之后,没有CPU使用权
接下来操作系统选择一个让他上CPU运行
-
运行态
CPU执行指令表序列
进程运行时可能会等待某个事件的发生操作系统就会让他下CPU,并且让它进入——————
-
阻塞态
当导致阻塞的等待事件发生的时候进程又可以回到CPU运行
-
终止态
执行exit系统调用,请求操作系统终止该进程,进程会进入终止态,操作系统让进程下CPU,回收资源,回收进程的PCB
进程不能从阻塞态转换为运行态,也不能从就绪态转换到阻塞态
运行态到阻塞态是主动的
阻塞态转换到就绪态是被动的(等资源)
运行态可以转到就绪态->时间片到时了,或处理被抢占
丁字裤模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHBy0Oi9-1624671582502)(操作系统.assets/image-20210621165502069-1624265704652.png)]
2.2-进程的组织
PCB中有state变量,操作系统通过这些参数把进程组织起来
-
链式方式
操作系统管理一系列的队列,每个队列都有指向相应状态的进程的PCB
通常把优先级高的进程放在队头
很多操作系统因为阻塞原因把阻塞状态的PCB的指针分为不同的队列
-
索引方式(用的很少)
给各个状态的进程建立索引表,表项指向相应PCB
03-进程控制
创建、撤销、实现进程状态转换
进程控制操作系统需要做什么(使用原语)?
使用原语
因为原语不能被打断,进程的状态需要一气呵成
①pcb中state变量设置为1
②把pcb从阻塞队列放到就绪队列
这两件事必须一起做,不然PCB表示的状态和所处的队列不一致,就会
通过关中断和开中断实现原语的原子性
-
创建原语
-
申请空白PCB
-
为新进程分配所需资源
-
初始化PCB
-
将PCB插入就绪队列
-
-
引起进程创建事件
- 用户登录 分时系统,用户登录,给一个进程
- 作业调度
- 提供服务
- 应用请求 应用主动请求创建紫禁城
-
撤消原语
- 从PCB集合找到PCB
- 剥夺CPU
- 终止其所有子进程
- 将该进程拥有的所有资源归还给父进程或操作系统
- 删除PCB
-
引起进程终止的事件
- 正常结束
- 异常结束
- 外界干预 用户杀掉
父子进程优点
进程间的关系是树形结构,父进程把自己的资源分配给子进程
- 阻塞原语
- 引起阻塞的事件
- 唤醒原语
- 引起唤醒的事件
切换原语
还需要保存运行环境
保存运行环境:寄存器那些信息也要保存下来
引起进程切换的事件
所有控制原语做的事
-
更新PCB信息
修改进程状态,载入 / 恢复运行环境
-
将PCB插入合适的队列
-
分配/回收资源
04-进程通信
各进程拥有的内存地址空间相互独立
一个进程不能随意访问其他进程的地址空间
操作系统提供以下三种方式实现进程通信
共享空间的访问时互斥的
共享存储
-
基于数据结构的共享
只能存放一种固定数据结构,限制多,速度慢,低级的通信方式
-
基于储存区的共享
划分共享空间,数据形式存放位置都由进程控制而不是操作系统,相对速度更快限制少,高级的通信方式
管道通信
开辟的一个和内存页面大小相同的缓冲区,
-
管道只能采取半双工通信:(某一时间段只能实现单向传输)————要想实现双向通信需要设置两个管道
-
各进程互斥的访问管道
-
数据以字符流形式写入管道,管道写满之后某些进程write()系统调用被阻塞,唤醒读进程的read();
等待读进程将数据取走之后读进程的read()阻塞
-
没写满不让读,没读空不让写
-
数据一旦读出就被抛弃,**意味着读进程最多只能有一个,**否则就会出现读错数据的情况。。。。。
消息传递
格式化消息为单位Message为单位,进程通过操作系统提供的**“发送消息/接收消息”**两个原语进行数据交换
Message有消息头和消息体
消息头有发送进程id,接收进程id,消息类型,消息长度(类似报文)
-
直接通信方式
直接挂到接收进程的消息缓冲队列上,
-
间接通信方式
发送到中间实体信箱中(发送方和接收方信息挂在消息头里)
05-线程多线程模型
线程
(基本的CPU执行单元)程序执行流的最小单位————轻量级进程
qq要用各种功能
5.1 引入线程后
进程只作为除了CPU以外的系统资源的份分配单元
-
线程成为调度的基本单位
-
并发性提高了
-
切换不同线程时不用切换进程环境,并发开销小
5.2 其他线程的属性
- 多核CPU线程可以调用不同的CPU
- 每个线程有一个线程ID,线程控制块(TCB)
- 线程也有就绪、阻塞、运行三种基本状态
- 线程几乎不拥有系统资源,资源是用进程的,统一进程的不同线程共享这个进程的资源
- 同一进程中的线程间通信不需要系统干预
- 同一进程内的的线程切换系统开销小
5.3 线程三种实现的方式
5.2.1 用户级线程
(早期的操作系统)通过代码实现的简单并发
优点:不用切换内核态和用户态
缺点:一个线程被阻塞其他的就得等着,多个线程不能分开在多个CPU上运行
5.2.2 内核级线程
操作系传统也能看得到的线程
优点:线程阻塞了不影响别人,多个线程可以分开在多核CPU上运行
缺点:要频繁切换用户态和内核态,线程管理开销大
5.2.3 多线程模型
把若干个用户级线程映射到内核级线程
-
一对一模型
相当于纯内核级线程
-
多对一模型
相当于纯粹用户级线程
-
多对多模型
用户线程n >= 内核级线程
用户线程就是【代码逻辑】载体
内核级线程就是【运行机会】载体
06-处理机调度的概念、层次
6.1 基本概念
当有一堆任务要处理,资源有限,要制定的规则决定顺序
6.2 调度的三个层次
6.2.1 高级调度(作业调度)
从外存往内存调度,频率最低
作业:一个具体的任务,作业后备队列
每个作业调入一次调出一次,调入建立PCB调出建立PCB
6.2.2 低级调度(进程调度/处理机调度)
按照某种策略从就绪队列中选一个进程,将处理机分配给他频率很高
进程调度是最基本的调度,进程调度频率很高一般几十毫秒一次
6.2.3 中级调度(内存调度)
内存不足的时候,挂起状态,PCB组成挂起队列
按照策略决定把哪个挂起状态的进程重新调入内存频率一般
6.3 挂起状态的七状态模型
挂起状态(suspend)分为就绪挂起和阻塞挂起两种状态
这两种状态分别是把之前的五状态模型(丁字裤模型)的就绪态、阻塞态的进程挂起产生的
运行态和创建态也有可能挂起到就绪挂起队列
进程映像:(完整的进程的对象包括进程全部信息)
注意阻塞挂起的进程映像还在内存里,就绪挂起是将内存映像调到外存去了
07-进程调度的时机、切换与过程、进程调度算法
之前的低级调度
时机
-
能进程调度的情况
- 当前进程主动放弃处理机:
-
进程终止,
-
进程异常而终止,
-
进程主动请求资源等待阻塞
-
- 当前进程被动放弃处理机:
- 时间片用完
- 更紧急的事情要处理,如IO中断
- 有更高优先级的进程进入就绪队列
- 当前进程主动放弃处理机:
-
不能进程调度的情况
-
处理中断中
-
进程在操作系统内核程序临界区(进程在普通临界区的时候是可以调度切换的)
(临界区:对系统资源进行访问的代码)
-
原子操作中(原语)
-
**注意:**内核程序临界区一般是访问某种内核数据结构的,访问的过程还是给这个数据结构上锁的,如果调度走了会让别的程序访问不了上锁资源出大问题,而像访问打印机资源,上了锁也不影响别的程序的(影响不大),可以被调度走,
方式
- 非剥夺调度方式(抢占式)
- 只允许进程主动放弃虚拟机,上面那些被动放弃的情况都做不到(实现简单,无法处理紧急任务)
- 剥夺调度方式(非抢占式)
- 中央集权。啥都能干(可以优先处理紧急任务,能实现时间片轮流执行功能,适合于分时操作系统、实时操作系统)
切换与过程
狭义的进程调度指的是就绪队列里面选中一个要运行的进程,这时候如果运行的不是刚刚暂停等待了一下又要执行的进程,那么就要进行进程切换
广义的进程调度指的是
包含了选择进程和进程切换两个步骤
进程切换的主要过程:
- 对原来运行的进程各种数据(/运行环境/PCB)的保存
- 对新的进程各种数据(/运行环境/PCB)的恢复
我们发现进程的切换是有代价的,过于频繁的话也会让系统效率变低,
让大部分时间花在进程切换上
08-调度算法评价指标
CPU利用率
忙碌时间 / 总时间
系统吞吐量
单位时间完成的作业数量
总共完成了多少道作业 / 总共花了多少时间
周转时间
作业提交给系统开始到作业完成为止
point
四个部分
- 后备队列等作业调度——高级调度
- 就绪队列等进程调度——低级调度
- 进程在CPU上执行的时间
- 进程等待I/O操作完成的时间
平均周转时间
各作业周转时间之和 / 作业数
带权周转时间
作业周转时间/作业实际运行时间
举例,上厕所的时候等十分钟上一分钟和上厕所等一分钟是完全两个概念
平均带权周转时间
等待时间
作业等待处理机状态之和
进程和作业的对比就是
作业还要加上在外存后备队列中等待的时间
平均等待时间
响应时间
用户提交请求到首次产生响应用的时间
各个时间之间的关系
周转时间=完成时间-到达时间
带权周转时间=周转时间/运行时间
等待时间=周转时间-运行时间
09-调度算法
复习脑图
- 算法思想
- 算法规则
- 调度算法是作业调度还是进程调度
- 抢占式还是非抢占式
- 优点和缺点
- 是否会导致饥饿
FCFS先来先服务FirstComeFirstServe
- 作业调度是看哪个作业先到后备队列、进程调度室考虑哪个进程先到就绪队列
- 非抢占式算法,进程/作业 主动放弃才可以
- 在考虑有IO的情况周转时间还要考虑剔除IO时间
- 优点:公平,算法简单
- 缺点:对长作业有利,短作业不利,(我就要做一件很小的事,为什么要这么久)
- 不会导致饥饿
SJF短作业优先Shortest Job First
- 要求最短时间的优先
- 非抢占式(只在当前已经到达的里面选)抢占式(过程中来了一个短的(比当前进程剩余的事件还短)得让给他)
- 优点:“最短的”平均等待时间和平均周转时间
- 会导致长进程饥饿
HRRN高响应比优先
等待时间+要求服务时间/要求服务时间,这样在短作业的基础上,随着时间的推移,他还是会获得高的优先权,巍哥一样
响应比是一个比,先把要求服务时间拿过来,生成一个一(要求服务时间 / 要求服务时间)然后再给分子加上可变的等待时间
所以
-
响应比一定大于1
-
非抢占式
10- 其他调度算法
时间片轮转调度算法
按照各进程到达就绪队列的顺序轮流让一个进程执行一个时间片,执行完时间片不管结束没结束都把进程放到队尾重新排队(时装装置发出一个时钟中断)
只有进程才有可能被分配处理机时间片所以这个算法专门给进程用的
- 抢占式算法
- 需要时间比时间片还短的会主动放弃处理机
设计时间片的时候一般会不让进程切换的开销大于1%
太大太小的影响?
优先级调度算法
给每个进程或者作业设计优先级,调度的时候选择优先级最高的调度算法
抢占式非抢占式都有,抢占式的算法在就绪队列有改变的时候都会检查一下就绪队列
优先级相同的时候,先来先服务
可能导致饥饿
多级反馈队列调度算法(UNIX使用的)
前提:有个多级反馈队列,一级最屌,往下优先级会变低
算法规则:每次有新进程进来就让他(按照先来先服务)排在第一级队列里,轮到他了让他用一个时间片,如果时间片结束了进程没有结束,进程进入下一级队列队尾,k级队列为空才给低一级的队列分分配时间片
用于进程调度
抢占式:新进程总是会来到第一级队列,就会抢占式的重新获取时间片,原来的进程会跑去原来那个进程队列的队尾
(拓展)可以让因I/O阻塞的进程重新回来,保持较高优先级
会饥饿
11-进程同步和进程互斥
同步就是为了解决进程异步的不可预知次序的问题
所以进程同步也可以当做是直接制约关系,
进程互斥
并发需要共享的支持,
互斥共享和同时共享(宏观同时,微观交替)
一个时间段内只允许一个进程使用的资源叫做临界资源,对于这些临界资源的访问需要互斥的进行,使用它的进程在使用的时候别人想用必须等待
实现进程的互斥访问逻辑上需要这样的四个部分
do {
entry section; //进入区 负责检查能不能进入临界区(能的话上锁)
critical section; //临界区 访问临界资源的代码
exit section; //退出区 解除临界资源标志,解锁
remainder section; //剩余区 做其他处理
} while(true)
实现进程互斥的原则
-
空闲让进 空着的必须响应请求
-
忙则等待 有进程进入了之后必须等待
-
有限等待 应该保证其在优先时间内能进入临界区(保证不饥饿)
-
让权等待 系统资源不够,进程不能进入临界区应该释放处理机,不行就不要占着茅坑不拉屎
(临界区:对系统资源进行访问的代码)
12-进程互斥的软件实现方法
复习点:
理解算法
重点理解算法在进入区退出区都做了什么
分析优缺点
单标志法
P0 进程:
while(turn!=0){} //进入区 //不让我用我不用
critical section; //临界区 (临界区:对系统资源进行访问的代码)
turn = 1; //退出区
remainder section; //剩余区
变量turn为检查位
在进去区进行检查,如果条件不满足就让他一直在一个不满足条件的循环里空转直到时间片结束开始去找该访问的进程
实现了互斥访问,但是会因为别人不访问就自己也等着(违反空闲让进)
双标志先检查
设置一个Bool数组
bool flag[2];
flag[0] = flase;
flag[1] = flase;
//P0进程
while(flag[1]){} //别人用的话我就不用了
flag[0] = true; //我要用
critical section; //访问临界区(临界区:对系统资源进行访问的代码)
flag[0] = false; //用完了
remainder section;
检查和上锁不是一气呵成的,中间可能时间片没了
违反忙则等待原则
双标志后检查
bool flag[2];
flag[0] = flase;
flag[1] = flase;
//P0进程
flag[0] = true;
while(flag[1]){} //别人用的话我就不用了
critical section; //访问临界区(临界区:对系统资源进行访问的代码)
flag[0] = false; //用完了
remainder section;
Peterson算法
bool flag[2];
int turn = 0;
//P0进程:
bool flag[0] = true; //我要用
turn = 1; //给对方让一下
while(flag[1] && turn == 1){} // 如果 对方说要用了 且 现在是我谦让的环节 那我就不用
//言外之意就是如果对面不用,或者现在是对面谦让的环节,就不用、用
critical section; //
flag[0] = false; //
remainder section; //
理解的时候注意,每个人的进程时顺序执行的,切换时间片再切回来还是上次执行到的地方而不是从头执行
理解完就是谁说最后一句谦让的话谁就真的会让给对方
13-进程互斥的硬件实现方法
中断屏蔽方法
使用关中断指令,不会切换时间片
简单高效但是不适合多处理机,只适合操作系统内核进程,用户使用的话很危险
TestAndSet
硬件的实现,每次要执行代码了先进入检查一个判断函数的返回值,但是true就死循环false就执行,函数根据参数在返回当前是否可用的同时也把当前的是否可用的那个锁设置成不可用的状态,因为当前这个函数无论能不能放行,CPU都会执行进程
硬件实现主要就是让这执行的期间没有被打断的可能
Swap指令(Exchange/XCHG指令)
和上一个差不多,实现逻辑不同,都会导致忙等,这个是通过交换旧的锁状态和新的锁状态以然后用旧的锁状态来做判断,
14-信号量机制
用户可以使用一对原语(P,V)(wait(S)和signal(S))来对信号量进行操作,传入的参数S就是信号量
信号量表示系统中的某种资源的数量
整型信号量
他的整型参数信号量S就是记录型信号量中的semaphore的value值代表资源的数量
void wait(int S){ //进入区
while(S <= 0){}
//这里提出了一个问题就是wait是原语但是如果条件满足了一直死循环怎么切换时间片,系统不就卡死了,我认为是while循环体内有关中断方法。
S=S-1;
}
void signal(int S){ //退出区
S=S+1;
}
整型信号量系统资源不够的话会忙等,不满足让权等待
记录型信号量
//记录型信号量的定义
typedef struct{
in value; //remain sources
struct process *L; //waiting line
}semaphore;
void wait(semaphore S){
S.value--;
if(S.value < 0){
block(S.L);//资源不够就使进程进入阻塞态进入等待队列,同时主动放弃CPU所以不会出现忙等现象
}
}
void signal(semaphore S){
S.value++;
if(S.value <= 0){
wakeup(S.L);//释放资源后如果还有人在等就用wakeup原语唤醒等待队列中的一个进程
}
}
15-信号量机制实现进程之间互斥、同步、前驱关系
实现进程互斥(可以先看同步)
设置互斥信号量mutex,
其实就是上节信号量中的value,用来表示资源剩余数量,
但是因为是用来表示两个进程的互斥,所以一般直接设置为1,一个进程用P操作拿走了,不用V释放别人就用不了
不同临界资源需要不同的互斥信号量,
P / V(wait / wakeup 等待需要耐心patient / 获得资源就是胜利Victory )操作必须成对出现
实现进程同步
进程同步让各个并发进程按要求有序的推进
比如说p1,p2两个进程,p1的一部分代码要基于p2的一部分代码运行,所以必须把不可预知的并发进程变成可预知的且可设置的
设置一个同步信号量S(代表这个制约关系的互斥信号量)
比如p1的【代码块a】需要在p2的【代码块b】之前执行,就在【代码块b】前写一个P(S)(wait)操作,在【代码块a】之后写一个V(S)(wake up)操作
实现进程的前驱关系(多级同步)
前V后P
比如p1的【代码块a】需要在p2的【代码块b】之前执行,就在【代码块b】前写一个P(S)(wait)操作,在【代码块a】之后写一个V(S)(wake up)操作
16-生产者消费者问题
问题描述
-
生产者进程——每次生产一个产品放入缓冲区,缓冲区没满的时候才可以放
-
消费者进程——每次消费一个产品从缓冲区取出,缓冲区不空的时候才可以取
-
缓冲区——初始为空,大小为五——临界资源,两种进程必须互斥访问,
两种互斥(其中一种属于同步问题)关系:
-
一种是empty-full两组同步关系,
为什么有两组同步关系呢,
因为无论是生产者还是消费者的操作,(以生产者为例,)他既被【缓冲区有没有满】制约行动,又能赋予【缓冲区有没有空】赋予能力,所以在生产者运行前要P(wait)【缓冲区有没有满】,运行后要V(wake)【缓冲区有没有空】
-
一种是生产消费的互斥关系
这个是在实际动作前后添加,使得生产和消费都变成原子操作,
注意一点就是,这个互斥关系的P操作必须要在同步的关系的P操作后面写,
因为empty-full各自是生产着消费者动作的前提条件,如果条件不满足就不能采取互斥操作,你每次互斥条件又不满足走不下去,别人被你互斥了,大家都没的走,产生死锁问题
-
17-多生产者多消费者的问题
同步关系相对复杂,但是当缓冲区为一致的时候可以抽象缓冲区为空不为空,所以不用多个PV对,只需要针对缓冲区来设置一个互斥信号量然后大家的都可以有相关PV操作,
缓冲区=1时,不需要互斥信号量,因为每次操作只会唤醒一个进程(并且停止当前进程)形成链式的单独进程操作
任何转换进程的操作都会关闭一个条件打开一个条件,01逻辑的效果
18-吸烟者问题
“可以生产多个产品的单生产者” 问题提供一个思路
19-读者_写者问题
有读者写者两组并发进程,共享一个文件,多人进行写可能会有冲突。
- 同时刻允许多个读者,
- 只允许一个写者
- 有人在写的时候文件不能读写
- 写者执行写操作前让读写这个文件的人全部退出
解决复杂的互斥问题提供了思路
重点:
-
新增了一个int count的值来记录读进程数,并且只给第一个加P,只当count归零才释放资源给V,
-
当需要一气呵成的时候就用一气呵成,关于一气呵成这个问题用到两次
- 第一次是读写互相需要互斥,把读写操作框起来
- 第二次是读者进程需要多个进程数时,只给第一个加P(释放V的时候也需要)需要一个判断,但是判断和加P两个操作没有原子化会导致有进程被阻塞
-
**读写公平法 ↓ **
-
加PV(信号量)还可以利用其队列性质使得不同进程中的而不同代码块相对公平的类似先来先服务的形式进行运行(特殊)
20 -哲学家进餐问题
问题描述
。。。
解决思路
- 限制条件,最多4个哲学家同时进餐(模拟一个最大上线的信号量)
- 要求技术号哲学家先拿左边的,再拿右边,偶数正好相反,(当被阻塞就不去拿另外一只筷子,这样五个人里面找出四个就会发现必然会有人拿到一筷子)
- 仅当哲学家左右手筷子都可以拿起来的时候才拿,这样的话需要给左手右手的两个P操作要被一个P操作包起来实现和别人的互斥,
21-管程
为什么需要 管程???
信号量难写,容易出错
管程的定义和基本特征
目的同样是实现进程互斥和同步,其实就是对临界资源的访问的互斥
组成:
-
定义和共享资源对应的共享数据结构(局部于管程)
-
对该数据结构操作的一组过程(类方法)
-
对局部于管程的共享数据设计初始值的语句
-
管程有名字
过程像定义一个类
基本特征:
- 管程的数据只能被管程相应的方法来修改(private)
- 每次仅允许一个进程在管程内执行方法
22-死锁概念
死锁:有双筷子,有两个人,一人抢一根,谁都吃不了饭
饥饿:有双筷子,有两个人,一个壮一个瘦,壮的一直用,不给瘦的
死锁必须满足四条件:破坏一个就破坏死锁
- 破坏互斥条件:两个人可以同时用筷子吃饭(画面不忍直视)
- 破坏不剥夺条件:两个人可以打一架(王者solo)决定筷子的归属
- 破坏请求和保持条件:拿着一根筷子吃不了饭还硬要拿着一根筷子玩
- 破坏循环等待条件:多给一根筷子给这两个傻×
对不可剥夺资源的不合理分配导致死锁
处理策略
- 预防死锁(破坏四个条件中的一个或几个)
- 避免死锁(银行家算法)
- 死锁的检测和解除。允许死锁的发生,靠操作系统检测和治理
23-预防死锁
破互斥条件
互斥条件是资源必须被互斥的访问
SPOOLing技术把独占设备在逻辑上改造成共享设备。
缺点:有时候做不到
破坏不剥夺条件
方案一:请求不了就立即释放,之后重新申请
方案二:抢占式调度方式,根据优先级
缺点:
- 复杂、
- 造成前一段工作的失效,所以这个方法适用于易保存和恢复状态的资源,如CPU
- 反复的申请和释放资源会增加系统开销,降低系统吞吐量
- 容易导致进程饥饿——若采用方案一,意味着只要暂时得不到某个资源,之前获得的资源就需要放弃,在申请
破坏请求和保持条件
采用静态分配方法,在运行前就一次分配完,投入运行就一直归他所有
缺点:整个运行期间保持着所有资源,造成资源浪费
可能导致自身饥饿,因为必须要所有资源满足,别人可能一直在请求其中一部分资源
破坏循环等待条件
顺序资源分配法:
- 给系统中各种资源编号,规定各个进程按照编号递增的顺序请求资源,同类资源一次申请完
举例:大家动态的申请资源时,总有一个进程当前申请的资源号是最大的,因为他不会回头申请小的,而更大的资源也没人跟他抢,所以总会有一个人不会被阻塞
缺点
-
进程实际使用的顺序和编号从小到大的排列不一致,可能需要先申请到小的,等申请到大的并且用完了之后才能使用小号资源,让小号资源空闲浪费了
-
新增设备需要重新编号
-
用户变成麻烦
24-避免死锁(*银行家算法
进程请求资源的时候必须获得所有资源才能运行,这时候如果你把手上的资源给多个进程都给一部分,这些进程都完成不了,资源也只会留在进程那里,永远不使用不释放,
建议自己代码实现一下
25-死锁的检测和解除
死锁的检测
使用了图的数据结构来表示系统状态,
两种节点:
- 进程节点
- 资源节点
进程节点指向资源节点的边:请求边
资源节点指向进程节点的边:分配边
查看资源还剩多少分配资源来消除边,能消除所有边就称之为可简化的图,就没发生死锁
死锁的解除
- 资源剥夺法:挂起(外存)某些产生死锁的进程——要防止挂起进程饥饿
- 撤销进程法:撤销,终止引起死锁的进程——浪费时间
- 进程回退法:回退某些死锁进程,直至能够避免死锁
决定撤销 / 回退谁?考虑的因素
- 进程优先级
- 已执行多久时间
- 还有多久能完成
- 进程已经使用了多少资源
- 进程是交互式的还是批处理式的