1.基本概念
1.1 进程的定义、组成、组织方式、特征
进程号(PID):进程被创建时,操作系统分配的 唯一的、不重复的
UID:进程所属用户
进程:是动态的,是程序的一次执行过程
组成:PCB(进程控制块,进程存在唯一的标志),程序段,数据段
组织方式:链接方式,指针指向不同的队列;索引方式,索引表
特征:动态性、并发性、独立性、异步性、结构性
1.2 进程的状态与转换
状态:
运行状态:占有CPU,并在CPU上运行,单核只能一个进程(双核两个)(CPU√,其它资源√)
就绪状态:已经具备运行条件,但是没有空闲的CPU,暂时不能运行(CPUX,其它资源√)
阻塞状态:等待某个事件的发生,暂时不能运行(CPUX,其它资源X)
创建状态:创建PCB,程序段,数据段
终止状态:回收内存,程序段,数据段,撤销PCB
进程状态间的转换(图,且只能这样转化)
创建态->就绪态
就绪态->运行态
运行态->就绪态
运行态->终止态(比如数组越界)
运行态->阻塞态(主动)
阻塞态->就绪态(被动)
1.3 进程控制
基本概念:
什么是进程控制?
答:实现各种进程状态转换。
如何实现进程控制?
答:用“原语”实现。
原语:执行具有原子性,不能被中断
原子性:利用关中断、开中断(特权指令)
CPU执行关中断后,不再检查中断信号,直到执行开中断指令后才恢复检查
-
机器指令级:不允许中断
-
功能级:不允许并发执行
原语做的事情:
1、更新PCB中的信息
2、将PCB插入合适的队列
3、分配/回收资源
进程控制相关的原语:
1、进程的创建:
创建原语:申请空白PCB、为新进程分配所需资源、初始化PCB、将PCB插入就绪队列
引起进程创建的事件:用户登录、作业调度、提供服务、应用请求
2、进程的终止:
撤销原语
引起进程终止的事件:正常结束、异常结束、外界干预
3、进程的阻塞:
阻塞原语:运行态->阻塞态
引起进程阻塞的事件:需要等待系统分配某种资源、需要等待相互合作的其他进程完成工作
4、进程的唤醒:
唤醒原语:阻塞态->就绪态
引起进程唤醒的事件:等待的事件发生
5、进程的切换
切换原语(运行态与就绪态之间的切换)
引起进程切换的事件:当前进程事件片到、有更高优先级的进程到达、当前进程主动阻塞、当前进程终止
1.4 进程通信
进程通信(IPC):指两个进程之间产生数据交互。
1、共享存储 (分配共享空间,且访问互斥(P、V操作)空间内存储地址自由
基于数据结构的共享:固定分配(低级)
基于存储区的共享:划分存储区(高级)
2、消息传递
消息:消息头、消息体
直接通信方式(直接挂载消息)由操作系统内核将消息添加至接收进程的消息队列
间接通信方式(间接利用信箱发送消息)
3、管道通信(pipe)
只能半双工通信(即某一时间段内只能单向传输)大小固定的内存缓冲区
先进先出,顺序存储(循环队列)
各进程访问互斥
当管道写满时,写进程将阻塞,直到读进程将管道中数据取走,即可唤醒写进程
当管道读空时,读进程将阻塞,直到写进程往管道写数据,即可唤醒读进程
1.5 线程概念和多线程模型
什么是线程,为什么要引入线程?
答:线程是一个基本的CPU执行单元,也是程序执行流的最小单位,进一步提高了系统的并发度
引入线程机制后,有什么变化?
资源分配、调度:进程是资源分配的基本单位,线程是调度的基本单位
并发性:各线程间也能并发,提升了并发度
系统开销:可以只在进程中切换,减小了CPU切换环境的系统开销
1、线程有哪些重要的属性
-
线程是处理机调度的基本单位
-
多CPU计算机中,各个线程可占用不同的CPU
-
每个线程都有一个线程ID、线程控制块(TCB)
-
线程也有就绪、阻塞、运行三种基本状态
-
线程几乎不拥有系统资源
-
同一进程的不同线程间共享进程的资源
-
由于共享内存地址空间,统一进程中的线程间通信甚至无需系统干预
-
同一进程中的线程切换,不会引起进程切换
-
不同进程中的线程切换,会引起进程切换
-
切换同进程内的线程,系统开销很小
-
切换进程,系统开销较大
2、线程的实现方式
用户级线程(ULT):
进程是处理机调度的单位
由应用管理,在用户态下实现进程切换,从用户的视角看能看到的线程
优点:在用户空间完成切换,线程管理的系统开销小,效率高
缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高
内核级线程(KLT):
由操作系统管理,从操作系统内核视角看能看到的线程
n个ULT可以映射到m个KLT上(n>=m)
内核级线程才是处理机调度的单位
3、多线程模型
多对一模型
n个ULT映射到1个KLT
优点:开销小,效率高
缺点:容易阻塞,并发度不高
一对一模型
n个ULT映射到n个KLT
优点:并发能力很强
缺点:占用成本高,开销大
多对多模型
n个ULT映射到m个KLT上(n>=m)
中和以上两种优缺点
4、 线程的状态与转换,组织与控制都和进程相同
进程与线程的比较
-
进程时资源分配的单元,线程是CPU调度单元
-
进程拥有一个完整的资源平台,而线程只独享指令流执行的必要资源
-
线程能减少并发执行的时间和空间开销
2.进程调度
2.1 处理机调度的概念、层次
基本概念
通常进程数量大于处理机数量,所以要按照一定的算法选择一个进程,并将处理机分配给它运行,以实现进程的并发执行
三个层次
高级调度(作业调度)
辅助外存与内存之间的调度,作业调入时会建立相应的PCB,作业调出时才撤销PCB,调入可由操作系统决定,调出由作业运行结束才调出
中级调度(内存调度)
将暂时不用的进程放到外存(PCB不外放),提高内存利用率和系统吞吐量,进程状态为挂起状态,形成挂起队列
低级调度(进程调度)
最基本,用算法从就绪队列中选取一个进程,并分配处理机资源,几十ms一次
三层调度的联系、对比
进程的“挂起态”
七状态模型
五状态前面学了,挂起分为就绪挂起、阻塞挂起
2.2 进程调度的时机、切换与过程调度方式
1、时机
什么时候需要进程调度?
-
主动放弃(进程正常终止、运行过程中发生异常而终止、进程主动请求阻塞)
-
被动放弃(分给进程的时间片用完、有更紧急的事需要处理、有更高优先级的进程进入就绪队列)
什么时候不能进行进程调度?
-
在处理中断的过程中
-
在操作系统内核程序临界区中
-
临界资源:一个时段段内各进程需要互斥地访问临界资源(即一个时间段内只允许一个进程使用的资源)。
-
临界区:访问临界资源的那段代码
-
内核程序临界区会访问就绪队列,导致其上锁
-
在原子操作过程中(原语)
2、切换与过程
“狭义的调度”与“进程切换”的区别
-
狭义:选择一个要运行的进程
-
广义:狭义+进程切换(一个进程让出处理机,另一个进程占用)
进程切换的过程需要做什么?
-
对原来运行进程各种数据的保存
-
对新的进程各种数据的恢复
进程调度、切换是有代价的
3、方式
非剥夺调度方式(非抢占式)
-
只允许进程主动放弃处理机
剥夺调度方式(抢占式)
-
进程被动放弃,可以优先处理紧急任务,(适合分时操作系统、实时操作系统)
2.3 调度程序
功能:
-
让谁运行 ——调度算法
-
运行多久——时间片大小
对象:
就绪态与运行态之间的切换
触发原因:
-
创建新进程
-
进程退出
-
运行进程阻塞
-
I/O中断发生
闲逛进程:
调度器没有其他就绪进程时,运行闲逛进程
特性:
-
优先级最低
-
能耗低
-
可以是0地址指令,占一个完整的指令周期(周期末尾例行检查中断)
2.4 调度算法的评价指标
1、CPU利用率
CPU利用率 = CPU忙碌的时间 / 总时间
2、系统吞吐量
系统吞吐量 = 总共完成了多少道作业 / 总共画了多少时间
3、周转时间(越小越好)
-
周转时间(提交作业到完成作业花费的时间)、平均周转时间(各作业周转时间之和 / 作业数)
-
带权周转时间(作业周转时间 / 作业实际运行的时间)、平均带权周转时间(各作业带权周转时间 / 作业数)
4、等待时间
进程或作业等待处理机状态时间的和
进程:等待被服务的时间之和
作业:建立后的等待时间 + 作业在外存后备队列中等待的时间
5、响应时间
从用户提交请求到首次产生响应所用的时间
2.5 FCFS、SJF、HRRN调度算法**
1、先来先服务(FCFS)
算法思想:先到达先进行服务
作业-后备队列;进程-就绪队列
非抢占式
优点:公平、算法简单
对长作业有利、对短作业不利、不会饥饿
饥饿:某进程/作业长期得不到服务
2、短作业优先(SJF,shortest job first)/(SPN,)
算法规则:最短(服务时间最短)的作业优先得到服务,时间相同,先到达的先被服务
非抢占式(SJF):选最短需要时间的作业先进入运行态
抢占式(SRTN):有新作业进入就绪队列或有作业完成了,考察队列中的最小需要时间的作业
在所有进程都几乎同时到达时,采用SJP调度算法的平均等待时间、平均周转时间最少
若无红色前提,抢占式的短作业/进程的平均时间最少
优点:“最短的”平均等待时间,平均周转时间
缺点:对短作业有利,对长作业不利,可能产生饥饿现象(一直有短进程到达,长进程得不到服务)
3、高响应比优先(HRRN)
要综合考虑作业/进程的等待时间和要求服务的时间
在每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务
响应比=(等待时间+要求服务时间)/要求服务时间
非抢占式
进程主动放弃CPU时,需要该算法选取就绪队列的作业
不会饥饿
2.5 时间片轮转、优先级调度、多级反馈队列(适合交互式系统)
1、时间片轮转算法(RR)
算法思想:公平轮流地位各个进程服务,让每个进程在一定时间间隔内都可以得到响应
算法规则:按照各进程到达就绪队列的顺序,轮流让各个进程执行一个时间片(如100ms)。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列对位重新排队。
只能用于进程调度
抢占式
优点:响应块,适用于分时操作系统
缺点:由于高频率的进程切换,因此有一定的开销;不区分任务的紧急程度
不会饥饿
2、优先级调度算法
算法思想:根据任务的紧急程度来决定处理顺序
算法规则:每个进程/作业有各自的优先级,调度时选择优先级最高的作业/进程
适用:作业 / 进程 / IO
抢占式 / 非抢占均有
静态优先级:不变
动态优先级:可以变
通常:系统进程优先级高于用户进程,前台进程优先级高于后台进程,操作系统更偏好I/O进程
可以从追求公平、提升资源利用率等角度考虑改变优先级
可能会饥饿
3、多级反馈队列调度算法(UNIX)
算法思想:对其它算法调度的这种权衡
算法实现:设置多级就绪队列,各级队列优先级从高到低,时间片从小到大。
新进程到达时先进入第一级队列,按照FCFS原则排队等待被分配时间片。若用完时间片进程还未结束,则进程进入下一级队列对位。如果此时已经在最下级的队列,则重新放回最下级队列末尾。
只有第K级队头的进程为空时,才会为K+1级对头的进程分配时间片,被抢占处理机的进程重新放回原队列队尾。
优点:对各个进程相对公平(FCFS的优点),每个新到达的进程都可以很快就得到响应(RR的优点);短进程只用较少的时间就可以完成(SPF的优点);不必实现估计进程的运行时间(避免用户作假);可灵活地调整对各类进程的偏好程度,比如CPU密集型进程、IO密集型进程
默认抢占式
会饥饿
3.进程同步与互斥
3.1 进程同步、进程互斥
1、进程同步
指为了完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们的工作次序而产生的制约关系。进程间的直接制约关系就是源于它们之间的相互合作。
(为解决进程异步性,为部分进程协调一定的工作次序)
2、进程互斥
把一个时间段内只允许一个进程使用的资源称为临界资源。
对临界资源的互斥访问,可以在逻辑上分为四个部分:
do{ entry section; //进入区 对访问的资源检查或进行上锁 critical section; //临界区(段) 访问临界资源的那部分代码 exit section; //退出区 负责解锁 remainder section; //剩余区 其它处理 } while(true)
互斥原则:
1、空闲让进。 临界区空闲的可以直接进去
2、忙则等待。 繁忙不能进去
3、有限等待。 不能让进程等待无限长时间(保证不会饥饿)
4、让权等待。 当进程不能进入临界区时,应立即释放处理机,防止进程忙等待(不能进去,不要堵着)
3.2 进程互斥的软件实现方法
1、单标志法
两个进程在访问完临界区后会把使用临界区的权限教给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予
实现:同一时刻最多只允许一个进程访问临界区
int turn =0; //p0进程 while(turn!=0); //检查对方是否让给自己 critical section; turn = 1; //让给对方 remainder section; //p1进程 while(turn!=1); critical section; turn = 0; remainder section;
可以实现互斥
存在的问题:必须轮流访问,可能p1要访问的话,必须p0先访问,违背:空闲让进原则
2、双标志先检查
算法思想:设置一个bool数组flag[]来标记自己是否想要进入临界区的意愿
bool flag[2]={false,false}; //p1进程 while(flag[1]); //检查对方是否想要(检查) flag[0]=true; //表达自己想要(上锁) critical section; flag[0]=false; remainder section; //p2进程 while(flag[0]); flag[0]=true; critical section; flag[1]=false; remainder section;
主要问题:由于进程是并发进行的,“检查”与“上锁”两个步骤不是一气呵成的,可能两个进程同时进入临界区。违背:忙则等待的原则
3、双标志后检查
算法思想:设置一个bool数组flag[]来标记自己是否想要进入临界区的意愿,不过是先上锁后检查
bool flag[2]={false,false}; //p1进程 flag[0]=true; //自己想要(上锁) while(flag[1]); //对方是否想要(检查) critical section; flag[0]=false; remainder section; //p2进程 flag[0]=true; while(flag[0]); critical section; flag[1]=false; remainder section;
主要问题:由于进程是并发进行的,可能会两个同时上锁,都进不去,违反:空闲让进和有限等待原则
会饥饿
4、Peterson 算法
主动让对方先使用处理器
bool flag[2]={false,false}; int turn=0; //p1进程 flag[0]=true; // 表达自己想要 turn=1; // 优先让给对方 while(flag[1]&&turn==1); // 对方想要 且 最后是自己谦让给对方 critical section; flag[0]=false; // 表达不用了 remainder section; //p2进程 flag[1]=true; turn=0; while(flag[0]&&turn==0); critical section; flag[1]=false; remainder section;
遵循空闲让进、忙则等待、有限等待三个原则
但是未遵循让权等待的原则
3.3 进程互斥的硬件实现方法
1*、中断屏蔽方法:
... 关中断(不允许进程中断) 临界区 开中断 ...
简单、高效
不适用多处理机,只适用单处理机,可能会同时访问临界资源
只适用OS内核进程
2*、TestAndSetLock(TSL指令)加锁法
TSL是用硬件实现的,上锁、检查一气呵成
不满足让权等待,会忙等
C语言描述逻辑:
//true表示已经上锁 bool TestAndSet(bool *lock){ bool old; old=*lock; *lock=true; return old; } //以下是使用TSL指令实现互斥的算法逻辑 while(TestAndSet (&lock));//上锁并检查,两个步骤变原子操作 临界区代码段... lock=false; //解锁 剩余区代码段...
3、Swap指令
别称:Exchange指令、XCHG指令
Swap指令是用硬件实现的
//true表示已经上锁 void Swap(bool *a,bool *b){ bool temp; temp=*a; *a=*b; *b=temp; } //以下是使用Swap指令实现互斥的算法逻辑 bool old=true; while(old=true) Swap(&lock,&old); 临界区代码段 lock=false; //解锁 //剩余代码段
简单
适用多处理机
不能让权等待
3.4 信号量机制
信号量:
信号量是一种变量,表示系统中某种资源的数量
一对原语:wait(S)原语和signal(S)原语,分别简称P(S)、V(S)/“申请”与“释放”
1、整形信号量
用一个整数表示系统资源的变量,用来表示系统中某种资源的数量
//类似双标志先检查,检查与上锁一气呵成 int S=1; void wait(int S){ //wait原语,相当于:进入区 while(S<=0); //如果资源数不够,就一直循环等待 S=S-1; //如果资源数够,则占用一个资源 } void signal(int S){//signal原语,相当于“退出区” S=S+1; //使用完资源后,在退出区释放资源 }
可能会出现忙等,不满足让权等待原则
2*、记录型信号量 !!!
记录型数据结构表示的信号量
//记录型信号量的定义 typedef struct{ int value; struct process *L; } semaphore; //某进程需要使用资源时,通过wait原语申请 void wait (semaphore S){ S.value--; if(S.value<0){ block (S.L);//将该进程加入到等待队列中(从运行态进入阻塞态) } } //进程使用完资源后,通过signal原语释放 void signal (semaphore S){ S.value++; if(S.value<=0){ //S.value<=0表示有进程等待本资源 wakeup(S.L);//唤醒等待队列中的一个进程(从阻塞态变为就绪态) } }
除非特别说明,否则默认S为记录型信号量
3.5 用信号量机制实现进程互斥、同步、前驱关系
1、实现进程互斥(信号量初值为1)
设置互斥信号量mutex,初值为1
对不同的临界资源需要设置不同的互斥信号量
PV必须成对出现
2、实现进程同步(信号量初值为0)
-
保证一前一后的操作顺序
-
设置同步信号量S,初始为0
-
在“前操作”之后执行V(S)
-
在“后操作”之后执行(V)
3、实现进程的前驱关系
1、要为每一对前驱关系各设置一个同步变量(初值为0)
2、在“前操作”之后对相应的同步变量执行V操作
3、在“后操作”之前对相应的同步变量执行P操作
3.6 生产者-消费者问题
原则:
-
只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待
-
只有缓冲区不空时,消费者才能从中取出产品,否则必须等待
-
缓冲区是临界资源,各个进程互斥访问
semaphore mutex = 1; //互斥信号量,实现对缓冲区的互斥访问 semaphore empty = n; //同步信号量,表示空闲缓冲区的数量 semaphore full = 0; //同步信号量,表示产品的数量,即非空缓冲区的数量
producer () { while(1) { 生产一个产品; P(empty); //消耗一个空闲缓冲区 P(mutex); //上锁 把产品放入缓冲区; V(mutex); //释放 V(full); 增加一个产品(非空缓冲区) } } consumer () { while(1) { P(full); P(mutex); 从缓冲区取出一个产品; //临界区 V(mutex); V(empty); 使用产品; } }
实现互斥的P操作要放在实现同步的P操作之后,不然会发生死锁
V操作不会导致进程发生阻塞的状态,所以可以交换
使用操作不要放在临界区,不然并发度会降低
3.7 读者-写者问题 XXX
1、允许多个读者同时对文件执行读操作
2、只允许一个写者往文件中写信息
3、任一写者在完成写操作之前不允许其他读者或写者工作
4、写者执行写操作前,应让已有的读者和写者全部退出
semaphore rw=1;//用于实现对文件的互斥访问。表示当前是否有进程在访问共享文件 int count=0;//记录当前有几个读进程在访问文件 semaphore mutex=1;//用于保证对count变量的互斥访问 semaphore w=1; //用于实现“写优先” writer(){ while(1){ P(w); P(rw); //写之前“加锁” 写文件。。。 V(rw);//写之后“解锁” V(w); } } reader(){ while(1){ P(w); P(mutex); //各读进程互斥访问count if(count==0) P(rw); //第一个读进程的读进程数+1 count++; //访问文件的读进程数+1 V(mutex); V(w); 读文件... P(mutex); //各读进程互斥访问count count--; //访问文件的读进程数-1 if(count==0) V(rw); //最后一个读进程负责“解锁” V(mutex); } }
3,8 哲学家进餐问题
五个人,必须拿左右的筷子才能吃饭(每个进程需要两个临界资源)
关键在于解决进程死锁
解决方案:
1、可以对哲学家进程施加一些限制条件,比如最多允许四个哲学家同时进餐,这样可以保证至少有一个哲学家是可以拿到左右两只筷子的。
2、要求奇数号哲学家先拿左边的筷子,然后再拿右边的筷子,而偶数号哲学家刚好相反。用这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭,那么只会有其中一个可以拿起第一只筷子,另一个会直接阻塞。这就避免了占有一只后再等待另一只的情况。
3、仅当一个哲学家左右两只筷子都可用时才允许他抓起筷子。
semaphore chopstick[5]={1,1,1,1,1}; semaphore mutex = 1; //互斥地取筷子 Pi(){ //i号哲学家的进程 while(1){ P(mutex); p(chopstick[i]); //拿左 p(chopstick[(i+1)%5]);//拿右 V(mutex); 吃饭... V(chopstick[i]); V(chopstick[(i+1)%5]); 思考... } }
3.9 管程
1、为什么要引入管程
PV操作容易出错、困难
2、管程的定义和基本特征
定义:
-
局部于管程的共享数据结构说明
-
对该数据结构进程操作的一组过程
-
对局部于管程的共享数据设置初始值的语句
-
管程有一个名字
基本特征:
-
局部于管程数据结构只能被局部于管程的过程所访问
-
一个进程只有通过调用管程内的过程才能进入管程访问共享数据
-
每次仅允许一个进程在管程内执行某个内部过程
心得:相当于C++的类,管程是数据放在private中,函数放在public中
拓展1:用管程解决生产者消费者问题
monitor producerconsumer condition full,empty; int count = 0; void insert(Item item){ if(count == N) wait(full); count++; insert_item (item); if(count == 1) signal(empty); } Item remove(){ if(count == 0) wait(empty); count--; if(count == N-1) signal(full); return remove_item(); } end monitor; //使用 producer(){ while(1){ item = 生产一个产品; producerconsumer.insert(item); } } consumer(){ while(1){ item = producerconsumer.remove(); 消费产品 item; } }
拓展2:Java中类似于管程的机制
java中用synchronized来描述一个函数,这个函数同一时间只能被一个线程调用
4.死锁
4.1 死锁的概念
1、什么是死锁
各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
2、进程死锁、饥饿、死循环的区别
死锁:
定义:各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
区别:至少两个或两个的进程同时发生死锁
饥饿:
定义:由于长期得不到想要的资源,某进程无法向前推进的现象。
区别:可能只有一个进程发生饥饿
死循环:
定义:某进程执行过程中一直跳不出某个循环的现象。
区别:死循环是程序员的问题
3、死锁产生的必要条件
-
互斥条件:多个进程争夺资源发生死锁
-
不剥夺条件(不可抢占):进程获得的资源不能由其它进程强行抢夺
-
请求和保持条件:某个进程有了资源,还在请求资源
-
循环等待条件:存在资源的循环等待链(循环等待未必死锁,死锁一定有循环等待)
4、什么时候会发生死锁
-
对系统资源的竞争
-
进程推进顺序非法
-
信号量的使用不当也会造成死锁
5、死锁的处理策略
-
预防死锁
-
避免死锁
-
死锁的检测和解除
4.2 死锁的处理策略——预防死锁
1、不允许死锁发生
静态策略:预防死锁
-
破坏互斥条件(有些不能破坏)
把互斥的资源改造为共享资源
-
破坏不剥夺条件(复杂,造成之前工作失效,降低系统开销,会全部放弃、导致饥饿)
方案1:当请求得不到满足的时候,立即释放手里的资源
方案2:由系统介入,强行帮助剥夺
-
破坏请求和保持条件(资源利用率极低,可能会导致某些进程饥饿)
采用静态分配方法,一次性全部申请,如果申请不到,不要允许
-
破坏循环等待条件(不方便增加新的设备,实际使用与递增顺序不一致,会导致资源的浪费,必须按规定次序申请资源)
顺序资源分配法:对资源编号,进程按编号递增顺序请求资源
动态检测:避免死锁
2、允许死锁发生
-
死锁的检测和解除
4.3 死锁的处理策略——避免死锁
动态检测:避免死锁
-
什么是安全序列
进行后面的某些情况,不会使系统发生死锁
2. 什么是系统的不安全状态,与死锁有何联系
如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
3.如何避免系统进入不安全状态——银行家算法
初始分配完成后,优先全部分配给最少的,并且拿回资源
步骤:
1、检查此次申请是否超过了之前声明的最大需求数
2、检查此时系统剩余的可用资源是否还能满足这次请求
3、试探着分配,更改各数据结构
4、用安全性算法检查此次所分配是否会导致系统进入不安全状态
安全性算法步骤:
-
检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列。并把该进程持有的资源全部回收
-
不断重复上述过程,看最终能否让所有进程进入安全序列
4.4 死锁的处理策略——检测和解除
死锁的检测
1、用某种数据结构来保存资源的请求和分配信息
2、提供一种算法,利用上述信息来检测系统是否已进入死锁状态
死锁的解除
1、资源剥夺法:挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。
2、撤销进程法:强制撤销部分,甚至全部死锁进程,并剥夺这些进程的资源。
3、进程回退法:让一个或多个死锁进程回退到足以避免死锁的地步。