计算机操作系统知识整理-进程与线程知识汇总(计算机操作系统入门指南)

第2章 进程与线程

进程与线程要学习的内容较多,博主会逐步将内容整理并更新,现已更新两万+字图文……

2.1 进程

2.1.1 进程的概念

说到进程,我们不得不将其与程序联系起来,因为进程就是程序的一次执行过程,是系统进行资源分配和调度的基本单位

注意:此时还未引入线程概念。

我们知道,程序,是存放在硬盘中的一个可执行的文件,即,一系列的指令集合。在多道程序下,操作系统允许程序并发执行,这就导致这些程序之间失去了封闭性,为了更好地描述和控制程序的并发执行,操作系统引入了进程的概念

举例说明进程与程序:

当我们启动一个应用程序A后,可以在控制面板中看到有一个与之对应的进程,如果启动多个应用程序A以后,就会出现多个相对应的进程。对于被启动的多个应用程序A,其指令集合是相同的,但是其对应的每个进程,都有着各自的数据(这部分与进程的组成有关,我们会在接下来的章节中详细的学习),即同一个应用程序执行多次会对应着多个不同的进程。

小节总结

在这里插入图片描述

2.1.2 进程的特征

进程是由多道程序的并发执行而引出的一个概念,因此进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。

进程的特征包括了动态性、并发性、独立性以及异步性。

  • 动态性

    进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。因此动态性是进程的最基本的特征。

  • 并发性

    指多个进程同时存在与内存中,能在一段时间内同时运行。引入进程的目的就是使多个进程可以并发的执行。并发性是进程的重要特征,也是操作系统的重要特征。

  • 独立性

    指进程是一个能独立运行、独立获得资源和独立接受调度的基本单位。

  • 异步性

    由于进程的相互制约,使得进程按各自独立的、不可预知的速度执行。异步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制。

进程的这个四个特征,只需要理解即可。

小节总结

在这里插入图片描述

2.1.3 进程的组成

在介绍进程的组成之前,我们将要引入一个新的概念:进程实体(进程映像)。

进程实体,由进程控制块程序段以及数据段三部分共同组成。在引入进程实体的概念后,进程也可以被定义为“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位”,即,进程实体是静态的,进程是动态的。如果不进行专门的区分,我们一般将进程实体就称为进程。因此,也可以说进程是一个独立的运行单位,是操作系统进行资源分配和调度的基本单位,它由进程控制块、程序段以及数据段三部分组成

2.1.3.1 进程控制块
  • 定义

    进程控制块(Process Control Block,PCB)是一个专门为进程配置的数据结构,用于描述进程的基本情况和运行状态。

  • 与进程的关系

    进程在创建时,操作系统会为它创建一个PCB,随后,这个PCB会被放置到内存中,在进程执行时,系统通过该进程的PCB可以了解进程的现行的状态信息,由此操作系统就可以对其进行控制和管理。当进程结束时,操作系统也会将PCB从内存中删除,进程也就随之消亡了。由此可以得知,PCB是进程存在的唯一标志

  • 进程控制块包含的主要信息

    PCB主要包含进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息等。

    进程描述信息 进程控制和管理信息 资源分配清单 处理机相关信息
    进程、用户标识符(PID、UID) 进程当前状态,进程优先级 代码运行入口地址、程序的外存地址等代码段、数据段、堆栈段指针,文件描述符,键盘、鼠标等 通用、地址、控制、标志寄存器值,状态字等
    • 进程描述信息

      进程标识符:标志每一个进程,每一个进程都有一个唯一的ID。

      用户标识符:标识每个进程所属的用户。

    • 进程控制和管理信息

      进程当前状态:描述进程的状态信息,作为处理机分配调度的依据。

      进程优先级:描述进程抢占处理机的优先级,优先级越高的进程越容易抢占到处理机。

    • 资源分配清单

      用于说明有关内存地址空间或虚拟地址空间的状况,所打开文件的列表和所使用的I/O设备信息。

    • 处理机相关信息

      也被称为处理机的上下文,主要指处理机中各个寄存器的值,当进程正在运行时,处理机的许多信息都在寄存器中,当该进程被挂起后,处理机的状态信息必须要被保存在相应的PCB中,以便在该进程重新执行时,能从上次执行的位置继续执行。

  • 系统是如何使用PCB控制进程的

    1. 当操作系统想要调度某个进程运行时,要从该进程的PCB中查出其现在的状态以及优先级。
    2. 当操作系统调度该进程成功后,要根据其PCB中保存的处理机状态信息,设置该进程恢复运行(从上次中断的地方继续执行),并且根据其PCB中的程序和数据的内存始址,找到其程序和数据。
    3. 进程在执行的过程中,如果需要和与之合作的进程实现同步、通信或访问文件时,也需要访问PCB。
    4. 当进程由于某种原因暂停执行时,需要将其断点的处理机环境保存在PCB中。

    由上述过程可知,在进程的整个生命周期中,系统都是通过PCB对进程进行控制的,即系统唯有通过PCB才能感知到该进程的存在。

  • PCB的组织方式

    由于操作系统中,往往会存在着许多进程,有的处于就绪态、有的处于阻塞态,并且阻塞的原因各不相同。为了方便进程的调度和管理,就需要将各个进程的PCB用适当的结构组织起来。常见的方式有链接方式和索引方式两种。(进程的状态将于下个小节进行介绍)

    链接方式即将PCB连接成一个队列,不同的状态对应不同的队列,根据进程的不同状态分为不同的队列,如就绪队列阻塞队列,甚至会根据阻塞的不同原因分成不同的阻塞队列。

    索引方式即将PCB放入一个索引表中,不同的状态对应不同的索引表,如就绪索引表阻塞索引表等。

2.1.3.2 程序段

程序段就是被进程调度程序调度到CPU执行的程序代码段,即该进程在CPU中要被执行的代码段。

注意:一个程序可以被多个进程共享,即多个进程可以运行同一个程序。

2.1.3.3 数据段

进程的数据段包含了进程对应的程序加工处理的原始数据和进程运行过程中产生的中间/最终结果。

小节总结

在这里插入图片描述

2.1.4 进程的状态与转换

2.1.4.1 进程的五种状态

进程有着完整的生命周期,主要包括五种状态:创建态就绪态运行态阻塞态以及结束态

  • 创建态

    进程正在被创建时,就处于创建态,在这个阶段操作系统会为该进程分配各种资源并且会初始化PCB。

    创建进程时,会分为多个步骤

    1. 申请一个空白的PCB,并向PCB中填写用于控制和管理进程的信息
    2. 为该进程分配运行时所必须的资源
    3. 把该进程转入就绪态并放置在就绪队列中,如果此时进程所需要的资源无法得到满足,就无法转入成就绪态,此时该进程的创建工作就没有完成,该进程就处于创建态
  • 就绪态

    进程获取到了除了CPU之外的所有必要资源后,就会被转为就绪态,在该状态下的进程,一旦被CPU调用,就会立刻执行。由于单核CPU同一时刻只会执行一个进程,因此,系统中会存在着大量的就绪态的进程,这些进程会被放置在一个队列中,该队列被称为就绪队列

  • 运行态

    进程在CPU上运行时所处的状态就是运行态。

  • 阻塞态

    当运行中的进程需要等待某一事件而暂停运行后所处的状态就是阻塞态。在该状态的进程会持续等待某个资源(例如,等待I/O设备),处于该状态的进程,即使CPU是空闲的,也不会被执行。由于系统中的很多资源都是独占式共享,因此系统中也会存在着多个处于阻塞态的进程,这些进程会被放置在队列中,这个队列被称为阻塞队列,系统甚至会根据阻塞的原因,将这些阻塞的进程放置在不同的阻塞队列中。

  • 结束态

    当进程需要被结束运行时,系统就会将该进程转为结束态。

  • 就绪态和阻塞态的区别

    处于就绪态的进程,拥有除了CPU之外的一切必要资源,只需要获得CPU资源后就会立刻执行,而处于阻塞态的进程在持续等待除了CPU之外的某个资源,即使此刻CPU是空闲的,该进程也不会被执行。

注意:

就绪态、运行态、阻塞态被称为进程的3种基本状态

2.1.4.2 进程的状态转换

下图表示了进程的五种状态之间的转换

在这里插入图片描述

