文章目录
进程的概念、组成、特征
进程的概念
- 程序:是静态的,就是个存放在磁盘里的可执行文件,就是一系列的指令集合
- 进程:是动态的,是程序的一次执行过程,如:可同时启动多次QQ程序
- 同一个程序多次执行会对应多个进程
进程的组成—PCB
进程的组成—程序段和数据段
程序是如何运行的?
进程的组成
进程的特征
进程的状态与转换
进程的状态
创建态、就绪态
运行态
阻塞态
终止态
进程状态的切换(五状态模型)
进程的组织
链式方式
索引方式
进程控制
什么是进程控制?
- 进程控制的主要功能是对系统中的所有进程实施有效的管理
- 进程控制具有创建新进程、撤销已有进程、实现进程转换等功能
- 简单来说:进程控制就是要实现进程状态的转换
如何实现进程控制?
- 用原语实现
原语
为什么要用原语
- 原语的执行具有“原子性”,也就是“一气呵成”
- 为了保证进程控制(进程转换)的过程的“一气呵成”
如何实现原语的“原子性”?
- 用 “开中断指令” 和 “关中断指令” 两个特权指令实现原语的原子性
进程控制相关的原语
创建
终止
阻塞和唤醒
切换
进程的运行环境
-
进程上下文(Context)
-
CPU中会设置很多“寄存器”,用来存放程序运行过程中所需要的某些数据
-
在程序的执行过程中,指令会顺序执行,在这个过程中很多的中间结果是放在各种寄存器当中的
-
但是寄存器都是唯一的,另一个进程在运行过程中也会使用各个寄存器,这也就意味着寄存器只能给进程们共用,若没有保存下各个寄存器的值,另一个进程执行过程会把上一进程的结果给覆盖掉,所以在进程切换的时候有必要保存下各个必要的寄存器里面的值,这就是现场的保护
-
解决方法:在进程切换时先在PCB中保存这个进程的运行环境(保存一些必要的寄存器信息),当原来的进程再次投入运行时,可以通过PCB来恢复它的运行环境
进程通信
什么是进程间通信?
- 进程间通信(Inter-Process Communication,IPC)是指两个进程之间产生数据交互
为什么进程通信需要操作系统支持?
共享存储
- 操作系统会分配一个共享内存区域,并映射到各个进程的虚拟地址空间当中
- 要把一个内存区域映射到内存当中,只需要加一个段表项就可以做到
消息传递
直接通信方式
-
在操作系统的内核地址空间,管理着各个进程的PCB,在这些PCB当中包含了一个消息队列(包含着其他进程要给这个进程发送的一些消息,都挂在这个消息队列里面)
-
首先进程P要在自己的地址空间完善消息的信息,包括消息头和消息体等信息
-
进程P会使用操作系统提供的发送原语
send(Q,msg)
,这个原语会导致操作系统内核接收到这个原语并且会把刚刚这个消息msg把它挂到进程Q的消息队列里面(从进程P的用户空间复制到了内核空间)
-
到了进程Q开始运行,它可以使用接受原语
receive(P,&msg)
,操作系统内核会检查进程Q的消息队列,看下哪个消息是由P发过来的,找到之后会把这条消息msg从操作系统的内核区给复制到进程Q的地址空间
间接(信箱)通信方式
-
如果进程P和进程Q要进行系统调用,那么进程P可以通过系统调用,向操作系统申请一个信箱A(当然也可以申请多个信箱B)
-
进程P要在自己的地址空间完善消息体的内容
-
进程P会使用操作系统提供的发送原语
send(A,msg)
,来指明想要发送到A信箱,而且发送的是msg消息体,这个原语会导致操作系统内核接收到这个原语并且会把刚刚这个消息msg把它复制到信箱A里面(从进程P的用户空间复制到了内核空间)
-
到了进程Q开始运行,它可以使用接受原语
receive(A,&msg)
,表示的是进程Q会从A这个信箱接收一个msg消息体,那么信箱A的消息体msg就会被复制到进程Q的地址空间里面
管道通信
- 管道,一种特殊的共享文件,
pipe文件
,类似于循环队列 - 数据的流向只能是
单向的
,不可能是双向同时进行的(除非设置两个管道来实现) - 如果两个进程之间要进行管道通信,首先要通过系统调用的方式来向操作系统申请一个管道文件(实质就是在内存中开辟了一个大小固定的内存缓冲区)然后两个进程可以从这个内存缓冲区里面读数据或者写数据,数据的读写是
先进先出
的FIFO
- 管道通信和共享内存的直接通信方式有什么区别呢?在共享内存的这种同行方式当中,P和Q想怎么读写数据、想怎么分配空间都是
任意的
,没有任何的限制,很自由;但是管道通信的这种方式如下图所示,是一种数据流的形式,在内存中的一系列P发给Q的消息中,并不是Q想读哪里就能读哪里的,因为是FIFO先进先出
,所以Q只能把头部的消息先取了才能依次往后取
线程的概念
什么是线程?为什么要引入线程?
引入线程机制后,有什么变化?
线程的属性
线程的实现方式和多线程模型
线程的实现方式
用户级线程
内核级线程
多线程模型
- 在支持内核级线程的系统中,根据用户级线程和内核级线程的映射关系,可以划分为几种多线程模型
一对一模型
多对一模型
多对多模型
线程的状态与转换(三状态模型)
线程的组织与控制
调度的概念和层次
调度的基本概念
- 当有一堆任务要处理,但由于资源有限,这些事件没法同时处理,此时就需要确定某种规则来决定处理这些任务的顺序,这就是“调度”研究的问题
调度的三个层次
高级调度(作业调度)
中级调度(内存调度)
低级调度(进程调度/处理机调度)
进程的挂起态
- 暂时调到外存等待的进程状态为挂起状态(挂起态,suspend)
- 挂起态又可以进一步细分为就绪挂起、阻塞挂起两种状态
- 注意“挂起”和“阻塞”的区别:两种状态都是暂时不能获得CPU的服务,但是挂起态是将进程映像调到外存去了,而阻塞态下进程映像还在内存中
- 有的操作系统会把就绪挂起、阻塞挂起分为两个挂起队列,甚至会根据阻塞原因不同再把阻塞挂起进程进一步细分为多个队列
进程的七状态模型
三层调度的联系和对比
进程调度的时机
进程调度的时机
内核程序临界区v.s.临界区
进程调度的方式
进程调度的切换与过程
调度器和闲逛进程
调度器/调度程序(scheduler)
闲逛进程
调度算法的评价指标
CPU利用率
吞吐量
周转时间
等待时间
响应时间
调度算法
先来先服务 FCFS
- First Come First Serve
- 对长作业/长进程有利,对短作业/短进程不利
短作业优先 SJF
- Shortest Process First
- 对短作业/短进程有利,对长作业/长进程不利
高响应比优先 HRRN
- Highest Response Ratio Next
时间片轮转调度算法 RR
- Round-Robin
优先级调度算法
多级反馈队列调度算法
- 设置多级就绪队列,各级队列优先级从高到低,时间片从小到大,优先级越高的队列时间片越小,优先级越低的队列时间片越大
- 新进程到达时先进入第1级队列,按先来先服务FCFS原则排队等待被分配时间片。若用完时间片进程还未结束,则进程进入下一级队列队尾。如果此时已经在最下级的队列,则重新放回最下级队列队尾
- 只有第k级队列为空时才会为k+1级队头的进程分配时间片
- 被抢占处理机的进程重新放回原队列队尾
- 在0这个时刻,P1先到达,所以会放入第1级队列,由于先到达而且没有其他的进程,所以P1理应分配一个时间片,当P1执行了1个单位的时间片之后,时间片用完还没有结束,P1会进入第2级队列的队尾
- 当P1执行完第一个时间片之后,在1这个时刻,P2刚好到达,由于此时更高级的队列还有未处理的进程,所以暂时不会处理更低级别的队列中的进程,因此在1这个时刻会选择P2让它上处理机运行,P2运行1个单位的时间片大小后会被放到下一级队列的队尾
- 接下来在2这个时刻,由于第1级的队列已经为空了,所以我们会为第2级的队列分配时间片,此时第二级队列的时间片大小是2,所以P1会执行2个单位的时间,那么执行完之后P1的任务还没有执行结束,那么P1会被放到第3级队列的队尾,此时第2级队列还有进程,所以会让P2进处理机运行
- 但是P2在还没有用完2个时间片的情况下(P2刚执行了1个时间片的时间),在5这个时刻,P3进程到达第1级队列,由于此时有个更高优先级的进程到达,所以会发生抢占处理机的情况,P2进程会被剥夺处理机,但是并不是放到下一级队列,而是把它放到原来这一级队列的队尾,之后让P3抢占处理机让它运行,运行完1个单位的时间之后,P3完成,被调出内存
- 接下来又是P2继续运行,之前P2运行了2个单位的时间(P2总共需要4各单位的时间),所以这次P2上处理机运行的时候,P2只需要运行完这2个单位的时间片就可以完成并被调出内存
- 最后再让P1上处理机运行4个单位的时间,运行完P1还差1个单位的运行时间,并且P1已经在最下面一级了,所以P1还会被放到最下面一级队列的队尾然后再次被调度,再上处理机运行1个单位的时间,完成任务并被调出内存
多级队列调度算法
进程同步&进程互斥
什么是进程同步
-
保证进程之间的推进顺序是按照我们想要的那种顺序依次推进的
-
同步又称直接制约关系,它是指为完成某种任务而建立的两个或者多个进程,这些进程因为需要在某些位置上协调工作次序而产生的制约关系
-
进程间的直接制约关系就是源于它们之间的相互合作
什么是进程互斥
- 进入区:负责检查此时能不能来访问这个临界资源,可以的话对其“上锁”,设置正在访问临界资源的标志
- 临界区:访问临界资源的那段代码,也称“临界段”
- 退出区:负责解除正在访问临界资源的标志,即“解锁”
- 剩余区:做其他处理
进程互斥的软件实现方法
为什么要进行进程互斥
单标志法
双标志先检查法
双标志后检查法
Peterson 算法
进程互斥的硬件实现方法
中断屏蔽方法
TestAndSet 指令
- TestAndSet,TS
- TestAndSetLock,TSL
- 硬件实现,执行过程中不允许被中断
Swap 指令
- 又称
Exchange 指令
、XCHG 指令
- 硬件实现,执行过程中不允许被中断
互斥锁
- 锁是用于实现互斥的一种方法
- 可以把锁简单理解为一个布尔型的变量,只有true/false或者1/0两种状态,分别表示当前已上锁或者没有上锁
信号量机制
整型信号量
记录型信号量
用信号量实现进程互斥|同步|前驱关系
信号量机制实现进程互斥
信号量机制实现进程同步
信号量机制实现前驱关系
生产者消费者问题
问题分析
如何实现
能否改变相邻P|V操作的顺序
- 如果不处理互斥问题,可能在多生产者时出现数据覆盖的问题
- 实现互斥的P操作一定要在实现同步的P操作之后,否则可能会导致死锁问题
- 如果把消费者是用产品的代码带到临界区,不会出现问题,但是会导致临界区的代码变得很冗长,不利于各个进程之间交替地使用临界区,对系统地效能产生影响
多生产者-多消费者问题
问题描述
问题分析
如何实现
吸烟者问题
问题描述
如何实现
读者-写者问题
问题描述
问题分析
如何实现
瑕疵版(读优先)
- 仿照此时读者的写法照搬给写者可以构造出写优先
完整版(读写公平)
哲学家进餐问题
问题描述
问题分析
如何实现
- 主要的问题就是避免死锁的发生
管程
为什么要引入管程
管程的定义和基本特征
- 管程和PV操作一样,也是用来实现进程的互斥和同步的,而进程之间之所以要实现互斥和同步就是因为进程之间可能会共享某些数据和资源,要实现进程对共享资源的互斥或同步的访问
- 管程的定义,有点像Java中的
类
拓展1:用管程解决生产者消费者问题
拓展2:Java中类似于管程的机制—synchronized关键字
死锁的概念
什么是死锁
死锁 | 饥饿 | 死循环 的区别
死锁产生的必要条件
什么时候会发生死锁
死锁的处理策略
预防死锁
破坏互斥条件
破坏不剥夺条件
破坏请求和保持条件
破坏循环等待条件
避免死锁(银行家算法)
什么是安全序列
安全序列 | 不安全状态 | 死锁 的关系
银行家算法
死锁的检测和解除
死锁的检测
死锁的解除