🚀 作者 :“码上有前”
🚀 文章简介 :考研--操作系统
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬
- 定义
- PCB是进程存在的唯一标志。
- 进程是一个程序执行的过程
- 进程是一个程序及数据在处理机上顺序执行所发生的活动
- 进程是具有独立功能的程序在数据集合上运行的过程
- 进程是进程实体的运行过程,是系统进行资源分配和调度的独立单位。
- 组成
- PCB,程序段,数据段三部分组成了进程实体。
- PCB的组成
- 进程描述信息:进程标识PID,用户标识UID
- 进程控制和管理信息:进程当前状态,进程优先级
- 资源分配清单:程序段指针,数据段指针,键盘,鼠标等
- 处理机相关信息:各种寄存器值
- 进程组织方式
- 链接方式:按照进程状态将PCB分为多个队列,OS持有指向各个队列的指针
- 索引方式:根据进程状态的不同,建立几张索引表,OS持有各个索引表的指针。
- 特征
- 动态性,并发性,独立性,异步性
- 结构性:每个进程都会配置一个PCB。结构上看,进程由程序段,数据段,PCB组成。
- 进程的状态
- 运行状态:CPU,其他所需资源都有
- 就绪状态:CPU无,其他资源有
- 阻塞状态:CPU无,其他资源无
- 创建状态:操作系统为新进程分配资源,创建PCB
- 终止状态:操作系统回收进程的资源,撤销PCB
- 进程转换
- 就绪态-->运行态:进程被调度
- 运行态-->就绪态:时间片到,或被CPU或者其他高优先级的进程抢占
- 运行态-->阻塞态:等待资源分配,或者等待某事发生
- 阻塞态-->运行态:资源分配到位,等待事件发生
- 进程控制
- 如何实现进程控制?原语!
- 进程控制相关的原语
- 进程的创建,终止,阻塞,唤醒,切换
- 进程通信
- 共享存储
- 概念:两个进程对共享空间的访问必须是互斥的
- 基于数据结构的共享:只能存放同一数据结构的数据,低级的通信方式
- 基于存储区的共享:在内存中画出一片共享存储区,数据的形式存放位置由进程控制,而不是操作系统,高级的一种方式
- 消息传递
- 概念:进程间的数据交换以格式化的消息为单位,通过发送消息/接受消息两个原语进行数据交换
- 直接通信
- 直接把消息挂载到接受进程的消息缓冲队列上
- 间接通信
- 消息要先发送到中间实体(信箱)中,因此称为‘消息通信方式’
- 管道通信
- 类似于缓冲区,一个管道只能实现半双工的通信,某一时间段内只能实现单向的传输,如果要实现双向同时通信,则需要设置两个管道。各个进程都需要互斥的访问管道
- 进程一写满进程二才能读数据,以字符流的形式写。没有写满的话是不允许读的,没有读空的话是不允许写的。
- 共享存储
- 线程,多线程模型
- 概念:有些进程也需要执行很多事情,传统的进程只能执行一系列程序,为此,引入线程来增加并发度。
- 引进之后的优点
- 引入线程之后,线程成为程序执行的最小单位,调度的最小单位。可以理解为‘轻量级的进程’。
- 线程是一个基本的CPU执行单元,也是程序执行流的最小单位。各线程之间的并发,进一步的提升了系统的并发度。引入之后,进程只作为除CPU之外的系统资源分配单元。
- 引入之后,传统的进程间并发,需要切换进程的运行环境,开销很大,而线程间并发,如果是同一进程内线程切换,则不需要切换进程环境,系统开销很小。引入线程之后,并发所带来的系统开销减少。
- 线程的实现方式
- 用户级线程:由应用程序通过线程库实现,所有的线程管理工作都由应用程序负责,用户级线程中,线程切换可以在用户态下即可完成,无需操作系统干预。其实就是用户的视角可以看到的线程。
- 内核级线程:线程的切换,调度要由内核完成,因此内核级线程的切换必然需要在核心态下才能完成
- 多线程模型
- 内核级线程才是处理机分配的单位
- 多对一
- 多个用户级线程映射到一个内核级线程。
- 优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高,
- 缺点:当一个用户级线程被阻塞是,整个进程都会被阻塞,并发度并不高。多个进程不可在多核处理机上并行运行。
- 一对一
- 一个用户级线程对一个内核级线程
- 优点:当一个线程被阻塞后,别的线程并不影响,并发能力强。多核可在多核处理机上运行
- 缺点:进程切换开销大
- 多对多
- 集两者之长
- 线程的重要属性
- 线程是处理机调度的单位
- 多CPU计算机中,各个线程可占用不同的CPU
- 每个线程都有线程ID,线程控制块
- 线程也有就绪,阻塞,运行三种基本状态
- 线程几乎不拥有系统资源
- 同一进程的不同线程共享线程资源
- 由于共享内存地址空间,同一进程中的线程见通信甚至不需要系统干预
- 同一进程中的线程切换,不会引起进程切换
- 不同进程的线程切换,会引起进程切换
- 切换同进程内的线程,系统开销很小
- 切换进程,系统开销很大
- 处理机调度
- 基本概念:需要确定某种规则来决定处理这些任务执行的顺序。
- 三个层次
- 高级调度(作业调度)
- 概念:由于内存空间有限,有时无法将用户提交的作业完全放入内存,因此需要某种规则来将作业调入内存。外存与内存之间的调度,每个作业只调入一次,调出一次,作业调入时会创建对应的PCB,作业调出时撤销PCB,只要值调入的问题。
- 中级调度(内存调度)
- 概念:把暂时不运行的进程调至外存等待,等他重新具备了运行条件且内存又稍有空闲时,再将其调入内存。这么做的目的是为了提高内存空间的利用率和系统吞吐量。这种暂时调用到外存的状态称为挂起状态,PCB不一定会调用到外存,而是会常驻内存中。PCB会记录进程数据在外存中的存放位置,进程等状态信息。被挂起的进程会被放到挂起的队列中。
- 七状态模型:挂起状态又可以细分为就绪挂起,阻塞挂起两种状态。运行态阻塞态就绪态都可以转换到挂起态。
- 低级调度(进程调度)
- 频率很高,几十毫秒执行一次
- 高级调度(作业调度)
- 三层调度的联系与对比
- 要做什么?
- 高级调度:将合适的队列从外存调入到内存,并为其创建进程。
- 中级调度:从挂起队列中选择合适的进程将其数据调回内存
- 低级调度:按照某种规则,从就绪队列中选择一个进程为其分配处理机。
- 调度发生场景
- 高级:外存-->内存,面向作业
- 中级:外存-->内存,面向进程
- 低级:内存-->CPU内
- 频率
- 高级:最低
- 中级:中等
- 低级:最高
- 要做什么?
- 对进程状态的影响
- 高级:无-->创建态-->就绪态
- 中级:挂起态-->就绪态(阻塞挂起-->阻塞态)
- 低级:就绪态-->运行态
- 进程调度的时机与方式
- 时机:
- 当前进程主动放弃
- 正常进程终止
- 运行过程中发生异常而终止
- 进程主动请求阻塞
- 当前进程被动放弃
- 分给进程的时间片用完了
- 有更紧急的事需要处理
- 有更高优先级的进程进入就绪队列
- 中断时不能被切换,原子操作也不能被切换,在内核临界区不能被调度
- 当前进程主动放弃
- 进程调度的方式
- 抢占式:当有一个进程正在处理机上执行时,如果有一个更加重要的更紧急的进程要使用处理机,则立即暂停正在执行的程序,将处理机分配给更重要紧迫的进程。
- 非抢占式:只允许进城主动放弃处理机,或者直到该进程主动放弃或终止进程,进去阻塞
- 进程调度和切换是有代价的,并不是说越频繁的调度并发性就越高
- 时机:
- 调度算法的评价指标
- CPU利用率
- 概念:昂贵,人们希望CPU竟可能的工作。
- 公式: 利用率 = 忙碌的时间/总时间
- 系统吞吐量:单位时间内完成了多少作业
- 周转时间(平均周转时间,带权周转时间,平均带权周转时间)
- 周转时间指:从作业被提交到系统开始,到作业完成为止的这段时间间隔。
- 公式:作业完成时间-作业到达时间
- 平均周转时间 = 各个作业周转时间之和/作业数
- 带权周转时间
- 有的作业时间比较长,有的比较短,因此在周转时间相同的情况下,运行时间不同的作业,给人的感受不一样。
- 公式:作业周转时间 / 作业实际运行的时间 = (作业完成时间-作业提交时间)/ 作业实际运行的时间
- 意义:在实际运行时间相同的两个作业,周转时间短的带权周转时间更小,用户满意度更高。且带权周转时间必定大于等于1。
- 问题:假如某个应用很快就能被完成,但是缺需要很久的周转时间,那么这个进程或者作业的体验就很差,
- 等待时间
- 用户希望自己的作业尽可能的少的等待处理机
- 指作业处于等待处理机状态时间之和,等待事件越长,用户满意度越低。
- 公式:作业完成时间 - 作业运行事件
- 响应时间
- 指用户提交请求到首次产生相应所用的时间
- CPU利用率
- 调度算法
- 需要考虑算法的思想,规则,应用场景,优缺点,抢占式还是非抢占式,是否饥饿
- 饥饿:一个作业或者服务长期得不到进程
- 先来先服务FCFS
- 思想:公平角度出发
- 规则:先来先服务
- 场景:用于作业调度时,考虑哪个作业到达后备队列,用于进程调度时,考虑是哪个进程先到达就绪队列。
- 是否抢占:非抢占式(要公平)
- 优点:公平,简单实现
- 缺点:排在长作业后的短作业,需要等待很久的时间才会被安排服务,以至于带权周转时间很大,因此对长作业有利,短时间作业很不利
- 是否饥饿:不会,因为排队来完成作业的。
- 短作业优先SJF
- 思想:追求最短的平均等待事件,最少的平均周转时间,最少的平均带权周转时间
- 规则:服务时间最短的(运行时间)作业优先服务
- 场景:用于作业调度也用于进程调度
- 是否抢占:分为抢占式和非抢占式
- 是否饥饿:抢占式饥饿
- 优点:等待时间,平均等待时间,带权周转时间,平均带权周转时间最少。相比于其他算法
- 缺点:不公平,对短作业有利,对长作业不利。可能产生饥饿
- 高响应比优先HRRN
- 算法思想:考虑作业/进程的等待时间和要求服务时间
- 算法规则:计算各个作业/进程的响应比,选择响应比更高的作业
- 公式:等待时间+要求服务时间 /要求服务时间
- 场景:作业调度也可,进程调度也可
- 是否抢占:非抢占式
- 优点:综合考虑了等待时间和运行时间。等待时间相同时,要求服务时间短的优先(SJF优点)。运行时间相同时,等待长的优先(FCFS的优点),对于长作业来说,随着等待时间越长,其响应比也会越来越大,从而
- 缺点:
- 是否饥饿:不会
- 时间片伦旭调度算法
- 优先级调度算法
- 多级反馈队列调度算法
- 进程同步与互斥
- 同步:并发性带来异步性,有时通过进程同步来解决这种异步问题
- 异步
- 概念:对临界资源访问时,需要互斥的进行。即同一段时间只能允许一个进程访问。、
- 组成
- 进入区:检查是否可以进入,若可进入,需要上锁
- 临界区:访问临界资源的那段代码
- 退出区:负责‘解锁
- 剩余区:其他代码
- 需要遵循的原则
- 空闲让进
- 忙则等待
- 有限等待:要在有限的时间内进入临界区
- 让权等待:进不了临界区的进程,要释放进程,防止忙等
- 进程互斥的软件实现方法
- 单标志法
- 双标志先检查
- 双标志后检查
- Peterson算法
- 进程互斥的硬件实现方法
- 中断屏蔽方法
- TestAndSet(TS指令/TSL指令)
- Swap指令
- 信号量
- 遗留问题
- 在双标志检查法中,进入区的检查,上锁无法一起呵成,从而导致两个进程之间可能同时进入临界区的问题。
- 所有的解决方案都无法实现让权等待
- 解决:用户进程可以通过操作系统提供的一对原语(P和V)来对信号量进行操作,从而方便的实现进程互斥
- 信号量机制
- 概念:其实就是一个变量,可以用来表示系统中某种资源的数量。
- 原语:一种特殊的数据段,只能一起呵成的执行,不可被中断。
- 整型信号量:用一个整数表示系统中某种资源的数量
- Singal原语:void singal(int S){S=S+1}
- Wait原语
- void wait(int S){while(S<=0){S= S-1}}
- 如果数量不够,会一直占用处理机,忙等状态,不满足让权等待原则
- 记录性信号量
- 为了解决上述问题,使用记录型数据结构表示信号量。添加等待队列,进行一次P操作,当S的值<0时,程序调用Block原语进行自我阻塞(运行态进入阻塞态),主动放弃处理机,并插入该类资源的等待队列中,可见,该机制遵循让权等待,不会出现忙等
- 进行一次V操作,表示进程释放一个单位的资源,S值+1,若加+1之后仍是小于等于0,表示依然有进程在等待该类资源,因此调用wakeUp原语唤醒等待队列中的第一个进程(阻塞态-->就绪态)
- 信号量机制实现进程互斥
- 进程互斥(前P后V)
- 分析问题,确定临界区
- 设置信号量,初值为1
- 临界区之前对信号量执行P操作
- 临界区之后对信号量进行V操作
- 进程同步(前V后P)
- 分析问题,找出一前一后的操作
- 设置同步信号量,初始值为0
- 前操作之后进行V操作
- 后操作之前执行P操作
- 进程的前驱关系
- 前V后P
- 进程互斥(前P后V)
- 生产者消费者问题
- 生产满不能再生产,生产者阻塞,消费者可以消费
- 没有产品不能消费,阻塞。当生产者生产产品唤醒消费
- 问题:后生产者覆盖前生产者生产的内容,缓存区是邻接资源,各个进程必须互斥地访问
- 找到关系
- 互斥信号量:实现缓冲区的互斥访问
- 同步信号量:表示空闲缓冲区的数量
- 同步信号量:表示产品的数量,即非空缓冲区的数量
- 多生产者多消费者问题
- 父母小孩出水果问题
- 生产者1父亲:生产苹果。生产者2母亲:生产橘子
- 消费者1女儿:吃苹果。消费者2儿子:吃橘子
- 缓冲区:果盘,大小为1
- 吸烟者问题
- 生产者1:提供每次将两种材料放在桌子上,轮流让这三个消费者抽到烟。
- 消费者1: 需要胶水和纸。消费者2:需要烟草和胶水
- 消费者3:需要烟草和纸。缓冲区:桌子,大小为1
- 同步问题:桌子上有对应组合时-->对应的抽烟者进行抽烟(一前一后的关系)前V后P
- 互斥问题
- 读者写者问题
- 哲学家进餐问题
- 五个哲学家进程,与相邻的哲学家访问筷子形成互斥的关系
- 每个进程与之前问题不同的是:需要访问两个临界资源。而在访问的过程中如果避免临界资源分配不当造成死锁现象这是哲学家问题的精髓。
- 过程
- semaphore chopstick[5] = {1,1,1,1,1}
- while(1)
- P(左边筷子)//申请占用左边的筷子,P(右边筷子)
- 吃饭,V(左边筷子)//释放资源,V(右边筷子)
- 死锁解决:再使用一个互斥信号量表示在吃饭之前不能被取走筷子,从而保证能有效的依次完成每次吃饭。即有人在吃饭的时候,其他的想吃饭的会被阻塞
- 遗留问题
- 管程
- 为什么引入管程:解决信号量机制编码麻烦,易出错的问题
- 管程的定义和基本特征
- 组成
- 局部于管程的共享数据结构
- 对该数据结构进行操作的一组过程(函数)
- 对局部于管程的共享数据设置初始值的语句
- 管程有一个名字,每个管程类似于一个类
- 基本特征
- 局部于管程的数据只能被局部于管程的过程所访问(封装)
- 一个进程只能通过调用管程内的过程才能进入管程访问共享数据(私有变量)
- 每次仅仅一个进程在管程内执行某个内部过程
- 组成
- 死锁
- 定义:在并发的环境下,各个进程因竞争资源,导致各个竞争发生阻塞,都无法向前推进的现象,称为死锁。
- 进程死锁,饥饿,死循环的区别
- 死锁:各个进程都在等待对方手里的资源,而导致各个进程都阻塞,进程无法向前推进的现象。至少是两个及以上的进程同时发生死锁。
- 饥饿:由于长期得不到资源,进程无法向前推进的现象,如在进程调度算法中的短进程优先算法中,长进程就会出现饥饿的现象,可能只有一个进程发生饥饿
- 死循环:在程序运行过程中一直跳脱不出某个循环,人为,管理者的问题。
- 死锁产生的必要条件
- 互斥条件:只有对必须互斥的资源进行竞争才会导致死锁,如哲学家的筷子,而内存扬声器可以同时被共享的资源是不会发生死锁的
- 不可剥夺:进程所获得的资源在未被使用完之前,不能由其他进程强行剥夺,只能主动释放,哲学家问题中的筷子是不能被抢走的
- 请求和保持条件:**进程已经保持了至少一个资源,但又提出新的资源请求,而该资源又被其他进程占用,此时请求被阻塞,但又对自己已有的资源保持不放**。如哲学家问题中手里握着左边的筷子不放,又想要别人手里的筷子作为右边的筷子。
- 循环等待:死锁的充分不必要条件,如哲学家问题中的出现第六个人
- 什么时候会发生死锁:对互斥资源的竞争以及进程推进的不当,以及信号量的使用不当
- 死锁的处理策略
- 预防死锁(静态策略):破坏死锁的四个条件
- 破坏互斥条件:如SPOOL技术使得将独占设备变成逻辑上共享的设备,但是有些设备也必须保持互斥,因此这个策略作用效果局限
- 破坏不剥夺条件:强行剥夺,中断,优先级更高等。缺点就是实现复杂
- 破坏请求和保持:在进程运行之前给其分配全部资源,否则不被运行。缺点:对于有些资源需要用很短时间如果进程的整个运行过程都一直保持,就会造成资源的严重浪费,资源利用率低,也可能导致饥饿
- 破坏循环等待:可采用顺序资源分配法,将资源进行编号规定每个进程必须按照编号递增的顺序进行请求资源。缺点:不方便增加新的设备,进程实际使用编号不同造成资源浪费
- 避免死锁(动态策略)
- 安全序列:不唯一,不会阻塞的序列,系统出去安全状态。安全状态一定不会发生死锁,不安全状态就可能发生过死锁
- 不安全序列:在每次操作之前判断是否进入不安全状态
- 避免系统进入不安全状态--银行家算法
- 死锁的检测和解除(允许死锁发生):定义一种数据结构,消除所有的边。
- 资源剥夺法:挂起某些死锁进程,并抢占他们的资源,将这些资源分配给其他资源
- 撤销进程法:强制撤销部分,甚至全部死锁资源,并剥夺这些进程的资源。优点实现简单,但是代价很大,因为有些资源可能运行很久接近尾声,一旦撤销就得重头再来
- 进程回退法:回退在没有到达死锁的方法。缺点:不太容易实现,需要设置还原点
- 预防死锁(静态策略):破坏死锁的四个条件