其中较为重要的状态转换是3种基本状态的转换

  • 就绪态->运行态

    处于就绪态的进程,被CPU调度后,获取了CPU资源,就会转入运行态。

  • 运行态->就绪态

    处于运行态的进程,如果被分配的时间片用完后,就会被转入就绪态。

  • 运行态->阻塞态

    处于运行态的进程,如果请求了某个资源的使用或者等待某一事件的发生时,就会从运行态转为阻塞态。进程会以系统调用的方式主动请求操作系统提供服务,即,从运行态转换为阻塞态是进程主动的行为。

  • 阻塞态->就绪态

    处于阻塞态的进程请求的资源或等待的事件到来后,中断处理程序就会把进程从阻塞态转为就绪态。

注意:

一个进程从运行态转为就阻塞态是进程主动的行为,从阻塞态转为就绪态是被动地行为,需要有其他进程与中断处理程序的协助。

小节总结

在这里插入图片描述

2.1.5 进程控制

2.1.5.1 定义以及实现方法
  • 进程控制的定义

    进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。即,进程控制就是实现进程状态的转换

  • 如何实现进程控制

    由上文可知,进程控制即实现进程状态的转换。进程状态的转换过程需要保证“一气呵成”的完成。因此,我们可以使用在第一章介绍的原语来实现进程控制。(原语相关的介绍见1.3.5.3小节)

    为什么一定要保证进程状态的转换要“一气呵成”的完成呢?

    接下来,我们将举例讲解,假设此时有一个进程处于就绪态(即该进程的PCB处于就绪队列中),现在要将该进程转换为阻塞态,此时负责进程控制的内核程序至少要做以下两件事:

    1. 将该进程的PCB的状态设置为阻塞态
    2. 将该进程的PCB放置在阻塞队列中

    显而易见,上述两个步骤必须保证“一气呵成”的完成,如果不能保证上述两个步骤“一气呵成”的完成,例如在完成了第一步后,CPU收到了中断信号,进而去执行相应的中断处理程序,那么此时,该进程的PCB的状态是阻塞态,但是PCB处于就绪队列中,这就导致操作系统中的关键数据结构信息的不统一,从而影响操作系统进行后续的管理工作。

2.1.5.2 进程的创建

进程的创建对应的进程状态转换为:

  • 无->创建态
  • 创建态->就绪态

进程的创建是通过创建原语实现的,其包括了如下几个步骤:

  1. 为新进程分配一个唯一的进程标识号(PID),并申请一个空白PCB,若PCB申请失败,则创建失败(PCB是有限的)。
  2. 为进程分配其运行所必须的资源(内存、文件、CPU时间和I/O设备等),如果资源不足,则此时进程处于创建态,等待必要的资源。
  3. 初始化PCB,主要包括初始化标志信息、处理机状态信息、处理机控制信息以及设置进程的优先级等。
  4. 将PCB插入就绪队列(如果就绪队列没有满队),将PCB状态设置为就绪态,此时进程就被转换为了就绪态。

会引起进程创建的事件一般有如下几种:

  1. 用户登录,在分时系统中,用户登录成功,系统就会为其创建一个进程。
  2. 作业调度,在多道批处理系统中,如果有新的作业被放入内存,则会为其创建一个进程。
  3. 提供服务,用户向操作系统提供某些服务时,会为其创建一个进程处理该请求。
  4. 应用请求,由用户进程主动请求创建一个子进程。

在操作系统中,存在父子进程的概念,即,允许一个进程创建另外一个进程,此时创建者为父进程,被创建的进程为子进程,子进程可以继承父进程所有的资源,当子进程被撤销时,会将其从父进程那里获取的资源归还给父进程。

2.1.5.3 进程的终止

进程的终止对应的进程状态转换为:

  • 运行态/阻塞态/就绪态->终止态

进程的终止是通过终止原语实现的,其包括了如下几个步骤:

  1. 根据被终止进程的PID,从PCB集合中找到该进程的PCB,读取进程的状态。
  2. 若该进程处于执行状态,则立即终止进程的执行,将CPU分配给其他进程。
  3. 若该进程还有子孙进程,则将其所有的子孙进程终止。
  4. 将该进程所拥有的全部资源归还给操作系统或其父进程。
  5. 将该PCB从所在的队列中删除。

会引起进程终止的事件一般有如下几种:

  1. 正常结束,表示进程的任务已完成并准备退出运行(进程自己请求终止,如exit系统调用)。
  2. 异常结束,表示进程在运行时,发生了异常事件,使得程序无法继续运行(整数除0、非法使用特权指令、运行超时、I/O故障等)。
  3. 外界干预,指进程应外界的请求而终止运行,如用户或者操作系统干预、父进程请求和父进程终止。
2.1.5.4 进程的阻塞

进程的阻塞对应的进程状态转换为:

  • 运行态->阻塞态

进程的阻塞是通过阻塞原语实现的,其包括了如下几个步骤:

  1. 通过进程的PID,从PCB集合中找到该进程的PCB,读取进程的状态
  2. 若该进程处于运行态,则保护其运行现场,将其转换为阻塞态,停止运行。
  3. 将该PCB插入相应的等待队列,将CPU调度给其他处于就绪态的进程。

会引起进程阻塞的事件一般有如下几种:

  1. 某个进程请求某些系统资源失败,如等待I/O设备资源。
  2. 某个进程等待某种操作的完成。
2.1.5.5 进程的唤醒

进程的唤醒对应的进程状态转换为:

  • 阻塞态->就绪态

进程的唤醒是通过唤醒原语实现的,进程的唤醒是基于某个事件的,是为了将等待某个事件的阻塞进程唤醒,其包括了如下几个步骤:

  1. 通过进程的PID,从该事件的等待队列中找到相应进程的PCB。
  2. 将其从等待队列中移出,并设置为就绪态。
  3. 将该PCB插入就绪队列,等待CPU的调度。

会引起进程唤醒的事件一般有如下几种:

  1. 被阻塞的进程所请求的系统资源空闲,被分配给该进程时,如I/O设备资源。
  2. 被阻塞的进程所等待的某种操作完成后。

注意:进程的阻塞原语和唤醒原语是一对作用刚好相反的原语,必须成对地使用,如果在某个进程中调用了阻塞原语,则必须在与之合作的或其他相关的进程中安排一条对应的唤醒原语,以便唤醒被阻塞的进程,否则,阻塞进程将永远不会被唤醒从而永久地处于阻塞状态。

2.1.5.6 进程的切换

进程的切换对应的进程状态转换为:

  • 运行态->阻塞态/就绪态
  • 就绪态->运行态

进程的切换是通过切换原语实现的,其包括了如下几个步骤:

  1. 根据进程的PID,从PCB集合中找到对应的PCB,将此刻的运行环境信息存入PCB中。
  2. 将PCB移入相应的队列中。
  3. 选择另外一个进程执行,并更新其PCB。
  4. 根据PCB恢复新进程所需的运行环境。

会引起进程切换的事件一般有如下几种:

  1. 进程的时间片时间已用完。
  2. 由更高优先级的进程到达。
  3. 当前进程主动阻塞。
  4. 当前进程主动终止。

切换进程时,有一个重要的步骤——将运行环境信息存入PCB

这里所讲的运行环境信息可以理解为CPU的运行上下文(Context),我们通过前面的学习,了解CPU中存在着各种寄存器(例如在CPU状态切换中用到的PSW寄存器,这方面是计算机组成原理中的知识,在这里就不进行详细的介绍了),当一个进程在运行时,会在寄存器中产生很多信息,这些信息就是当前进程运行的上下文,当CPU被其他进程占用结束后,再次执行当前进程时,CPU中的寄存器的信息可能就被其他进程的信息覆盖了,因此,为了让该进程能够继续无误的运行,就需要将进程的运行上下文信息保存到PCB中,当CPU再次执行该进程时,从PCB中读取该进程的运行环境信息,就可以继续执行该进程了。

由上述的进程控制相关的五种原语的介绍,我们可以做出进一步的总结:

无论哪种进程原语,要做的事情都包含在以下三件事中:

  1. 更新进程PCB的信息(修改进程的状态、保护/恢复进程的运行环境信息)。
  2. 将PCB插入合适的队列(阻塞队列、就绪队列)
  3. 分配/回收资源

注意:切换上下文和模式切换是不同的,模式切换时,CPU逻辑上可能还在执行同一进程,用户进程最开始都运行在用户态,若进程因中断或异常进入核心态运行,执行完后又回到用户态刚才被中断的进程运行。用户态和内核态之间的切换称为模式切换,其并没有改变当前进程,上下文切换只能发生在内核态,它是多任务操作系统中的一个必需的特性。

