在多道程序环境下,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性及不可再现性的特征。为引入了进程的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性。(最基本的两个特征)
✨✨
为了让参与并发执行的每个程序都能独立运行,必须为之配备一个专门的数据结构,称为进程控制块(Process Control Block, PCB)系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程。
程序段、相关数据、PCB三部分构成了进程实体。所谓的创建进程,实质上是创建进程实体中的PCB;而撤销进程,实质上是撤销进程的PCB。进程映像是静态的,进程则是动态的。
注意:PCB是进程存在的唯一标志!
进程的定义:
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
进程是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。
进程在生命周期内,由于系统中的进程之间的相互制约及运行环境的变化,使得进程状态也在不断地发生变化。通常进程的状态有以下5种状态,前3种是进程的基本状态。
就绪态和等待态的区别:
就绪态是指进程仅缺少处理器,只要获得处理机资源就立即运行;而等待态是指进程需要其他资源(除处理机)或等待某一事件。
3种基本状态之间的转换如下:
一个进程从运行态变成阻塞态是主动的行为,而从阻塞态变为就绪态是被动的行为。
注意:不能由阻塞态直接转换为运行态,也不能由就绪态转换为阻塞态
进程是一个独立的运行状态,也是操作系统进行资源分配和调度的基本单位。它由进程控制块(PCB)、程序段、数据段。
PCB 是进程实体的一部分,是进程存在的唯一标志。
各个部分的说明如下:
进程描述信息
进程控制和管理信息
资源分配清单
用于说明有关内存地址空间或虚拟地址空间的状态,所打开文件的列表和所使用的输入、输出设备信息
处理机相关信息
也称处理机上下文,主要指处理机中各寄存器的值。当进程处于执行状态时,处理机的许多信息都存在寄存器中。当进程被切换时,处理机状态都必须保存在相应的PCB中,以便在进程重新执行时,能从断点继续执行
程序段就是能被进程调度程序调度到的CPU执行的程序代码。注意:程序可被多个进程共享,即多个进程可以运行一个程序
一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。
因为进程的管理,例如将进程从阻塞态到就绪态等都需要多条指令才能完成一次进程管理的全部操作。而若在进程管理操作的过程中出现中断,系统就很容易出现错误甚至奔溃。因此需要借助原语实现对进程的管理。
原语的执行具有原子性,即执行过程中只能一气呵成,期间不允许被中断。可以用“关中断指令”和“开中断指令”这两个特权指令实现原子性。
CPU执行了关中断指令,就不再例行检查中断信号,知道开中断指令之后才会恢复检查中断。
进程的创建过程如下(创建原语):
引起创建的事件:
终止进程的过程如下(终止原语;就绪态/阻塞态/运行态->终止态):
引起进程终止的事件:
进程的阻塞
当正在执行的进程,由于某些事件未发生,如系统资源失败、等待某种操作的完成、新数据尚未到达或无新任务可做等,进程便通过阻塞原语(Block),使自己由运行态变为阻塞态。。由此可见,阻塞是进程吱声的一种主动行为,进程阻塞的原语执行如下(运行态->阻塞态):
引起进程阻塞的事件有:
进程的唤醒
当被阻塞的进程所等待的事件完成时,如它所等待的I/O操作已完成或其他所期待的数据到达,由有关进程调用唤醒原语(Wakeup),将等待该事件的进程唤醒。唤醒原语如下:
引起进程唤醒的事件有:
Block原语和Wakeup原语是一对作用刚好相反的原语,必须成对使用。否则当只调用了Block原语而不使用Wakeup原语的时候,阻塞进程将不能被唤醒而永远处于阻塞状态。
进程切换原语(运行态->就绪态、就绪态->运行态):
引起进程切换的事件:
进程间的通信(Inter-Process Communication, IPC)是指两个进程之间产生数据交互。
共享存储分为两种:低级方式的共享是基于数据结构的共享;高级方式的的共享则是基于存储区的共享。在对共享空间进行读/写操作时,需要使用同步互斥工具(如P操作、V操作)
基于存储区的共享:
操作系统在内存中划分出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式。(通过同步)
基于数据结构的共享:
比如共享空间里只能放一个长度为10 的数组。这种共享方式速度慢、限制多,是一种低级的通信方式。
在消息传递系统中,进程间的数据交换以格式化的消息(Message)为单位。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。
直接通信方式
发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。
间接通信方式
发送进程把消息发送到某个中间实体,接收进程从中间实体取得消息。这种实体一般称为信箱。
直接通信和间接通信的方式,最大的区别就是接收方是否要维护一个消息队列。间接通信以信箱的方式实现将消息暂存在信箱中,而直接通信则将收到的消息暂存在消息队列中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jD0GQGan-1680006302656)(https://raw.githubusercontent.com/yuyuyu258963/pic-go-picStore/main/%E5%BC%95%E5%85%A5%E7%BA%BF%E7%A8%8B%E5%B8%A6%E6%9D%A5%E7%9A%84%E5%8F%98%E5%8C%96.png)]
引入线程的目的是更好地使用多道程序并发执行,提高资源利用率和系统吞吐量;引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
进程最直接的理解就是“轻量级进程”
,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分配的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可以和同一个进程的其他线程共享进程所拥有的全部资源。线程也有就绪、阻塞、执行三种基本状态。
引入进程后,进程的内涵发生了改变,进程只作为除了CPU外的系统资源分配单元,而线程则作为处理机的分配单元。由于一个进程内部有多个线程,若线程的切换发生在同一个进程内部,则只需要很少的时空开销。
因为进程的资源被线程所共享,要是仅切换线程的话,计算机中的很多资源(各种寄存器:IR、PC、各种通用寄存器)都不需要重新切换保存。大大节省了进程切换时的上下文信息的保存所耗费的时间。
线程的实现可以分为两类:用户级进程(User-Level Thread,ULT)和内核级线程(Kernel-Level Thread,KLT)。内核级线程又称内核支持的线程。
在用户级线程中,有关线程管理的所有工作都由应用程序在用户空间中完成,内核意识不到线程的存在。应用程序通过使用线程库设计成多线程程序。
用户级线程的特点:
优点
用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理开销小,效率高
线程的实现与操作系统平台无关
缺点
当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可再多核处理机上并行(区别于并发,再同一时刻运行)运行。
例如下面这段代码就可以模拟进程实现最简答的线程调度:
int main(){
int i = 0;
while (true) {
if(i==0){printf("处理视频聊天");}
if(i==1){printf("处理文字聊天");}
if(i==2){printf("处理文件传输");}
i = (i + 1)%3;
}
}
// QQ进程
历史背景:早期的操作系统(如:Unix)只支持进程,不支持线程,当时的“线程”是由线程库实现的
内核级线程(KLT)。在操作系统中,无论是系统进程还是用户进程,都是在操作系统内核的支持下运行的,与内核紧密相关。
内核级进程的特点:
优点:
缺点:
一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大
在组合实现方式中,内核支持多个内核级线程的建立、调度和管理,同时允许用户程序建立、调度和管理用户级线程。
由于用户级线程和内核级线程连接方式的不同,形成了以下三种不同的多线程模型。
多对多模型
将n个用户级线程映射到m个内核级线程上,要求n>=m
多对一模型
多个用户级线程映射到一个内核级线程。
一对一模型
将每个用户级线程映射到一个内核进程。
线程的三个基础状态和进程的基础状态转化一样
线程的组织就是将多个线程记录到的同一个线程表中。组织方式可以按照一个进程一个线程表、按照运行状态对应一个线程表等。
处理机的调度是多道程序操作系统的基础,是操作系统设计的核心问题
高级调度(作业调度)
按照一定的原则从外村上处于后备队列的作业中挑选一个(或多个),给他们分配内存、输入/输出设备等必要资源,并建立相应的进程,以使它们获得竞争处理机的权力。间而言之作业的调度就是内存和辅存之间的调度。每个作业只调入一次、调出一次。作业调入时会创立PCB,调出时会撤销PCB。
作业:可以理解为一个具体的任务。比如用户向系统提交一个作业 = 用户让操作系统启动一个程序
中级调度(内存调度)
引入中级调度的目的是提高内存利用率和系统吞吐量。内存不够时,将某些进程的数据调出外村。等待内存空闲或进程运行时再重新调入内存。暂时调到外村等待的进程状态为挂起状态。被挂起的进程PCB会被组织成挂起队列。
按照某种策略决定将哪个处于挂起状态的进程重新调入内存。一个进程可能被多次调出、调入内存,因此中级调度发生的概率比高级调度更高。
低级调度(进程调度)
低级调度(处理机调度/处理机调度)——按照某种策略从就绪队列中选取一个进程,将处理机分配给它
进程调度是操作系统中最基本的一种调度,在一般操作系统中都必须配置进程调度。进程调度的频率很高,一般几十毫秒一次。
暂时调到外村等待的进程状态为挂起态(suspend)
挂起状态又可以进一步细分为就绪挂起、阻塞挂起两种状态
进程调度(低级调度)就是按照某种算法从就绪队列中选择一个进程为其分配处理机
调度程序是操作系统的内核程序。请求调度的事件发生后,才能进行调度程序,调度了新的就绪进程后,才会进程进程的切换。但是在某些时刻发生了引起进程调度的因素,不能马上进行调度和切换。不能调度和切换的情况有以下三种:
在处理中断的过程中。中断处理处理复杂,与硬件密切相关,很难做到在中断处理过程中进行进程切换。
进程在操作系统内核程序临界区中
❌进程处于临界区时不能进行处理机调度
临界资源:一个时间段内只允许一个进程使用的资源。各进程需要互斥地访问临界资源。
临界区:访问临界资源的那段代码
内核临界区:一般是用来访问某种内核数据结构的,比如进程的就绪队列
在原子操作过程中(原语)。原子操作不可中断,要一气呵成
应该进行进程调度和转化的情况如下:
非剥夺调度方式(非抢占式),即只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理结果,知道该进程终止或主动要求进入阻塞态
特点:
实现简单,系统开销小但是无法及时处理紧急任务,适合早期的批处理系统
剥夺式调度(抢占式)。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的程序,将处理机分配给更重要紧迫的那个程序
特点:
可以优先处理更紧急的进程,也可以实现让个进程按时间片轮流执行的功能(通过时钟中断)。适合分时操作系统、实时操作系统。
狭义的进程调度
与进程切换
的区别:
狭义的进程调度指的是从就绪队列中选一个要运行的程序;
进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程
广义的进程调度
包含了选择一个进程和切换两个步骤
进程的切换过程主要完成了:
✨注意:进程的切换是有代价的,因此如果过于频繁地进行进程调度、切换必然会使整个系统的效率降低,使系统大部分时间都花在了进程切换上,而真正用于执行进程的时间减少
用于分派CPU的组件称为调度程序。
调度器通常由三个部分组成:
为减少上下文切换时间,通常采用两组寄存器,其中一组供内核使用,一组供用户使用。这样,上下文切换时,只需改变指针,让其指向当前寄存器组。
没有其他就绪进程时,运行闲逛进程(idle)
闲逛进程的特征:
CPU利用率
CPU利用率。CPU是计算机系统中最重要和最昂贵的资源之一,所以应尽可能使CPU保持“忙”状态,使这一资源的利用率最高。CPU利用率的计算方法如下:
C P U 利用率 = C P U 有效工作时间 C P U 有效工作时间 + C P U 空闲时间 CPU利用率 = \frac{CPU有效工作时间}{CPU有效工作时间+CPU空闲时间} CPU利用率=CPU有效工作时间+CPU空闲时间CPU有效工作时间
系统吞吐量
表示单位时间内CPU完成作业的数量。长作业需要消耗较长的处理机时间,因此会降低系统的吞吐量。对于短作业,需要消耗的处理机时间较短,因此能提高系统的吞吐量。 系统吞吐量 = 单位时间完成作业的数量 系统吞吐量 = 单位时间完成作业的数量 系统吞吐量=单位时间完成作业的数量
系统吞吐量 = 总共完成了多少道作业 总共花了多少时间 系统吞吐量 = \frac{总共完成了多少道作业}{总共花了多少时间} 系统吞吐量=总共花了多少时间总共完成了多少道作业
周转时间
周转时间是指从作业提交到作业完成所经历的时间,使作业等待、在就绪队列中排队、在处理机上运行及输入/输出操作所花费时间的总和。$周转时间 = 作业完成时间 - 作业提交时间 或者 或者 或者周转时间 = 等待时间 + 运行时间 + I/O操作的时间$。
平均周转时间就是所有作业周转时间的平均值。
带权周转时间是指作业周转时间与作业实际运行时间的比值:
带权周转时间 = 作业周转时间 作业实际运行时间 带权周转时间 = \frac{作业周转时间}{作业实际运行时间} 带权周转时间=作业实际运行时间作业周转时间
带权周转时间必然>= 1,带权周转时间与周转时间都是越小越好。
等待时间
指进程/作业处于等待处理机状态时间之和,等待时间越长,用户满意度越低。一般也以“平均等待时间”来评价整体性能。
响应时间
指从用户提交请求到系统首次产生响应所用的时间。
算法每次从后备队列中选择最先进入该队列的一个或几个作业,将它们调入内存分配必要的资源,创建的进程并放入就绪队列。(只考虑了等待时间)
FCFS调度算法属于不可剥夺算法,可以理解成先到的永远都会被放在最前面所以其实调用不了。
算法的特点:
算法简单,效率低;对长作业比较有利,但对短作业不利;有利于CPU频繁忙型作业,而不利于I/O频繁忙型作业。不会导致饥饿。
算法思想:追求最少的平均等待时间,最少的平均周转时间、最少的平均带权周转时间。(在所有进程都几乎同时到达)
最短的作业优先得到服务(最短是指是要求服务时间最短)。既可用于作业调度,也可以用于进程调度。用于进程调度时称为SPF
算法。(只考虑了执行时间)
SJF和SPF是非抢占式的算法。但是也有抢占式的版本——最短剩余时间优先算法SRTN。
最短剩余时间优先算法SRTN:每当有进程加入就绪队列或改变时就需要需求调研,如果新到达的进程剩余时间比当前运行进程的剩余时间更短,则由新的进程抢占处理机。
优点:
”最短的平均等待时间、平均周转时间“
缺点:
会导致饥饿。如果圆圆不断地有短作业进来,可能使长作业长时间得不到服务。如果一直得不到服务,则称为”饿死“
做题小tips:
- 题目中未特别说明,所提到的“短作业/进程优先算法”默认时非抢占式的
- 书上说的“SJF调度算法的平均等待时间、周转时间最小”。前面的例子表明最短剩余时间优先算法得到的平均等待时间、平均周转时间还要更少。应该要加上一个限定条件:在所有进程几乎同时到达时,采用SJF调度算法的平均等待时间、平均周转时间最少
- SJF的平均等到时间、周转时间并不一定最少,但相比其他算法SJF可以获得较少的平均等待时间、平均周转时间
综合考虑做的等待时间和要求服务时间。在每次调度时先计算各个作业的相应比,选择响应比最高的作业为其服务。不会导致饥饿。
响应比 = 等待时间 + 要求服务时间 要求服务时间 响应比 = \frac{等待时间 + 要求服务时间}{要求服务时间} 响应比=要求服务时间等待时间+要求服务时间
通过这个式子可以理解为什么要选响应比最高的,响应比高说明对于这个任务来说等待时间已经很长了。短任务和长任务对比时,要到达相同的响应比短作业的等待时间更短。同长短的任务,先到达的任务响应比更高,更先被执行。
非抢占式算法。因此只有当前运行的作业/进程主动放弃处理机时,才需要调度,才需要计算响应比。
算法思想:公平、论住哪地为各个进程服务,让每个进程在一定时间间隔内都可以得到响应。按照各进程达到就绪队列的顺序,轮流让各个进程执行一个时间片。若进程未在一个时间片内完成则剥夺处理机,将进程重新放到就绪队列队尾重新排队。若进程未能在时间片内运行完,将被抢占剥夺处理机使用权,时间片轮转调度算法属于抢占式算法。由时钟装置发出时钟中断来通知CPU时间片已到
用于进程调度(只有作业放入了内存建立了相应的进程后,才能被分配处理机的时间片)。
✨时间片的大小对系统性能的影响很大。若时间片足够大,以至于所有进程都能在一个时间片内执行完毕,则时间片轮转调度算法就退化为先来先服务。时间片太小会导致处理机将在进程间过于频繁地切换,使得处理机的开销增大。
时间片的长短通常由:系统响应时间、就绪队列中的进程数目和系统的处理能力决定。
优先级调度算法既可用于作业调度也可以用于进程调度。算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它。该算法既可以抢占也可以不抢占。
按创建的后优先级是否变化,可以将优先级分为:
优先级设置可以参考的规则:
优点:用优先级区分紧急程度、重要程度,适用于实时操作系统。可灵活地调整对各种作业/进程的偏好程度
缺点:若源源不断地有高优先级进程到来,则可能导致饥饿
算法规则:
抢占式算法。在K级队列的进程运行中,若上一级的队列中进入了一个新的进程,则会由于新进程处于优先级更高的队列中,所以新进程会抢占处理机,原来运行的进程放回k级队列队尾。
优缺点:
可能会导致饥俄(要是一直有新的任务进入就绪队列中,导致优先级低的队列中的任务很久不被运行)