操作系统-第二章
文章目录
进程的概念,组成,特征
进程的概念
程序:是静态的,就是个存放在磁盘里的可执行文件,就是一系列的指令集合
进程:是动态的,是程序的一次执行过程
同一个程序多次执行会对应多个进程
进程的组成–PCB
当进程被创建时,操作系统会为该进程分配一个唯一的,不重复的“身份证号“,–PID
- 操作系统要记录PID,进程所属用户ID(基本的进程描述信息,可以让操作系统区分整个进程)
- 记录进程分配了哪些资源(操作系统对资源的管理)
- 记录进程的运行情况(用于实现操作系统对进程的控制,调度)
这些信息都被保存在一个数据结构PCB中,即进程控制块
操作系统需要对各个并发运行的进程进行管理,但凡管理时所需要的信息,都会被放在PCB中
进程控制块(PCB)
- 进程存在的唯一标志
进程描述信息
进程标识符PID
用户标识符UID
进程控制和管理信息(进程的管理者(操作系统)所需的数据都在PCB中
CPU、磁盘、网络流量使用情况统计。。
进程当前状态:就绪态/阻塞态/运行态
资源分配清单
正在使用哪些文件
哪些内存区域
哪些I/O设备
处理及相关信息
PSW、PC等等各种寄存器的值(用于实现进程切换
进程的组成–程序段、数据段
- 程序段:程序的代码(指令序列)
- 数据段:运行过程中产生的各种数据(eg:程序中定义的变量
PCB是给操作系统用的
程序段、数据段是给进程自己用的
一个进程实体(进程映像)由PCB,程序段,数据段组成。进程是动态的,进程实体是静态的
进程实体反应了进程在某一时刻的状态
程序段,数据段,PCB三部分组成了进程实体
进程是进程实体的运行过程,是系统进行资源分配,调度的一个独立单位
一个进程被调度,就是指操作系统决定让这个进程上CPU运行
进程的特征
- 动态性:进程的最基本特性
- 并发性
- 独立性:进程是能够独立运行,独立获得资源,接收调度的基本单位
- 异步性:导致并发程序的不确定性
- 结构性
进程状态的转换
创建态========>就绪态进程被调度>运行态=进程运行结束,或遇到不可修复错误>终止态
< =时间片到,或处理机被抢占==
👆 👇
|| ||
|| ||
= 阻塞态 ===
运行态->阻塞态是一种进程自身做出的主动行为
阻塞态->就绪态不是进程自身控制的,是一种被动行为
注意不能由阻塞态直接转换为运行态,也不能由就绪态直接转换为阻塞态(因为进入阻塞态是进程主动请求的,必然需要进程在运行时发出这种请求)
三种基本状态
运行态:占有CPU,并在CPU 上运行
就绪态:已经具备运行条件,但没有空闲的CPU,暂时不能运行
阻塞态(等待态):因等待某一件事情而暂时不能运行
另外两种状态
创建态(新建态):进程正在被创建,操作系统为进程分配资源,初始化PCB
终止态(结束态):进程从系统中撤销,系统会收回进程拥有的资源,撤销PCB
进程PCB中,会有一个变量来表示进程当前的状态
进程的组织–连接方式
按照进程状态将PCB分为多个队列
操作系统持有指向各个队列的指针
- 执行指针:单CPU计算机中,同一时刻只会有一个进程处于运行态
- 就绪队列指针:通常会把优先级高的进程放在队头
- 阻塞队列指针:根据阻塞原因不同,再分为多个阻塞队列
进程的组织–索引方式
根据进程状态的不同,建立几张索引表
操作系统持有指向各个索引表的指针
进程的控制
-
实现进程状态转换
-
如何实现进程控制—用原语实现
-
如何实现原语的“原子性”
用“关中断指令”和“开中断指令”,这两个特权指令实现原子性(内核程序,运行在内核态
进程的创建
创建原语
- 申请空白PCB
- 为新进程分配所需资源
- 初始化PCB
- 将PCB插入就绪队列(创建态–>就绪态
引起进程创建的事件
- 用户登录
- 作业调度
- 提供服务
- 应用请求
进程的终止
撤销原语
- 从PCB集合中找到终止进程的PCB
- 若进程正在运行,立即剥夺CPU,将CPU分配给其他进程
- 终止其所有子进程
- 将该进程拥有的所有资源归还给父进程或操作系统
- 删除PCB
引起进程终止的事件
- 正常结束
- 异常结束
- 外界干预
进程的阻塞和唤醒
阻塞原语唤醒原语必须成对使用
进程的阻塞
进程原语(运行态–>阻塞态)
- 找到要阻塞的进程对应的PCB
- 保护进程运行现场,将PCB状态信息设置为”阻塞态“,暂时停止进程运行
- 将PCB插入相应事件的等待队列
引起进程阻塞的事件
- 需要等待系统分配某种资源
- 需要等待互相合作的其他进程完成工作
进程的唤醒
唤醒原语(阻塞态–>就绪态)
- 在事件等待队列中找到PCB
- 将PCB从等待队列移除,设置进程为就绪态
- 将PCB插入就绪队列,等待被调度
引起进程唤醒的事件
- 等待的事件发生(因何事阻塞,就应由何事唤醒)
进程的切换
切换原语
- 将将运行环境信息存入PCB
- PCB移入相应队列
- 选择另一个进程执行,并更新其PCB
- 根据PCB恢复新进程所需的运行环境
切换进程切换的事件
- 当前进程时间片到
- 有更高优先级的进程到达
- 当前进程主动阻塞
- 当前进程终止
先在PCB中保存这个进程的运行环境,再次投入使用是,可以通过PCB恢复它的运行环境
无论哪个进程控制原语,要做的无非三类事情
- 更新PCB中的信息
- 将PCB插入适合的队列
- 分配/回收资源
进程通信
- 进程是分配系统资源的单位,因此各进程拥有的内存地址空间相互独立
- 为了保证安全,一个进程不能直接访问另一个进程的地址空间
共享存储
-
设置一个共享区域,并映射到进程的虚拟地址空间
-
各个进程对共享空间的访问应该是互斥的
基于数据结构的共享
eg:只能存放长度为10的数组,这种共享方式速度慢,限制多,是一种低级通信方式
基于存储区的共享
- 数据的形式,存放位置都用通信进程控制,而不是操作系统,速度块,是一种高级通信方式
消息传递
进程间的数据交换以格式化消息为单位,进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换
消息头:发送进程ID,接收进程ID,消息长度等格式化消息
消息体
直接通信方式(消息直接挂到接收进程的消息队列里)
- 接收和发送的id,点名道姓的消息传递
间接通信方式(消息先发到中间体)
- 以信箱作为中间实体进行消息传递
- 可以多个进程往同一个信息send消息,也可以多个进程从同一个信箱receive消息
管道通信
在内存中开辟一个大小固定的内存缓冲区
- 管道只能采用半双工通信,单向传输,如果要实现双向同时通信,则需要设置两个管道
- 各个进程互斥的访问管道
- 管道写满时,写进程将阻塞
- 管道读空时,读进程将阻塞
- 一个管道允许多个写进程,一个读进程
- 只要管道没空,读进程就可以从管道读数据
- 只要管道没满,写进程就可以从管道写数据
线程
- 引入线程后,线程称为了程序执行流的最小单位
- 线程可以理解为“轻量级进程”
- 线程是一个基本的CPU执行单元,也是程序执行流的最小单元
- 线程可以并发,线程之间也可以并发,进一步提高了系统的并发度,使得一个进程内也可以并发处理各种任务
- 引入线程后,进程只作为除CPU之外的系统资源的分配单元(eg:打印机,内存地址空间等都是分配给进程的)
引入线程机制后,带来的变化
资源分配,调度
传统进程机制中,进程是资源分配,调度的基本单位
引入线程后,进程是资源分配的基本单位,线程是调度基本单位
并发性
传统进程机制中,只能进程间并发
引入线程后,各线程间也能并发,提高了并发度
系统开销
传统的进程间并发,需要切换进程的运行环境,系统开销很大
线程间并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小
引入线程后,并发所带来的系统开销减小
线程的属性
- 是处理机调度的单位
- 多CPU计算机中,各个线程可占用不同的CPU
- 每个线程都有一个线程ID,线程控制块TCB
- 线程也有就绪,阻塞,运行三种基本状态
- 线程几乎不拥有系统资源
- 同一进程的不同线程间共享进程资源
- 由于共享内存地址空间,同一进程中的线程间通信甚至无需系统干预
- 同一进程中的线程切换,不会引起进程切换
- 不同进程中的线程切换,会引起进程切换
- 切换同进程内的线程,系统开销很小
- 切换进程,系统开销较大
线程的实现方式
用户级线程
- 用户级线程由应用程序通过线程库实现,所有线程管理工作都有应用程序负责
- 线程切换可以在用户态下即可完成,无需操作系统干预
- 在用户看来,有多个线程,但操作系统内核看来,并意识不到线程的存在
- 用户级线程就是从用户视角看能看到的线程
优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高,多个线程不可在多核处理机上并行运行
内核级线程
-
内核级线程的管理工作由操作系统内核完成
-
线程调度,切换等工作都由内核负责,因此内核级线程的切换必然需要在核心态下才能完成
-
操作系统会为每个内核级线程建立相应的TCB,通过TCB对线程进行管理,内核级线程就是从操作系统内核视角看能看到的线程
优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强,多线程可在多核处理机上并行执行
缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理成本高,开销大
多模型线程
一对一模型:(与内核级线程相同)
多对一模型:(与用户级线程优缺点相同)多个用户级线程映射到一个内核级线程,且一个进程只被分配一个内核级线程
操作系统只看得见内核级线程,因此只有内核级线程才是处理机分配的单位
多对多模型:n用户级线程映射到m个内核级线程(n>=m),每个用户进程对应m个内核级线程
克服了多对一模型并发不高的缺点,又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点
用户级线程是“代码逻辑”的载体
内核级线程是“运行机会”的载体
内核级线程才是处理机分配的单位
一段代码逻辑只有获得了“运行机会”才能被CPU执行
线程的组织与控制
TCB:线程标识符(TID),程序计数器PC(目前执行到哪里),其他寄存器(运行的中间结构),堆栈指针(保存函数调用信息,局部变量等),线程运行状态(运行、就绪、阻塞),优先级(线程调度,资源分配发参考)
调度
当由一堆任务要处理,但由于资源有限,这些事情没办法同时处理,这就需要确定某种规则来决定处理这些任务的顺序,这就是调度所研究的问题
调度的三个层次
高级调度(外存–>内存)面向作业
无->创建态->就绪态
作业:一个具体任务
用户向系统提交一个作业=用户让操作系统启动一个一个程序(来处理一个具体的任务)
高级调度(作业调度),按一定原则从外存的作业后备队列中挑选一个作业调入内存,并创建进程,每个作业只调度一次,调出一次,作业调入使会建立PCB,调出时才撤销PCB
低级调度(内存–>CPU)
就绪态–>运行态
低级调度(进程调度、处理机调度)–按照某种策略从就绪队列中选取一个进程,将处理机分配给它
进程调度是操作系统中最基本的一种调度,一般的操作系统中都必须配置进程调度,进程调度频率很高,一般几十毫秒一次
中级调度(外存–>内存(面向进程)
挂起态–>就绪态
(阻塞挂起–>阻塞态)
内存不够,可将某些进程的数据调出外存,等内存空闲或者进程需要运行时重新调入内存
暂时调到外存等待的进程状态为挂起状态,被挂起的进程PCB会被组织成挂起队列
中级调度(内存调度)–按照某种策略决定将哪个处于挂起状态的进程重新调入内存
中级调度发生的频率要比高级调度更高
七状态模型=五状态+就绪挂起+阻塞挂起
暂时调到外存等待的进程状态为挂起状态,挂起态又可以细分为就绪挂起,阻塞挂起两种状态
挂起态在外存,阻塞态在内存
进程调度的时机
进程调度(低级调度):按照某种策略从就绪队列中选取一个进程,将处理机分配给处理机
需要进行进程调度与切换的情况:
当前运行的进程主动放弃处理机:
进程正常终止
运行过程中发生异常终止
进程主动请求阻塞
当前运行的进程被动放弃处理机:
分给进程的时间片用完
有更紧急的事需要处理(I/O中断)
有更高优先级的进程进入就绪队列
不能进行进程调度与切换的情况:
- 在处理中断的过程中,中断处理过程复杂,与硬件密切相关,很难做到中断处理过程中进行进程切换
- 进程在操作系统内核临界区(但是进程在普通临界区中是可以进行调度,切换的)
- 在原子操作过程中
临界资源:一个时间段内只允许一个进程使用的资源,各进程需要互斥地访问临界资源
临界区:访问临界资源的那段代码
内核程序临界区一般是用来访问某种内核数据结构的,比如进程的就绪队列(由各就绪进程的PCB组成)
进程调度的方式
非剥夺调度方式
又称非抢占方式,实现简单,系统开销小但是无法及时处理紧急任务,适合于早期的批处理系统
剥夺调度方式
抢占方式,可以优先处理更紧急的进程,也可实现让各进程按时间片轮流执行的功能(时钟中断),适合分时操作系统,实时操作系统
狭义的进程调度指从就绪队列中选中一个要运行的进程
进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程
广义的进程调度包含了选择一个进程和进程切换两个步骤
进程切换的过程主要完成了:
- 对原来运行进程各种数据的保存
- 对新的进程各种数据的恢复
注意:
进程切换是有代价的,因此如果过于频繁的进行进程调度,切换,必然会使整个系统的效率降低,使操作系统大部分时间花在了进程切换上,而真正用于执行进程的时间减少,并不是调度越频繁,并发度就越高
什么事件会触发“调度程序”
- 创建新进程
- 进程退出
- 进程阻塞
- I/O中断发生
调度器,调度程序
- 不支持内核级线程的操作系统,调度程序的处理对象是进程
- 支持内核级线程的操作系统,调度程序的处理对象是内核线程
闲逛进程
-
调度程序永远的备胎,没有其他就绪进程时,运行闲逛进程
-
特性:
-
优先级最低
可以是0地址指令,占领一个完整的指令周期
能耗低
调度算法的评价指标
CPU利用率
利用率=忙碌时间/总时间
系统吞吐量
系统吞吐量:单位时间内完成作业的数量
系统吞吐量=总共完成了多少道作业/总共花了多少时间
周转时间
周转时间,指作业被提交给系统开始,到作业完成为止的这段时间间隔
周转时间=作业完成时间-作业提交时间
平均周转时间=各作业周转时间之和/作业数
带权周转时间=作业周转时间/作业实际运行的时间=(作业完成时间-作业提交时间)/作业实际运行时间
平均带权周转时间=各作业带权周转时间之和/作业数
等待时间
等待时间:进程/作业处于等待处理状态时间之和
对于进程来说,等待时间是指进程建立后等待被服务的时间之和,等待I/O完成的期间其实进程也是被服务的
等待时间=周转时间-运行时间-I/O操作时间
对于作业来说,不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待的时间
等待时间=周转时间-运行时间
响应时间
响应时间:指从用户请求到首次产生响应所用的时间
调度算法
先来先服务(FCFS)
算法思想
主要从“公平”的角度考虑
算法规则
按照作业/进程到达的先后顺序进行服务
用于作业/进程调度
用于作业调度时,考虑的是哪个作业先到达后备队列;用于进程调度时,考虑的是哪个进程先到达就绪队列
是否可以抢占
非抢占式算法
优缺点
优点:公平,算法实现简单
缺点,对长作业有利,对短作业不利
是否会导致饥饿(某进程/作业长期得不到服务)
不会
短作业优先(SJF)
算法思想
最求最少的平均等待时间,平均周转时间,平均带权周转时间
算法规则
最短的作业/进程优先得到服务(服务时间最短)
用于作业/进程调度
都可用
是否可以抢占
SJF和SPF是非抢占式,但也有抢占式的版本—最短剩余时间优先算法(SRTN)
SRTN:每当有进程加入就绪队列改变时就需要调度,如果新到达的进程的剩余时间比当前运行的进程剩余时间更短,则由新进程抢占处理机,当前运行进程重新回到就绪队列,当一个进程完成时也需要调度
优缺点
优点:“最短的”平均等待时间,平均周转时间
缺点,对短作业有利,对长作业不利,可能产生饥饿现象,作业/进程的运行时间是由用户提供的并不一定真实,不一定能做到真正的短作业优先
是否会导致饥饿(某进程/作业长期得不到服务)
会,如果一直有短作业/进程到来,使得长作业/进程长时间得不到服务,产生饥饿现象,一直得不到服务,则称“饿死”
注意:
- 题目未特别说明,默认是非抢占式的
- 严格的说所有进程同时运行时或所有进程几乎同时到达时,SJF的平均等待时间,平均周转时间最少
- 如果不加上述条件,则应该说抢占式短作业/进程优先调度算法最优
高响应比优先
算法思想
综合考虑
算法规则
计算响应比,选择响应比最高的
用于作业/进程调度
都可用
是否可以抢占
非抢占式
优缺点
综合考虑,不会产生饥饿现象
是否会导致饥饿(某进程/作业长期得不到服务)
不会
响应比=(等待时间+要求服务时间)/要求服务时间
时间片轮转调度算法
算法思想
公平的,轮流的为各个进程服务,让每个进程在一定时间间隔内都可以得到响应
算法规则
按照各进程到达就绪队列的顺序,轮流让各个进程执行一个时间片,若一个进程未处理完,则会剥夺处理机,将进程重新放到就绪队列队尾重新排队
用于作业/进程调度
用于进程调度(只有作业放入内存建立了相应的进程后,才能被分配处理机时间片)
是否可以抢占
抢占式,由时钟装置发出时钟中断
优缺点
优点:
公平,响应块,适合分时操作系统
缺点:
由于高频率的进程切换,因此有一定开销,不区分任务的紧急程度
是否会导致饥饿(某进程/作业长期得不到服务)
不会
如果时间片太大,使得每个进程都可以在一个时间片就完成,则时间片轮转调度算法退化为先来先服务调度算法,并且会增大进程响应时间,因此时间片不能太大
如果时间片太小,会导致进程切换过于频繁,进程会花大量时间来处理进程切换,从而导致实际用于进程执行的时间比例减少,时间片不能太小
优先级调度算法
算法思想
根据任务的紧急程度来决定处理顺序
算法规则
每个作业/进程都有优先级,选择优先级高的
用于作业/进程调度
都可以,也可用于I/O调度中
是否可以抢占
抢占式和非抢占式
优缺点
优点:
用优先级区分紧急程度,适用于实时操作系统,可以灵活的调整对各种作业.进程的偏好程度
缺点:
若源源不断的有高优先级进程进来,可能会导致饥饿
是否会导致饥饿(某进程/作业长期得不到服务)
会
优先级分为静态优先级,动态优先级
静态优先级:创建进程时确定,之后一直不变
动态优先级:创建进程时有一个初始值,之后会根据情况动态地调制优先级
通常:
系统进程优先级高于用户进程
前台进程优先级高于后台进程
操作系统偏好I/O型进程(I/O繁忙型进程)
I/O进程相对的时计算机进程(CPU繁忙型进程)
多级反馈队列调度算法
算法思想
对其他调度算法的折中权衡
算法规则
1.优先级从高到底,时间片从小到大
2.新进程先进入一级,按照FCFS原则若时间片用完还没结束,进程进入下一级队列队尾,若已经在最下级,重新放回最下级队列,被抢占处理机的进程重新放回原队列
3.只有k级队列为空时,才会为k+1对头分配时间片
用于作业/进程调度
进程调度
是否可以抢占
抢占式
优缺点
综合各类算法
是否会导致饥饿(某进程/作业长期得不到服务)
会
多级队列调度算法
系统中按进程类型设置多个队列,进程创建成功后插入某个队列
队列之间可采取固定优先级,或时间片划分
固定优先级:高优先级空时低优先级进程才能被调度
时间片划分:如三个队列分配时间50%,40%,10%
各队列可采用不同的调度策略,如:
系统进程队列采用优先级调度
交互式队列采用RR
批处理队列采用FCFS
进程同步
- 进程具有异步性的特征,异步性指,各并发执行的进程以各自独立的,不可预知的速度向前推进
- 同步亦称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们的工作次序而产生的制约关系,进程间的直接制约关系就是源于他们之间的相互合作
进程互斥
进程的“并发”需要“共享”的支持
两种资源共享方式
- 互斥共享方式
- 同时共享方式
一个时间段内只允许一个进程使用的资源称为临界资源,此外还有许多变量,数据,内存缓冲区等都属于临界资源
对于临界资源的访问,必须互斥地进行,互斥,也称间接制约关系,进程互斥
临界资源的互斥访问,在逻辑上分为四个部分
- 进入区:检查是否可进入临界区,若可进入,需要“上锁”
- 临界区:访问临界资源的那段代码
- 退出区:负责“解锁”
- 剩余区:其余代码部分
需要遵循的原则
- 空闲让进:临界区空闲时,应允许一个进程访问
- 忙则等待:
- 有限等待:在有限的时间进入临界区,保证不会饥饿
- 让权等待:进不了临界区的进程,释放处理机,防止忙等
临界区是进程中访问临界资源的代码段
进入区和退出区是负责实现互斥的代码段
临界区也称为“临界段”
进程互斥的软件实现方法
单标志法
算法思想:两个进程在访问玩临界区后会把使用临界区的权限转交给另一个进程,每个进程进入临界区的权限只能被另一个进程赋予
turn=0
该算法可以实现同一时刻最多只有一个进程访问临界区
主要问题:违背“空闲让进”
双标志先检查法
算法思想:设置一个布尔数组flag【】。数组中各元素用来标志各进程想进入临界区的意愿
主要问题:违背“忙则等待”
原因:进入区的“检查”和上锁,不是一气呵成的
双标志后检查法
算法思想:先上锁后检查
主要问题:空闲让进,有限等待,产生饥饿现象
Peterson算法
算法思想:结合双标志法,单标志法的思想,如果两者都争着想进入临界区,可以谦让
进入区:
- 主动争取
- 主动谦让
- 检查对方是否想使用,是否自己谦让
主要问题:违背“让权等待”
(进不了临界区,一直占用CPU资源)
进程互斥的硬件实现方法
中断屏蔽方法
- 利用“开/关中断指令”实现
- 优点:简单,高效
- 缺点:不适用于多处理机:只适用于操作系统内核进程,不适用于用户进程(开/关中断指令只能运行再内核态,这组指令如果能让用户随意使用会很危险)
TestAndSet指令
简称TS指令,或者TSL指令
TSL指令 是用硬件实现的,执行过程不允许被中断,只能一气呵成
-
优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞,适用于多处理机环境
-
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致忙等
Swap指令(XCHG指令)
与TSL指令类似
进程互斥:锁
- 互斥锁
- acquire()获得锁,release()释放锁,avaliable表示锁是否可用
- 缺点:忙等
- 需要连续忙等的互斥锁称为自旋锁
- 优点:等待期间不用切换进程上下文,多处理器系统中,若上锁的时间短,则等待价很低
- 常用于多处理器系统,一个核忙等,其他核照常工作,并快速释放临界区
- 不太适用于单处理机系统,忙等过程中不可能解锁
信号量机制
- 用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥,进程同步
- 信号量其实就是一个变量(可以是一个整数也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量,eg:系统中只有一个打印机,就可以设置一个初值为1的信号量
- 原语是一种特殊的程序段,执行只能一气呵成,不可被中断,原语是由关中断/开中断指令实现的,软件解决方案的主要问题是由“进入区无法一气呵成执行”,如果进入区和退出区的操作都用“原语”实现,可以避免问题
- 一对原语:wait(S)和signal(S),函数wait,signal,S为信号量
- wait和signal原语常简称为P,V操作P(S),V(S)
整型信号量
- 用一个整数型的变量作为信号量,用来表示系统中某种资源的数量
- 存在问题:不满足让权等待,会发生忙等
记录型信号量
- 结构体(含有剩余资源数量,等待队列)
- block和wakeup
- 可以用记录型信号量实现系统资源的“申请”和“释放”
- 可以用记录型信号量实现进程互斥,进程同步
P(S)–申请一个资源S,如果资源不够就阻塞等待
V(S)–释放一个资源S,如果有进程再等待该资源,则唤醒一个进程
实现进程互斥
- 分析并发进程的关键活动,划定临界区(对临界资源打印机的访问就应放在临界区)
- 设置互斥信号量meutex,初值为1(meutex进入临界区的名额)
- 在进入区P(meutex)–申请资源
- 在退出区V(meutex)–释放资源
- 注意:对对不同的临界资源需要设置不同的信号互斥量
- P,V操作必须成对出现,缺少P就不能保证临界资源的互斥访问,确实V就会导致资源永不被释放,等待进程永不被唤醒
实现进程同步
- 分析什么地方需要实现“同步关系”,必须保证“一前一后”执行的两个操作
- 设置同步信号量S,初始值为0
- 在“前操作”之后执行V(S)
- 在”后操作“之前执行P(S)
- 不同进程之间
消费者问题
-
互斥的P操作一定要在实现同步的P操作之后
-
V操作不会导致进程阻塞,因此两个V操作顺序可以交换
-
生产者,消费者
管程
- 解决信号量机制编程麻烦,易出错的问题
管程的定义
- 一种特殊的软件模块,组成:
- 局部于管程的共享数据结构说明
- 对该数据结构进行操作的一组过程(函数)
- 对局部于管程的共享数据设置初始值语句
- 管程有一个名字
管程的基本特征
- 局部于管程的数据只能被局部于管程的过程所访问
- 一个进程只有通过调用管程内的过程才能进入管程访问共享数据
- 每次仅允许一个进程在管程内执行某个内部过程
引入管程的目的无非就是要更方便地实现进程互斥和同步
-
定义共享数据
-
定义用于访问这些共享数据的入口(函数)
-
通过这些特定的“入口”才能访问共享数据
-
管程中有很多“入口”,但是每次只能开放其中一个“入口”,只能让一个进程或线程进入(注意这种互斥特性是由编译器负责实现,程序员不用关心)
-
可在管程中设置条件变量及等待/唤醒操作以解决同步问题
封装思想
死锁
什么是死锁
- 各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进
死锁,饥饿,死循环的区别
- 死锁:至少是两个进程一起死锁,死锁进程处于阻塞态
- 饥饿:可以只有一个进程机制,饥饿进程可能阻塞也可能就绪
- 死循环:可能只有一个进程发生死循环,死循环的进程可上处理机
- 死锁和饥饿是操作系统解决的问题,死循环是应用程序员要解决的
死锁产生的必要条件
-
互斥条件:对必须互斥使用的资源的争抢才会导致死锁
-
不剥夺条件:进程保持的资源只能主动主动释放,不可强行剥夺
-
请求和保持条件:保持着某些资源不放的同时,请求别的资源,此时请求已经被阻塞,但对自己几有资源保持不放
-
循环等待条件:
-
存在一种进程资源的循环等待
循环等待未必死锁,死锁一定有循环等待
什么时候发生死锁
- 对不可剥夺条件资源的不合理分配,可能导致死锁
死锁的处理策略
- 预防死锁:破坏死锁的四个必要条件
- 避免死锁:避免系统进入不安全状态(银行家算法)
- 死锁的检测和解除:允许死锁发生,系统负责检测出死锁并解除
预防死锁
-
破坏互斥条件
-
将临界资源改造为可共享使用的资源(eg:SPOOLing技术)
缺点:可行性不高,很多时候无法破坏互斥条件
-
破坏不剥夺条件
-
- 申请的资源得不到满足时,立即释放拥有的所有资源
- 申请的资源被其他进程占用时,由操作系统协助剥夺(考虑优先级)
缺点:实现复杂,;剥夺资源可能导致部分工作失效;反复申请和释放导致系统开销大,降低系统吞吐量;可能导致饥饿
-
破坏请求和保持条件
-
采用静态分配方法运行前分配好所有需要的资源,之后一直保持
缺点:资源利用率低;可能导致饥饿
-
破坏循环等待条件
-
采用顺序资源分配法给资源编号,必须按编号从小到大的顺序申请资源
缺点:不方便增加新设备,可能需要重新分配所有编号;使用顺序和编号递增顺序不一致,会导致资源浪费;用户编程麻烦
避免死锁
安全序列:如果系统按照这种序列分配资源,则每个进程都能顺利完成,只要能找出一个安全序列,系统就是安全状态,安全状态可能有多个
如果分配了资源之后,系统找不出任何一个安全序列,系统就进入了不安全状态,可能所有进程都无法顺利的执行下去,当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总要考虑最坏情况
如果系统处于安全状态,就一定不会发生死锁,如果系统进入不安全状态,可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
在分配资源之前预判这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是银行家算法的核心思想
数据结构:
长度m的一维数组Available表示还有多少可用资源
n*m矩阵Max表示各进程对资源的最大需求数
n*m矩阵Allocation表示已经各进程分配了多少资源
Max-Allocation=Need矩阵表示各进程最多还需多少资源
长度为m的一维数组Request表示进程此次申请的各种资源数
银行家算法步骤:
- 检查此次申请是否超过了之前声明的最大需求数
- 检查此时系统剩余的可用资源是否还能满足需求
- 试探着分配,更改各数据结构
- 用安全性算法检查此次分配是否会导致系统进入不安全状态
安全算法步骤:检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收,不断重复,看最终是否否能让所有进程加入安全序列
死锁的检测和解除
如何检测
-
数据结构:资源分配图
-
两种节点
进程节点
资源节点
两种边
进程节点—>资源节点(请求边)
资源节点—>进程节点(分配边)
-
死锁检测算法
-
依次撤销与不阻塞进程相连的边,直到无边可消
所谓不阻塞是指其中申请的资源数还足够的进程
死锁定理:若资源分配图不可完全简化,说明发生了死锁
如何解除
- 资源剥夺法
- 撤销进程法(终止进程)
- 进程回退法