小节总结

由于该小节的内容过多,因此博主将思维导图分为了几个小图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.1.6 进程间的通信

2.1.6.1 简介
  • 什么是进程间的通信?

    进程通信指进程与进程之间的信息交换(数据交换)。进程间的通信依赖于操作系统提供的支持

  • 为什么进程间的通信依赖于操作系统的支持?

    由上面的学习,我们知道,进程是操作系统分配系统资源的基本单位,各个进程拥有的内存地址是相互独立的。因此各个进程之间如果要进行数据交换,就必须依赖操作系统的支持。

  • 为什么各个进程拥有的内存地址要相互独立?

    从系统安全的层面来理解,如果各个进程之间拥有的内存地址不是相互独立的,这就意味着,各个进程之间可以相互访问对方的数据。这将会是一个十分危险的事情,例如,你的某些APP中存储有你的私密信息(银行账户、身份证信息等等),此时,你安装了一个恶意的APP,当你同时运行这两个APP时,这个恶意的APP就可以随意的访问你的存有私密信息的APP的数据,那么你的私密信息就会存在暴露的风险。

    因此,为了保证安全,一个进程不能直接访问另外一个进程的内存地址

2.1.6.2 进程通信的分类

进程间的通信可以分为三类:共享存储消息传递以及管道通信

2.1.6.3 共享存储
2.1.6.3.1 简介

共享存储是指需要进行通讯的若干进程向操作系统申请一块共享空间,该共享空间可以由这些进程直接访问,通过对这片共享空间进行读/写操作实现进程之间的信息交换。操作系统会将这块共享空间映射到对应的进程的虚拟地址空间。(后续章节会进行介绍)

通俗的讲,就是有两个人A和B,他们之间要通过黑匣子交换物品,A只能通过从黑匣子中获取B放入黑匣子中的物品,而不能直接从B那里获取物品。同理,B想将物品交给A,也只能先将物品放入黑匣子,而不能直接交给A。

在这里插入图片描述

2.1.6.3.2 特点
  • 为了避免出现错误,在对共享空间进程读/写操作时,需要使用同步互斥工具(如P/V操作)对读/写进行控制。
  • 操作系统只负责为通信进程提供共享空间以及同步互斥工具,数据交换则由用户自行安排读/写指令完成。
2.1.6.3.3 分类

共享存储可以进一步的分为基于数据结构的共享基于存储区的共享

  • 基于数据结构的共享(低级)

    操作系统在内存中定义好某种数据结构的共享空间供各个进程使用,例如共享空间里只能放大小为10的数组,这种共享方式速度慢、限制多。

  • 基于存储区的共享(高级)

    操作系统在内存中划分一块共享存储区,而不指定数据结构,数据存放的形式和位置都由进程控制,这种共享方式速度快。

2.1.6.4 消息传递
2.1.6.4.1 简介

消息传递指进程间以格式化的信息为单位进行数据交换,如果通信的进程之间不存在共享空间,则可以利用操作系统提供的消息传递方法实现进程通信。

格式化的信息包括信息头信息体两部分

  • 信息头

    包括发送进程ID、接收进程ID、信息长度等信息。

  • 信息体

    进程要交换的内容。

2.1.6.4.2 特点
  • 消息传递依赖与操作系统提供的发送消息接收消息的两个原语进行的数据交换。

    发送消息原语:send(pid,msg)

    接收消息原语:receive(pid,&msg)

  • 由于其隐藏了通信实现细节,简化了通信程序的设计(依赖两个原语即可),是当前应用最广泛的进程间通信机制。

2.1.6.4.3 分类

消息传递可以进一步分为两种:直接通信方式间接通信方式

  • 直接通信方式

    发送进程通过发送原语,直接通过接收进程的PID将消息发送给接收进程,并且将它挂在接收进程的消息缓冲队列上,接收进程从该队列中获取信息。

  • 间接通信方式

    发送进程把消息发送到某个中间实体,接收进程从该中间实体获取信息。这种中间实体一般称为”信箱“,因此间接通信方式也被称为信箱通信方式。

通俗理解就是,有两个人A和B,A要写信给B,A将写好的信交给快递员,第一种方式就是快递员将信直接交到B的手上,第二种方式就是快递员将信放入B的邮箱中,B从邮箱中取信。

2.1.6.5 管道通信
2.1.6.5.1 简介

管道通信是指通过管道这种特殊的共享文件进行进程间的信息交换。

管道是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件(pipe文件)。向该文件中写入数据的进程被称为写进程,从该文件中读取数据的进程被称为读进程

在这里插入图片描述

2.1.6.5.2 特点
  • 管道中的数据采用FIFO的方式。
  • 管道只能采用半双工通信,即同一时间段内只能实现单向的传输,如果想要进行双向传输,则需要两个管道。
  • 由操作系统控制各类进程互斥地访问管道。
  • 当管道写满时,写进程将阻塞,直至读进程将管程中的数据取走使得管道有空闲空间。
  • 当管道为空时,读进程将阻塞,直至写进程往管程中写入数据使得管道不再为空。
  • 管道中的数据一旦被读取,就会彻底的消失,因此,当多个进程读取同一个管道的数据时,可能会发生错乱,此时通常有两种解决方案
    1. 一个管道允许多个写进程和一个读进程。
    2. 一个管道允许有多个写进程和多个读进程,且操作系统会让各个读进程轮流从管道中读取数据(Linux操作系统的解决方案)
2.1.6.5.3 Linux中的管道

在Linux中,管道是一种使用十分频繁的通信机制。管道可以克服使用文件进行通信的两个如下的问题:

  1. 限制管道的大小。实际上,管道是一个固定大小的缓冲区,在Linux中,管道的大小为4KB,这使得它的大小不像文件那样可以不加检验地增大。使用固定大小的缓冲区会带来一个问题,比如在写入数据时,该管道可能已满,此时,对于该管道的write()调用将会被阻塞。
  2. 读进程也可能工作得比写进程快。当所有的数据都被读取时,管道为空,此时对于该管道的read()调用将会被阻塞。
2.1.6.5.4 与共享存储的区别
  • 共享存储的互斥访问由通信进程实现,管道通信的互斥访问由操作系统实现。
  • 共享存储中,允许进程在共享空间中的任意位置进行读/写数据,管道通信中,只允许从一边写入,另一边读取数据。
  • 共享存储中,若某个进程要访问共享空间,就必须没有其他进程在该共享空间中进行写操作,否则访问行为就会被阻塞;管道通信中,只要管道中有数据,就可以从管道中读取数据,不必担心因为其他进程正在对管道进行写操作而阻塞。

小节总结

在这里插入图片描述

2.2 线程

2.2.1 线程的基本概念与属性

2.2.1.1 简介

线程是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成,它是进程中的一个实体,是被系统独立调度和分派的基本单位。可以理解为“轻量级进程”。

2.2.1.2 为什么要引入线程

在前面我们学到了,引入进程的原因是为了使多道程序可以并发地执行,提高资源利用率。一个进程可能需要同时的进行多项工作(例如QQ需要同时进行视频聊天、传输文件等),这些功能就不是一个进程顺序处理就可以解决的了。此时,就引入了线程的概念,它将进程进行了进一步的划分,一个进程可以拥有多个线程,这些线程共享进程所拥有的资源,每个线程都可以独立的被CPU调度,这些线程可以分别负责不同的功能。例如上述QQ的例子,就可以是一个QQ进程,对应两个线程(视频聊天和传输文件)。

有上述可知,引入线程可以进一步提高操作系统的并发性能。在引入线程后,进程的含义就发生了改变,此时,进程只作为除CPU外的系统资源的分配单元,线程则作为CPU的分配单元

2.2.1.3 线程的主要属性
  • 线程不拥有系统资源,它共享所属进程的资源。
  • 不同的线程可以执行相同的程序。
  • 线程是CPU独立调度的基本单位,多个线程可以并发执行。
  • 一个线程被创建后,便开始了它的生命周期,直至终止,线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。

小节总结

在这里插入图片描述

2.2.2 线程的状态

与进程十分类似,线程也具有五种状态:新建状态、就绪状态、运行状态、阻塞状态、终止状态。

由于线程可以理解为对进程进行了进一步的划分,因此这五种状态以及五种状态之间的转换,与进程基本相同,详情可见2.1.4小节

2.2.3 线程的组织与控制

2.2.3.1 线程控制块

与进程类似,操作系统为每个线程也配备了一个线程控制块TCB,用于记录控制和管理线程的信息。

TCB主要包括的内容如下图所示。

在这里插入图片描述

2.2.3.2 线程的组织

与进程类似,操作系统根据不同的状态将TCB分为不同的线程表,通过线程表将线程组织起来。

2.2.3.3 线程的控制
  • 线程的创建

    操作系统中提供了创建线程的系统调用,用户程序启动时,通常仅有一个称为“初始化线程”的线程正在执行,其主要功能是用于创建新线程,在创建新的线程时,需要使用创建线程的系统调用,提供相应的参数即可。

  • 线程的终止

    当线程完成自己的任务或者线程在运行过程中出现异常被强制终止时,由终止线程调用相应的终止线程的系统调用来执行该线程的终止。

    注意:线程被终止后并不会立即释放它所占有的资源,只有当进程中的其他线程执行了分离函数后,被终止的线程才会释放资源。

小节总结

在这里插入图片描述

2.2.4 线程与进程的比较

  • 调度

    引入线程之前,进程是资源分配和调度的基本单位,每次调度都要切换上下文,开销较大。

    引入线程之后,进程是除CPU之外资源分配的基本单位,线程是CPU调度的基本单位,在同一个进程中,切换线程的开销很小,且不会触发进程的切换。但是从一个进程中的线程切换到另外一个进程中的线程时,还是会触发进程的切换。

  • 并发性

    引入线程之前,进程与进程之间可以并发执行。

    引入线程之后,不仅进程与进程之间可以并发执行,线程与线程之间也可以并发执行,进一步提高了操作系统的并发性能。

  • 拥有资源

    进程是系统中除CPU之外的资源分配的基本单位,线程不拥有系统资源,线程可以访问所属进程的系统资源。

  • 独立性

    每个进程都有独立的地址空间和资源,不同进程之间的线程互不可见,同一进程的多个线程共享该进程的资源。

  • 多CPU系统

    引入线程之前,不管有多少个CPU,进程只能运行在一个CPU上。

    引入线程之后,可以将进程中的多个线程分配到不同的CPU上执行。

  • 执行情况

    引入线程之前,处于“执行态”的进程是指该进程正在被CPU执行。

    引入线程之后,处于“执行态”的进程是指该进程中的某个线程正在被CPU执行。

小节总结

在这里插入图片描述

2.2.5 线程的实现方式

线程的实现可以分为两类:用户级线程(ULT)内核级线程(KLT)

2.2.5.1 用户级线程
  • 概念

    使用用户级线程时,有关线程管理的所有工作由应用程序通过线程库实现

    线程库是指由编程语言为程序员提供的创建和管理线程的API库,实现线程库主要的方法有以下两种:

    • 在用户空间中提供一个没有内核支持的API库,该API库中的所有代码和数据结构都在用户空间中,调用该库内的函数只会导致用户空间中的一个本地函数的调用。
    • 实现由操作系统直接支持的内核级的API库,该API库中的所有代码和数据结构位于内核空间中,调用该库内的函数会导致对内核的系统调用。

    此时,操作系统内核无法感应到线程的存在,只能感应到进程的存在。即,在用户看来是多个线程在并发执行,但是对于操作系统内核而言,只有一个进程在运行。使用用户级线程的系统的结构图如下图所示。

在这里插入图片描述

使用上述结构的操作系统,CPU的调度单位依旧是进程(因为操作系统内核感知不到用户级线程),各个进程轮流的执行时间片,例如,系统中的进程A包含1个用户级线程,进程B包含10个用户线程,此时,进程A中的线程运行时间就是进程B中每个线程的十倍。

  • 优点

    1. 由于用户级线程的管理维护是在应用程序中实现的,因此,线程之间的切换不需要转换到内核态,节省了CPU切换状态的开销。
    2. 每个进程的调度算法可以使专用的,不同的进程可以根据自身需要,选择不同的调度算法来调度自己的线程。
    3. 用户级线程的实现和管理相关的代码,在应用程序中,不依赖操作系统平台。
  • 缺点

    1. 当同一个进程的用户级线程中,有任意一个线程遇到系统调用相关的阻塞问题后,都会影响同进程下的其他线程,使其他线程也阻塞。
    2. 无法发挥多核的优势,操作系统内核每次分配给一个进程的CPU仅有一个,因此进程中同一时刻仅有一个线程可以执行。
2.2.5.2 内核级线程
  • 概念

    使用内核级线程时,有关线程管理的所有工作由操作系统内核程序实现,操作系统会为每个内核级线程设置一个TCB,以此来对内核级线程进行管理(在前面的章节,已介绍过TCB相关的知识)。使用内核级线程的系统的结构图如下图所示。

在这里插入图片描述

使用上述结构的操作系统,CPU的调度单位是内核级线程。

  • 优点

    1. 可以发挥多核的优势,同一进程的多个线程可以分别被不同的CPU调度执行。
    2. 如果同一个进程中的某个线程被阻塞,并不会影响该进程中的其他线程,系统让其他线程占用CPU。
    3. 内核级线程具有很小的数据结构和堆栈,线程切换快、开销小。
  • 缺点

    同一进程中的用户级线程之间的切换要从用户态转换为内核态,系统开销较大。

2.2.5.3 混合形式
  • 概念

    混合形式是指,将用户级线程和内核级线程组合起来使用,在该实现方式中,内核支持多个内核级线程的建立、调度和管理,也允许用户程序建立、调度和管理用户级线程。一个内核级线程可以对应多个用户级线程(这些用户级线程时分多路复用内核级线程)。使用该实现方式的系统的结构图如下图所示。

在这里插入图片描述

使用上述结构的操作系统,CPU的调度单位是内核级线程,但是允许应用程序通过线程库管理用户级线程

  • 优缺点

    这种方式结合了用户级线程和内核级线程的优点。

小节总结

在这里插入图片描述

2.2.6 多线程模型

当操作系统同时支持用户级线程和内核级线程,即使用的是上文介绍的混合型方式时,由于用户级线程和内核级线程的连接方式的不同,可以分为三种不同的多线程模型:一对一模型多对一模型以及多对多模型

2.2.6.1 一对一模型
  • 概念

    将每个用户级线程映射分别映射到不同的内核级线程,结构图如下图所示。

在这里插入图片描述

由上图可知,这种多线程模型,其实就是我们上文介绍的启用内核级线程时的线程实现方式。

  • 优点

    当一个线程被阻塞后,允许CPU调度另外一个线程运行,并发能力较强。

  • 缺点

    每创建一个用户线程,都需要创建对应的内核线程,系统开销较大。

2.2.6.2 多对一模型
  • 概念

    将多个用户级线程映射映射到一个内核级线程,这些用户线程属于一个进程,线程的调度和管理在用户空间完成,只有在用户级线程需要访问内核时,才将其映射到一个内核级线程中,并且每次只允许一个线程进行映射,结构图如下图所示。

在这里插入图片描述

由上图可知,这种多线程模型,其实就是我们上文介绍纯粹的用户级线程的实现方式。

  • 优点

    线程管理在用户空间进行,效率较高。

  • 缺点

    1. 如果有线程在访问内核时发生阻塞,整个进程都会被阻塞。
    2. 在同一时刻都只能有一个线程访问内核。
    3. 多个线程无法同时被多个CPU调度执行。
2.2.6.3 多对多模型
  • 概念

    将n个用户级线程映射映射到m个内核级线程(n大于等于m),结构图如下图所示。

在这里插入图片描述

由上图可知,这种多线程模型,其实就是我们上文介绍混合形式的实现方式。

该多线程模型下,用户级线程可以被理解为“代码逻辑”的载体,内核级线程可以被理解为“运行机会”的载体,即内核级线程才是CPU调度的单位。内核级线程可以运行任何由映射关系的用户级线程中的代码。

  • 特点

    既克服了多对一模型并发度不高的缺点,又克服了一对一模型的一个用户进程占用太多内核级线程导致的开销太大的缺点。

小节总结

在这里插入图片描述

2.3 CPU调度

2.3.1 调度的相关概念

2.3.1.1 什么是调度

当同时有多个事情要处理时,由于资源有限,无法同时处理这些事情,此时就需要通过按照某种规则来决定处理这些事情的先后顺序,这就是“调度”。

在多道程序系统中,由于进程的数量往往会多于CPU的数量,因此会存在多个进程竞争CPU的现象发生,且十分普遍,此时就需要按照某种规则从就绪队列中选择某个进程并将CPU分配给该进程,使该进程可以执行。

2.3.1.2 七状态模型

在2.1.4小节中,我们学习了进程的五种状态,此时为了学习CPU调度,我们需要额外的学习进程的一种特殊的状态挂起状态(挂起态),挂起态可以进一步的被细分为就绪挂起阻塞挂起两种状态,因此进程的五状态模型就可以进一步延伸为七状态模型

那么什么时候会出现挂起状态的进程?

由于内存有限,需要将某些进程的数据调出暂存至外存,待这些进程需要运行且内存空闲时再重新调入内存,这些被暂存至外存等待的进程的状态就是挂起状态。处于挂起状态的进程的PCB会被组织成挂起队列

七状态模型的转换图如下图所示

在这里插入图片描述

阻塞与挂起的比较

相同之处:都处于暂时无法获得CPU的服务

差异之处:阻塞状态的进程映像依旧在内存中,而挂起状态的进程映像处于外存中。

2.3.1.3 三级调度

在操作系统中将调度分为了三级,从高到低分别是高级调度->中级调度->低级调度。

  • 高级调度

    高级调度也被称为作业调度,是指按照一定的原则从外存的作业后备队列中挑选一个作业调入内存,并且创建相应的进程,以便于其可以竞争CPU。每一个作业只调入一次,调出一次。当作业被调入时就会为其建立PCB,调出时就会撤销其PCB。

    作业是指具体的任务,用户向系统提交一个作业就是指用户让操作系统启动一个程序(处理具体的任务)。

    内存空间是有限的,因此当内存空间已满时,又有用户向操作系统提交了作业,此时这些作业就会被放入内存之外的作业后备队列中。

  • 中级调度

    中级调度也被称为内存调度,是指按照某种规则决定将哪个处于挂起状态的进程重新调入内存,一个进程可能被多次调出和调入内存。内存调度的频次比作业调度要高。

  • 低级调度

    低级调度也被称为进程调度,是指按照某种规则从就绪队列中选取一个进程,将CPU分配给该进程。

    进程调度是操作系统中最基本的一种调度,频率很高,一般几十毫秒一次。

2.3.1.4 三级调度的联系与对比
做的事情 发生的场景 频率 对进程状态的影响
高级调度 按照某种规则,从后备队列中选择合适的作业将其调入内存,创建进程 外存->内存(面向作业) 最低 无->创建态->就绪态
中级调度 按照某种规则,从挂起队列中选择合适的进程,将其数据调回内存 外存->内存(面向进程) 中等 就绪挂起->就绪态(阻塞挂起->阻塞态)
低级调度 按照某种规则,从就绪队列中选择一个进程为其分配CPU 内存->CPU 最高 就绪态->运行态

小节总结

在这里插入图片描述

2.3.2 调度的实现

2.3.2.1 调度程序与闲逛进程
  • 调度程序

    在操作系统中,通过调度程序来进行调度和分派CPU,调度程序通常由排队器、分派器以及上下文切换器这三个部分组成。

    • 排队器

      用于将操作系统中的所有就绪进程按照一定的规则排成一个或多个队列,以便于调度程序选择。

    • 分派器

      依据调度程序所选的进程,将其从就绪队列中调出并为其分配CPU。

    • 上下文切换器

      在CPU切换进程时,会通过上下文切换器对两个进程的运行环境进行保护和恢复。

      在上下文切换器中会发生两对上下文(运行环境)的切换操作

      1. 将当前进程的运行环境保存至对应的PCB中,再装入分派程序的运行环境,以便分派程序运行。
      2. 移出分派程序的运行环境,将新的进程的运行环境装入CPU的各个寄存器。

    调度程序的结构图如下图所示

在这里插入图片描述

  • 闲逛进程

    当系统中没有就绪进程时,CPU会执行闲逛进程,如果没有其他就绪进程,CPU就会一直执行闲逛进程,并且在执行闲逛进程时测试中断。

    可以理解为,当CPU空闲下来时,会通过执行闲逛进程,不断的巡逻。

    闲逛进程的优先级是最低的,一旦有就绪进程存在,就会立刻让出CPU。

2.3.2.2 调度与切换的时机

在2.3.1.3小节中,我们介绍了进程调度的相关概念,那么什么时候才会发生进程的调度与切换?

首先,在2.3.2.1小节中,我们介绍了调度程序,可知,进程调度是通过调度程序完成的,调度程序是操作系统内核程序。当请求调度的事件发生后,才可能运行调度程序,调度了新的就绪进程后,才会发生进程的切换。然而并非所有情况下,请求调度后就一定可以发生进程的调度和切换。

  • 可以进行进程调度与切换的情况

    1. 当前进程主动放弃CPU
      • 进程正常终止时,会发生进程调度与切换
      • 进程运行过程中,发生异常而终止时,会发生进程调度与切换
      • 进程主动请求阻塞(请求I/O等)时,会发生进程调度与切换
    2. 当前进程被动放弃CPU
      • 分配给当前进程的时间片用完时,会发生进程调度与切换
      • 有优先级更高的进程要运行时,会发生进程调度与切换
      • 有更紧急的事情要处理(如I/O中断)时,会发生进程调度与切换
  • 不可以进行进程调度与切换的情况

    1. 在处理中断的过程中,不能进行进程调度与切换。因为中断处理过程十分复杂,在实现上很难做到进程切换。

    2. 进程在操作系统内核的临界区中,不能进行进程调度与切换。当进程进入操作系统内核临界区后,需要对该临界区加锁,不允许其他进程访问,在解锁前不应该切换到其他进程,以避免发生临界区无法解锁的情况发生。

      临界资源:一个时间段内只允许一个进程使用的资源,各个进程要互斥地访问临界资源。

      临界区:访问临界资源的代码片段。

      内核临界区:一般用于访问某种操作系统内核数据结构,例如进程的就绪队列等,因此内核临界区必须要尽快的释放锁,否则可能会影响操作系统内核的其他管理工作。

      基于上述介绍,进程在操作系统内核临界区中不能进行进程调度与切换,但是进程在普通临界区中时,可以进行进程调度与切换。

    3. 在进行原子性(原语)操作时,不能进行进程调度与切换。在进行原子性操作时,连中断都要屏蔽,就更不应该进行进程调度与切换了。

2.3.2.3 调度的方式分类

进程调度方式是指当某个进程正在CPU上执行时,有某个更为重要的进程需要处理时,分配CPU的方式。通常情况下有两种进程调度方式:非抢占式调度抢占式调度

  • 非抢占式调度

    只允许进程主动放弃CPU,即当某个进程在CPU上执行时,即使有优先级更高的进程进入就绪队列时,依旧会让正在执行的进程继续执行,直至该进程主动放弃CPU。

    非抢占式调度的优点在于实现简单,系统开销小适用于批处理系统,不适用于分时系统和实时系统

  • 抢占式调度

    允许进程被动放弃CPU,即当某个进程在CPU上执行时,如果有优先级更高的进程进入就绪队列时,会立即暂停正在执行的进程,将其转换为就绪态,并将CPU分配给优先级更高的就绪进程。

    抢占式调度的优点在于可以提高系统吞吐率和响应效率,适用于分时系统和实时系统

2.3.2.4 线程的调度

在操作系统中有两种线程的调度,它们分别是,用户级线程调度和内核级线程调度。

  • 用户级线程调度

    由于内核并不知道用户级线程的存在,因此此时,内核调度的单位依旧是进程。

  • 内核级线程调度

    由于内核知道内核级线程的存在,因此此时,内核会选择一个特定线程运行,并为其分配一个时间片,如果时间片结束了就会强制挂起该线程。

小节总结

在这里插入图片描述

2.3.3 调度算法

2.3.3.1 调度算法的评价指标

就像每个人有每个人不同的优点一般,不同的调度算法也有着不同的特性,为了对调度算法的性能进行评价,人们提出了很多评价标准。主要的评价标准有:CPU利用率系统吞吐量周转时间等待时间以及响应时间

  • CPU利用率

    CPU利用率是指,CPU处于忙碌状态的时间占比。

    CPU利用率=CPU有效工作时间/(CPU有效工作时间+CPU空闲等待时间)

  • 系统吞吐量

    系统吞吐量指单位时间内CPU完成作业的数量。

  • 周转时间

    周转时间指作业从提交到系统至作业完成所经历的时间,即作业等待、在就绪队列中排队、在CPU上运行以及I/O操作所花费的时间总和。

    周转时间=作业完成时间-作业提交时间

    对于操作系统而言,更加关心整体作业的周转表现,因此延伸出了另外一个评价标准——平均周转时间,平均周转时间是指多个作业周转时间的平均值。

    平均周转时间=各个作业周转时间总和/作业数

    对于不同的作业,较大的作业周转时间一般较长,较小的作业周转时间一般较短,因此延伸出了一个评价指标——带权周转时间,带权周转时间是指作业周转时间与作业实际运行时间的比值。

    带权周转时间=作业周转时间/作业实际运行时间

    基于带权周转时间自然而然的就延伸出了另外一个评价标准——平均带权周转时间。

    平均带权周转时间=各作业带权周转时间/作业数

  • 等待时间

    等待时间指进程建立后等待被服务的时间之和。

    注意:等待时间不包含等待I/O设备操作完成的时间,因为在这个时间内,进程正在被该I/O设备服务。
    等待时间=周转时间-运行时间

  • 响应时间

    响应时间是指从用户提交请求到系统至首次产生响应所用的时间,响应时间越短,用户体验越好。

小节总结

在这里插入图片描述

2.3.3.2 先来先服务调度算法
  • 适用范围

    可以作用于作业调度以及进程调度

  • 算法介绍

    类似日常生活中排队买东西,先来先服务(FCFS)调度算法根据作业或者进程到达的先后顺序进行服务

    在作业调度中,FCFS会从后备作业队列中选择最先进入队列的一个或几个作业,将它们调入内存中运行。

    在进程调度中,FCFS会从就绪队列中选择一个最先进入队列的进程,为其分配CPU,使其运行。

  • 是否可抢占

    FCFS是非抢占式调度算法

  • 案例分析

    假设系统中有4个进程(纯计算进程,即没有I/O操作的进程),它们的到达以及预估运行时间如下表所示。

    进程 到达时间 预估运行时间
    P1 0 8
    P2 4 6
    P3 5 2
    P4 6 6

    使用FCFS调度算法,上述各进程的调度过程为P1->P2->P3->P4,根据2.3.3.1小节的各个指标的计算规则,可计算出上述各个进程的各项指标如下表所示。

    进程 完成时间 周转时间 带权周转时间 等待时间
    P1 0+8=8 8-0=8 8/8=1 8-8=0
    P2 6+8=14 14-4=10 10/6=1.67 10-6=4
    P3 14+2=16 16-5=11 11/2=5.5 11-2=9
    P4 16+6=22 22-6=16 16/6=2.67 16-6=10

    平均周转时间=(8+10+11+16)/4=11.25

    平均带权周转时间=(1+1.67+5.5+2.67)/4=2.71

    平均等待时间=(0+4+9+10)/4=5.75

    可以看出进程P3的带权周转时间的值是5.5,比其他三个进程都要高出许多,因此,就意味着对于P3这个进程,明明只需要运行很短的时间,但是却要等待很长。

  • 优缺点

    • 优点:公平、算法简单
    • 缺点:排在长作业(进程)后面的短作业需要等待很长时间,带权周转时间很大,对于短作业十分不友好,即对长作业有利,对短作业不利。
  • 是否会导致饥饿(作业/进程长时间得不到服务)

    由于排在队列中的作业/进程,会被按照顺序依次调度,因此并不会导致饥饿发生。

2.3.3.3 短作业优先调度算法
  • 适用范围

    可以作用于作业调度以及进程调度

  • 算法介绍

    基于FCFS调度算法的缺点,人们提出了短作业优先(SJF)以及短进程优先(SPF)调度算法,即针对短作业(进程)优先调度。

    SJF会从后备队列汇总选择一个或若干预估运行时间最短的作业,将它们调入内存运行。

    SPF会从就绪队列中选择一个预估运行时间最短的进程,为其分配CPU,使其运行。

    还有一种短作业优先调度算法叫最短剩余时间优先算法SRTN,该算法是每当有进程加入就绪队列或者进程完成时就需要调度,如果新到达的进程剩余时间比当前运行的进程剩余时间更短,就会由新进程抢占CPU,将当前运行的进程改为就绪状态。

  • 是否可抢占

    SJF和SPF是非抢占式的算法,SRTN是抢占式算法

  • 案例分析

    假设系统中有4个进程(纯计算进程,即没有I/O操作的进程),它们的到达以及预估运行时间如下表所示。

    进程 到达时间 预估运行时间
    P1 0 8
    P2 4 6
    P3 5 2
    P4 6 6

    使用SPF调度算法,上述各进程的调度过程为P1->P3->P2->P4,根据2.3.3.1小节的各个指标的计算规则,可计算出上述各个进程的各项指标如下表所示。

    进程 完成时间 周转时间 带权周转时间 等待时间
    P1 0+8=8 8-0=8 8/8=1 8-8=0
    P2 10+6=16 16-4=12 12/6=2 12-6=6
    P3 8+2=10 10-5=5 5/2=2.5 5-2=3
    P4 16+6=22 22-6=16 16/6=2.67 16-6=10

    平均周转时间=(8+12+5+16)/4=10.25

    平均带权周转时间=(1+1.67+5.5+2.67)/4=2.0425

    平均等待时间=(0+4+9+10)/4=4.75

    使用SRTN调度算法,上述各进程的调度过程如下(Pn(m)表示当前进程Pn剩余时间m):

    0时刻(P1到达):P1(8)

    4时刻(P2到达):P1(4),P2(6)

    5时刻(P3到达):P1(3),P2(6),P3(2)

    6时刻(P4到达):P1(3),P2(6),P3(1),P4(6)

    7时刻(P3完成):P1(3),P2(6),P4(6)

    10时刻(P1完成):P2(6),P4(6)

    16时刻(P2完成):P4(6)

    22时刻(P4完成),结束

    根据2.3.3.1小节的各个指标的计算规则,可计算出上述各个进程的各项指标如下表所示。

    进程 完成时间 周转时间 带权周转时间 等待时间
    P1 10 10-0=10 10/8=1.25 10-8=2
    P2 16 16-4=12 12/6=2 12-6=6
    P3 7 7-5=2 2/2=1 2-2=0
    P4 22 22-6=16 16/6=2.67 16-6=10

    平均周转时间=(10+12+2+16)/4=10

    平均带权周转时间=(1.25+2+1+2.67)/4=1.73

    平均等待时间=(2+6+0+10)/4=4.5

    对比FCFS与SPF以及SRTN算法的平均周转、平均带权周转、平均等待时间,可以看出花费时间FCFS>SPF>SRTN。

  • 优缺点

    • 优点

      “最短的”平均等待时间、平均周转时间

    • 缺点

      该算法对长作业不利,对短作业有利,如果不断有短作业到达,长作业可能长时间得不到调度。

      由于作业的预估运行时间是由用户提供的,因此该预估运行时间并不代表真正的运行时间,因此该算法不一定能真正做到短作业优先调度。

  • 是否会导致饥饿(作业/进程长时间得不到服务)

    会导致长作业饥饿。

2.3.3.4 高响应比调度算法
  • 适用范围

    适用于作业调度与进程调度

  • 算法介绍

    平衡了FCFS和SJF调度算法的优缺点,同时考虑了每个作业的等待时间和预估运行时间,在每次进行作业调度时,先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业运行。

    响应比=(等待时间+要求服务时间)/要求服务时间

  • 是否可抢占

    非抢占式算法

  • 案例分析

    假设系统中有4个进程(纯计算进程,即没有I/O操作的进程),它们的到达以及预估运行时间如下表所示。

    进程 到达时间 预估运行时间
    P1 0 8
    P2 4 6
    P3 5 2
    P4 6 6

    使用高响应比优先调度算法,上述各进程的调度过程为如下(Pn(m)表示当前进程Pn的响应比为m):

    0时刻,只有P1到达就绪队列,P1运行

    8时刻,P1运行完成,主动放弃CPU,此时就绪队列中有P2(1.67),P3(2.5),P4(1.33),P3运行

    10时刻,P3运行完成,主动放弃CPU,此时就绪队列中有P2(2),P4(1.67),P2运行

    16时刻,P2运行完成,主动放弃CPU,此时就绪队列中只有P4,P4运行

    22时刻,P4运行完成,结束

  • 优缺点

    折中了FCFS算法和SJF算法,综合考虑了等待时间和运行时间。

  • 是否会导致饥饿(作业/进程长时间得不到服务)

    不会导致饥饿

2.3.3.5 时间片轮转调度算法
  • 适用范围

    用于进程调度

  • 算法介绍

    时间片轮转调度算法主要应用于分时操作系统,系统会将所有就绪进程按照FCFS策略排成一个就绪队列,调度程序总是选择就绪队列中的第一个进程执行,但该进程只能执行一个固定的时间片,当时间片结束后,该进程还没有执行完,就会剥夺该进程的CPU使用权并且将该进程重新放到就绪队列的队尾重新排队。

  • 是否可抢占

    可抢占,由时钟装置发出时钟中断来通知CPU时间片已到。

  • 案例分析

    假设系统中有4个进程(纯计算进程,即没有I/O操作的进程),它们的到达以及预估运行时间如下表所示。

    进程 到达时间 预估运行时间
    P1 0 8
    P2 4 6
    P3 5 2
    P4 6 6

    使用时间片轮转调度算法进行调度,按照时间片大小分别为2和8来进行分析。

    时间片大小为2时,调度过程如下(Pn(m)表示进程Pn的剩余运行时间m就绪队列(队头…队尾)):

    时刻 就绪队列中的进程情况 CPU分配情况
    0 P1(8) P1
    2 P1(6) P1
    4 P2(6),P1(4) P2
    5 P1(4),P3(2) P2
    6 P1(4),P3(2),P4(6),P2(4) P1
    8 P3(2),P4(6),P2(4),P1(2) P3
    10 P4(6),P2(4),P1(2) P4
    12 P2(4),P1(2),P4(4) P2
    14 P1(2),P4(4),P2(2) P1
    16 P4(4),P2(2) P4
    18 P2(2),P4(2) P2
    20 P4(2) P4
    22 空闲 空闲

    上面的表格的文字描述版本:

    0时刻,就绪队列中的进程情况为(P1(8)),此时让P1运行。

    2时刻,时间片用完,此时就绪队列中的进程情况为(P1(6)),此时继续让P1运行。

    4时刻,P2到达,且时间片刚好用完,此时就绪队列中的进程情况为(P2(6),P1(4)),此时让P2运行。

    5时刻,P3到达,但是时间片没有用完,此时就绪队列中的进程情况为(P1(4),P3(2)),此时P2继续运行。

    6时刻,P4到达,且时间片刚好用完,此时就绪队列中的进程情况为(P1(4),P3(2),P4(6),P2(4)),此时让P1运行。

    8时刻,时间片用完,此时就绪队列中的进程情况为(P3(2),P4(6),P2(4),P1(2)),此时让P3运行。

    10时刻,P3执行结束,且时间片用完,此时就绪队列中的进程情况为(P4(6),P2(4),P1(2)),此时让P4运行。

    12时刻,时间片用完,此时就绪队列中的进程情况为(P2(4),P1(2),P4(4)),此时让P2运行。

    14时刻,时间片用完,此时就绪队列中的进程情况为(P1(2),P4(4),P2(2)),此时让P1运行。

    16时刻,P1执行结束,且时间片用完,此时就绪队列中的进程情况为(P4(4),P2(2)),此时让P4运行。

    18时刻,时间片用完,此时就绪队列中的进程情况为(P2(2),P4(2)),此时让P2运行。

    20时刻,P2执行结束,且时间片用完,此时就绪队列中的进程情况为(P4(2)),此时让P4运行。

    22时刻,P4执行结束,至此所有进程执行完毕。

    时间片大小为8时,调度过程如下(Pn(m)表示进程Pn的剩余运行时间m就绪队列(队头…队尾)):

    时刻 就绪队列中的进程情况 CPU分配情况
    0 P1(8) P1
    4 P2(6) P1
    5 P2(6),P3(2) P1
    6 P2(6),P3(2),P4(6) P1
    8 P2(6),P3(2),P4(6) P2
    14 P3(2),P4(6) P3
    16 P4(6) P4
    22 空闲

    上面的表格的文字描述版本:

    0时刻,就绪队列中的进程情况为(P1(8)),此时让P1运行。

    4时刻,P2到达,时间片没有用完,此时就绪队列中的进程情况为(P2(6)),此时继续让P1运行。

    5时刻,P3到达,时间片没有用完,此时就绪队列中的进程情况为(P2(6),P3(2)),此时继续让P1运行。

    6时刻,P4到达,时间片没有用完,此时就绪队列中的进程情况为(P2(6),P3(2),P4(6)),此时继续让P1运行。

    8时刻,P1执行结束,且时间片用完,此时就绪队列中的进程情况为(P2(6),P3(2),P4(6)),此时让P2运行。

    14时刻,P2执行结束,此时就绪队列中的进程情况为(P3(2),P4(6)),此时让P3运行。

    16时刻,P3执行结束,此时就绪队列中的进程情况为(P4(6)),此时让P4运行。

    22时刻,P4执行结束,至此所有进程执行完毕。

    当时间片为8时,可以看出这四个进程的执行顺序为P1->P2->P3->P4,此种情况与使用FCFS调度算法时的结果是一致的。

    由此,可以知道如果时间片太大,使得每个进程都可以在一个时间片内完成,则时间片轮转调度算法会退化为FCFS调度算法,并且会增大进程响应时间。因此时间片不能太大

    所谓的增大进程响应时间,在此举例说明,若系统中有10个进程在并发执行,且时间片为1秒,则一个进程被响应可能需要等待9秒,即,如果用户在自己的进程的时间片外通过键盘发出调试命令,可能需要等待9秒才能被系统响应,这种用户体验就是极其糟糕的了。

    与此同时,由之前的章节我们已经了解到,进程调度与切换是有时间代价的,因此如果时间片太小,进程调度切换太过于频繁,系统就会花费大量的时间来处理进程切换,就会导致实际用于处理用户进程的时间比例减少,因此时间片也不能太小

    一般来说,时间片要让切换进程的开销占比不超过1%

  • 优缺点

    • 优点

      公平、响应快,适用于分时系统

    • 缺点

      由于高频率的切换进程,因此有一定的系统开销

      无法区分任务的紧急程度

  • 是否会导致饥饿

    不会

2.3.3.6 优先级调度算法
  • 适用范围

    适用于作业调度以及进程调度。

  • 算法介绍

    优先级调度算法中的优先级用于描述作业/进程的紧急程度。

    在作业调度中,每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存。

    在进程调度中,每次从就绪队列中选择优先级最高的一个或几个进程,为其分配CPU。

  • 是否可抢占

    既有抢占式的也有非抢占式的。

    抢占式优先级调度算法,当一个进程正在运行时,若有优先级更高的进程进入就绪队列,则将当前进程暂停,将CPU分配给优先级更高的进程。

    非抢占式优先级调度算法,当一个进程正在运行时,即使有优先级更高的进程进入就绪队列,依旧让当前进程继续运行,直至由于当前进程自身原因让出CPU后,才会将CPU分配给优先级更高的进程。

  • 案例分析

    假设系统中有4个进程(纯计算进程,即没有I/O操作的进程),它们的到达时间、预估运行时间以及优先级如下表所示。

    进程 到达时间 预估运行时间 优先级
    P1 0 8 3
    P2 4 6 2
    P3 5 2 1
    P4 6 6 2

    使用非抢占式优先级调度算法时,运行情况如下:

    0时刻,只有P1到达,此时运行P1

    8时刻,P1运行完毕,此时就绪队列中有P2、P3、P4。P3优先级最高,让P3运行。

    10时刻,P3运行完毕,此时就绪队列中有P2、P4,此时虽然P2和P4的优先级一致,但是P2比P4更早进入就绪队列,因此让P2运行。

    16时刻,P2运行完毕,此时就绪队列只有P4,让P4运行。

    22时刻,P4运行完毕,所有进程执行结束。

    使用抢占式优先级调度算法时,运行情况如下(Pn(m)表示进程Pn的剩余运行时间m):

    0时刻,只有P1(8)到达,此时运行P1

    4时刻,P2(6)到达,且优先级比P1高,此时就绪队列中进程情况为(P2(6))此时让P2运行,将P1重新放回就绪队列。

    5时刻,P3到达,此时就绪队列中进程情况为(P1(4)、P3(2)),由于P3的优先级比P2的优先级高,因此让P3运行。

    6时刻,P4到达,此时就绪队列中进程情况为(P1(4),P2(5),P4(6)),由于此时P3的优先级依旧是最高的,因此继续让P3运行。

    7时刻,P3运行结束,此时就绪队列中进程情况为(P1(4),P2(5),P4(6)),由于此时P2与P4的优先级相同,并且比P1高,并且P2先到达就绪队列,因此让P2运行。

    12时刻,P2运行结束,此时就绪队列中进程情况为(P1(4),P4(6)),由于此时P4的优先级最高,让P4运行。

    18时刻,P4运行结束,此时就绪队列中进程情况为(P1(4)),让P1运行。

    22时刻,P1运行结束,所有进程执行结束。

  • 优缺点

    • 优点

      用优先级区分紧急程度,适用于实时操作系统。

    • 缺点

      若不断地有高优先级的进程到来,可能会导致饥饿

  • 是否会导致饥饿

    可能会导致饥饿

  • 优先级

    • 优先级分类

      优先级可分为两种:动态优先级、静态优先级。

      动态优先级是指在进程运行过程中,根据进程情况的变化动态调整优先级,主要依据有CPU的占用时长,就绪进程等待CPU的时长等。

      静态优先级是指在创建进程时就确定了,且在进程的整个运行期间保持不变,主要依据有进程类型、进程对资源的要求、用户要求等。

    • 优先级设置的原则

      1. 系统进程>用户进程,系统进程作为系统的管理者,应该拥有更高的优先级
      2. 交互型进程>非交互型进程(前台进程>后台进程)
      3. I/O型进程>计算机型进程,I/O型进程是指会频繁使用I/O设备的进程,计算机型进程是指会频繁使用CPU的进程(很少使用I/O设备),由于I/O设备可以与CPU并行运行,因此若将I/O型进程的优先级设置得更高,就可以使I/O设备更多地和CPU并行,提高系统的整体效率。
2.3.3.7 多级反馈队列调度算法
  • 适用范围

    用于进程调度

  • 算法介绍

    多级反馈队列调度算法是时间片轮转调度算法和优先级调度算法的综合与发展,在该算法中,通过动态地调整进程优先级和时间片的大小,来达到兼顾多个方面的系统目标。

    具体思想:

    1. 设置多个就绪队列,为每个队列赋予不同的优先级,第1级队列的优先级最高,第2级的队列优先级次之,以此类推至后续队列。

    2. 为每个队列的进程赋予不同大小的时间片,优先级越高的队列,时间片越小。

    3. 在每个队列中采用FCFS调度算法,当新进程进入内存后,首先将它放入第1级队列的末尾,按照FCFS原则等待调度。当轮到该进程执行时,如果它能够在给予的时间片内完成,则该进程就正常的执行完毕,否则,调度程序会将它转入第2级队列的末尾继续按照FCFS原则等待调度,若它在第2级队列的时间片内依旧未完成,就会被转入第3级队列,以此类推,当该进程被转入最后的第n级队列后,在第n级队列中便采用时间片轮转调度算法进行调度(因为此时已经没有更低优先级的队列了)。

    4. 按队列的优先级进行调度,只有第1级队列为空时,才会调度第2级队列中的进程,即,仅当第k级队列为空时,才会调度第k+1级队列中的进程。如果正在执行第k级队列中的某个进程时,系统有一个新的进程进入了更高优先级的队列时,就会立即把正在运行的进程放回到第k级队列中,把CPU分配给新进入的进程。

      具体规则稍微有些复杂,各位同学最好结合后面的案例分析来进行理解。

  • 是否可抢占

    是抢占式算法

  • 案例分析

    假设系统中有4个进程(纯计算进程,即没有I/O操作的进程),它们的到达以及预估运行时间如下表所示。

    进程 到达时间 预估运行时间
    P1 0 8
    P2 4 6
    P3 5 2
    P4 6 6

    并且,在系统中设置了三个优先级队列,分别是第1级队列、第2级队列、第3级队列,其时间片大小分别是1、2、4。

    使用多级反馈队列调度算法后,这些进程的执行情况如下(Pn(m)表示进程Pn的剩余运行时间m):

    0时刻,P1(8)进入第1级队列,执行P1。

    此时队列中的进程情况为:第1级队列[P1(8)],第2、3级队列为空。

    1时刻,P1(7)时间片用完,此时P1会进入第2级队列的队尾,此时由于第1级队列中没有任何进程,系统继续执行P1。

    此时队列中的进程情况为:第1、3级队列为空,第2级队列[P1(7)]。

    3时刻,P1(5)时间片用完,此时P1会进入第3级队列的队尾,此时由于第1、2级队列中没有任何进程,系统继续执行P1。

    此时队列中的进程情况为:第1、2级队列为空,第3级队列[P1(5)]。

    4时刻,P1(4)时间片没有用完,但是,这是P2(4)进入第1级队列,因此P1被重置到第3级队列队尾,系统执行P2。

    此时队列中的进程情况为:第1级队列[P2(4)],第2级队列为空,第3级队列[P1(4)]。

    5时刻,P2(5)时间片用完,且P3(2)进入第1级队列,此时P2进入第2级队列,执行P3。

    此时队列中的进程情况为:第1级队列[P3(2)],第2级队列[P2(5)],第3级队列[P1(4)]。

    6时刻,P3(1)时间片用完,且P4(6)进入第1级队列,此时P3进入第2级队列队尾,执行P4。

    此时队列中的进程情况为:第1级队列[P4(6)],第2级队列[P2(5),P3(1)],第3级队列[P1(4)]。

    7时刻,P4(5)时间片用完,进入第2级队列队尾,此时第1级队列为空,第2级队列的队头为P2,系统执行P2。

    此时队列中的进程情况为:第1级队列为空,第2级队列[P2(5),P3(1),P4(5)],第3级队列[P1(4)]。

    9时刻,P2(3)时间片用完,进入第3级队列队尾,此时第1级队列为空,第2级队列的队头为P3,系统执行P3。

    此时队列中的进程情况为:第1级队列为空,第2级队列[P3(1),P4(5)],第3级队列[P1(4),P2(3)]。

    10时刻,P3执行完毕,此时第1级队列为空,第2级队列的队头为P4,系统执行P4。

    此时队列中的进程情况为:第1级队列为空,第2级队列[P4(5)],第3级队列[P1(4),P2(3)]。

    12时刻,P4(3)时间片用完,进入第3级队列队尾,此时第1、2级队列为空,第3级队列队头为P1,系统执行P1。

    此时队列中的进程情况为:第1、2级队列为空,第3级队列[P1(4),P2(3),P4(3)]。

    16时刻,P1执行完毕,此时第1、2级队列为空,第3级队列队头为P2,系统执行P2。

    此时队列中的进程情况为:第1、2级队列为空,第3级队列[P2(3),P4(3)]。

    19时刻,P2执行完毕,此时第1、2级队列为空,第3级队列队头为P4,系统执行P4。

    此时队列中的进程情况为:第1、2级队列为空,第3级队列[P4(3)]。

    22时刻,P4执行完毕,所有进程执行结束。

  • 优缺点

    • 优点

      • 对各类型进程相对公平(先来先服务调度算法的优点)
      • 每个新到达的进程可以很快得到响应(时间片轮转调度算法的优点)
      • 短进程只用较少时间就可以完成(短进程优先调度算法的优点)
    • 缺点

      算法实现较为复杂

  • 是否会导致饥饿

    会导致饥饿(一直有短进程到达,就会导致低优先级队列中的长进程饥饿)

2.3.3.8 各类调度算法对比汇总
算法名称 是否可抢占 优点 缺点 适用范围 默认决策模式
先来先服务 非抢占式 公平,实现简单 不利于短作业 批处理系统 非抢占式
短作业优先 既有抢占式也有非抢占式 平均等待时间最少,效率最高(此处默认包括了最短剩余时间优先算法) 长作业会饥饿,预估运行时间不易确定 作业调度,批处理系统 非抢占式
高响应比优先 非抢占式 兼顾长短作业 计算响应比的开销大 分时系统 非抢占式
时间片轮转 抢占式 公平、响应快,适用于分时系统 频繁切换进程,会导致额外的开销 分时系统 抢占式
优先级调度算法 既有抢占式的也有非抢占式的 用优先级区分紧急程度,适用于实时操作系统。 可能会导致饥饿 实时系统 非抢占式
多级反馈队列 抢占式(每个队列内不一定) 兼顾长短作业,有较好的响应时间,可行性强 算法较复杂 通用 抢占式

小节总结

在这里插入图片描述
未完待续……

参考文献

《2023年操作系统考研复习指导》-王道论坛

如果这篇文章有帮助到你,不妨点赞、转发、收藏一下喽~

猜你喜欢

转载自blog.csdn.net/weixin_42584100/article/details/127831572