• 第 2 章 进程与线程

    Intro

    0x00 考纲内容

    1. 进程与线程 进程与线程的基本概念;进程/线程的状态与转换 线程的实现:内核支持的线程,线程库支持的线程 进程与线程的组织与控制 进程间通信:共享内存,消息传递,管道
    2. CPU调度与上下文切换 调度的基本概念:调度的目标; 调度的实现:调度器/调度程序(scheduler),调度的时机与调度方式(抢占式/非抢占式),闲逛进程,内核级线程与用户级线程调度 典型调度算法:先来先服务调度算法:短作业(短进程、短线程)优先调度算法,时间片轮转调度算法,优先级调度算法,高响应比优先调度算法,多级队列调度算法,多级反馈队列调度算法上下文及其切换机制
    3. 同步与互斥 同步与互斥的基本概念 基本的实现方法:软件方法;硬件方法 锁:信号量;条件变量 经典同步问题:生产者-消费者问题,读者-写者问题:哲学家进餐问题
    4. 死锁 死锁的基本概念;死锁 预防死锁避免;死锁检测和解除

    0x01 复习提示

    进程管理是操作系统的核心,也是每年必考的重点。其中,进程的概念、进程调度、信号量机制实现同步和互斥、进程死锁等更是重中之重,必须深入掌握。需要注意的是,除选择题外,本章还容易出综合题,其中信号量机制实现同步和互斥、进程调度算法和死锁等都可能命制综合题,如利用信号量进行进程同步就在往年的统考中频繁出现。

     

    一、进程与线程

    在学习本节时,应该思考以下问题:

    1. 为什么要引入进程?
    2. 什么是进程?进程由什么组成?
    3. 进程是如何解决问题的?

    希望读者带着上述问题去学习本节内容,并在学习的过程中多思考,从而更深入地理解本节内容。进程本身是一个比较抽象的概念,它不是实物,看不见、摸不着,初学者在理解进程概念时存在一定困难,在介绍完进程的相关知识后,我们会用比较直观的例子帮助大家理解。

    0x00 进程的概念和特征

    1. 进程的概念

    在多道程序环境下,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性及不可再现性的特征。为此引入了进程(Process)的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性(最基本的两个特性)。

    为了使参与并发执行的每个程序(含数据)都能独立地运行,必须为之配置一个专门的数据结构,称为进程控制块(Process Control Block,PCB)。系统利用 PCB来描述进程的基本情况和运行状态,进而控制和管理进程。相应地,由程序段、相关数据段和PCB三部分构成了进程实体(又称进程映像)。所谓创建进程,实质上是创建进程实体中的 PCB;而撤销进程,实质上是撤销进程的PCB。值得注意的是,进程映像是静态的,进程则是动态的。

    注意:PCB是进程存在的唯一标志

    从不同的角度,进程可以有不同的定义,比较典型的定义有:

    1. 进程是程序的一次执行过程
    2. 进程是一个程序及其数据在处理机上顺序执行时所发生的活动
    3. 进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。

    引入进程实体的概念后,我们可以把传统操作系统中的进程定义为:“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位”。

    读者要准确理解这里说的系统资源。它指处理机、存储器和其他设备服务于某个进程的“时间”,例如把处理机资源理解为处理机的时间片才是准确的。因为进程是这些资源分配和调度的独立单位,即“时间片”分配的独立单位,这就决定了进程一定是一个动态的、过程性的概念。

     

    2. 进程的特征

    进程是由多道程序的并发执行而引出的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。

    1. 动态性。进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征
    2. 并发性。指多个进程实体同存于内存中,能在一段时间内同时运行。引入进程的目的就是使进程能和其他进程并发执行。并发性是进程的重要特征,也是操作系统的重要特征。
    3. 独立性。指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序,都不能作为一个独立的单位参与运行。
    4. 异步性。由于进程的相互制约,使得进程按各自独立的、不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制。

    通常不会直接考查进程有什么特性,所以读者对上面的 4 个特性不必记忆,只求理解。

    0x01 进程的状态与转换

    进程在其生命周期内,由于系统中各进程之间的相互制约及系统的运行环境的变化,使得进程的状态也在不断地发生变化。通常进程有以下5种状态,前3种是进程的基本状态。

    1. 运行态。进程正在处理机上运行。在单处理机中,每个时刻只有一个进程处于运行态。
    2. 就绪态。进程获得了除处理机外的一切所需资源,一旦得到处理机,便可立即运行。系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
    3. 阻塞态,又称等待态。进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。系统通常将处于阻塞态的进程也排成一个队列,甚至根据阻塞原因的不同,设置多个阻塞队列。
    4. 创建态。进程正在被创建,尚未转到就绪态。创建进程需要多个步骤:首先申请一个空白PCB,并向PCB中填写用于控制和管理进程的信息;然后为该进程分配运行时所必须的资源;最后把该进程转入就绪态并插入就绪队列。但是,如果进程所需的资源尚不能得到满足,如内存不足,则创建工作尚未完成,进程此时所处的状态称为创建态。
    5. 终止态。进程正从系统中消失,可能是进程正常结束或其他原因退出运行。进程需要结束运行时,系统首先将该进程置为终止态,然后进一步处理资源释放和回收等工作。

    注意区别就绪态和等待态:就绪态是指进程仅缺少处理器,只要获得处理机资源就立即运行;而等待态是指进程需要其他资源(除了处理机)或等待某一事件。之所以把处理机和其他资源划分开,是因为在分时系统的时间片轮转机制中,每个进程分到的时间片是若干毫秒。也就是说,进程得到处理机的时间很短且非常频繁,进程在运行过程中实际上是频繁地转换到就绪态的;而其他资源(如外设)的使用和分配或某一事件的发生(如I/O操作的完成)对应的时间相对来说很长,进程转换到等待态的次数也相对较少。这样来看,就绪态和等待态是进程生命周期中两个完全不同的状态,显然需要加以区分。

    图 2.1说明了 5 种进程状态的转换,而 3 种基本状态之间的转换如下:

    2

    需要注意的是,一个进程从运行态变成阻塞态是主动的行为,而从阻塞态变成就绪态是被动的行为,需要其他相关进程的协助,

    0x02 进程的组成

    进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位。它由以下三部分组成,其中最核心的是进程控制块(PCB)。

    1. 进程控制块

    进程创建时,操作系统为它新建一个PCB,该结构之后常驻内存,任意时刻都可以存取,并在进程结束时删除。PCB是进程实体的一部分,是进程存在的唯一标志。

    进程执行时,系统通过其 PCB 了解进程的现行状态信息,以便操作系统对其进行控制和管理;进程结束时,系统收回其PCB,该进程随之消亡。

    当操作系统欲调度某进程运行时,要从该进程的PCB中查出其现行状态及优先级;在调度到某进程后,要根据其PCB中所保存的处理机状态信息,设置该进程恢复运行的现场,并根据其PCB中的程序和数据的内存始址,找到其程序和数据;进程在运行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也需要访问PCB;当进程由于某种原因而暂停运行时,又需将其断点的处理机环境保存在PCB 中。可见,在进程的整个生命期中,系统总是通过PCB对进程进行控制的,亦即系统唯有通过进程的PCB才能感知到该进程的存在。

    表 2.1是一个 PCB 的实例。PCB 主要包括进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息等。各部分的主要说明如下:

    2

    1. 进程描述信息。进程标识符:标志各个进程,每个进程都有一个唯一的标识号。用户标识符:进程归属的用户,用户标识符主要为共享和保护服务
    2. 进程控制和管理信息。进程当前状态:描述进程的状态信息,作为处理机分配调度的依据。进程优先级:描述进程抢占处理机的优先级,优先级高的进程可优先获得处理机。
    3. 资源分配清单,用于说明有关内存地址空间或虚拟地址空间的状况,所打开文件的列表和所使用的输入/输出设备信息。
    4. 处理机相关信息,也称处理机的上下文,主要指处理机中各寄存器的值。当进程处于执行态时,处理机的许多信息都在寄存器中。当进程被切换时,处理机状态信息都必须保存在相应的 PCB 中,以便在该进程重新执行时,能从断点继续执行。

    在一个系统中,通常存在着许多进程的PCB,有的处于就绪态,有的处于阻塞态,而且阻塞的原因各不相同。为了方便进程的调度和管理,需要将各进程的PCB用适当的方法组织起来。目前,常用的组织方式有链接方式和索引方式两种。链接方式将同一状态的PCB链接成一个队列,不同状态对应不同的队列,也可把处于阻塞态的进程的PCB,根据其阻塞原因的不同,排成多个阻塞队列。索引方式将同一状态的进程组织在一个索引表中,索引表的表项指向相应的PCB,不同状态对应不同的索引表,如就绪索引表和阻塞索引表等。

    2. 程序段

    程序段就是能被进程调度程序调度到CPU·执行的程序代码段。注意,程序可被多个进程共享,即多个进程可以运行同一个程序

    3. 数据段

    一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。

    0x03 进程控制

    进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。在操作系统中,一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断,它是一个不可分割的基本单位。

    1. 进程的创建

    允许一个进程创建另一个进程,此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程哪里获得的资源归还给父进程。此外,在撤销父进程时,通常也会同时撤销其所有的子进程

    在操作系统中,终端用户登录系统、作业调度、系统提供服务、用户程序的应用请求等都会引起进程的创建。操作系统创建一个新进程的过程如下(创建原语):

    1. 为新进程分配一个唯一的进程标识号,并申请一个空白 PCB(PCB 是有限的)。若 PCB 申请失败,则创建失败。
    2. 为进程分配其运行所需的资源,如内存、文件、I/O设备和CPU 时间等(在 PCB中体现)。这些资源或从操作系统获得,或仅从其父进程获得。如果资源不足(如内存),则并不是创建失败,而是处于创建态,等待内存资源。
    3. 初始化PCB,主要包括初始化标志信息、初始化处理机状态信息和初始化处理机控制信息,以及设置进程的优先级等。‘
    4. 若进程就绪队列能够接纳新进程,则将新进程插入就绪队列,等待被调度运行。

    2. 进程的终止

    引起进程终止的事件主要有:

    1. 正常结束,表示进程的任务已完成并准备退出运行。
    2. 异常结束,表示进程在运行时,发生了某种异常事件,使程序无法继续运行,如存储区越界、保护错、非法指令、特权指令错、运行超时、算术运算错、IO 故障等。
    3. 外界干预,指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。

    操作系统终止进程的过程如下(终止原语):

    1. 根据被终止进程的标识符,检索出该进程的PCB,从中读出该进程的状态。
    2. 若被终止进程处于运行状态,立即终止该进程的执行,将处理机资源分配给其他进程。
    3. 若该进程还有子孙进程,则应将其所有子孙进程终止
    4. 将该进程所拥有的全部资源,或归还给其父进程,或归还给操作系统。
    5. 将该PCB从所在队列(链表)中删除。

    3. 进程的阻塞和唤醒

    正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新任务可做等,进程便通过调用阻塞原语(Block),使自己由运行态变为阻塞态。可见,阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞态。阻塞原语的执行过程如下:

    1. 找到将要被阻塞进程的标识号对应的PCB。
    2. 若该进程为运行态,则保护其现场,将其状态转为阻塞态,停止运行。
    3. 把该PCB插入相应事件的等待队列,将处理机资源调度给其他就绪进程。

    当被阻塞进程所期待的时间出现时,如它所期待的 I/O 操作已完成或其期待的数据已到达,由有关进程(比如,释放该I/O设备的进程,或提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。唤醒原语的执行过程如下:

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

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

    0x04 进程的通信

    进程通信是指进程之间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。高级通信方法主要有以下三类。

    1. 共享存储

    在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换,如图2.2所示。在对共享空间进行写/读操作时,需要使用同步互斥工具(如 P 操作、V 操作),对共享空间的写/读进行控制。共享存储又分为两种:低级方式的共享是基于数据结构的共享;高级方式的共享则是基于存储区的共享。操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。

    注意,进程空间一般都是独立的,进程运行期间一般不能访问其他进程的空间,想让两个进程共享空间,必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。

    简单理解就是,甲和乙中间有一个大布袋,甲和乙交换物品是通过大布袋进行的,甲把物品放在大布袋里,乙拿走。但乙不能直接到甲的手中拿东西,甲也不能直接到乙的手中拿东西。

    3

     

    2. 消息传递

    在消息传递系统中,进程间的数据交换以格式化的消息(Message)为单位。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。这种方式隐藏了通信实现细节,使通信过程对用户透明,简化了通信程序的设计,是当前应用最广泛的进程间通信机制。在微内核操作系统中,微内核与服务器之间的通信就采用了消息传递机制。由于该机制能很好地支持多处理机系统、分布式系统和计算机网络,因此也成为这些领域最主要的通信工具。

    1. 直接通信方式。发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息,如图2.3所示。 4
    2. 间接通信方式。发送进程把消息发送到某个中间实体,接收进程从中间实体取得消息。这种中间实体一般称为信箱。该通信方式广泛应用于计算机网络中。

    简单理解就是,甲要告诉乙某些事情,就要写信,然后通过邮差送给乙。直接通信就是邮差把信直接送到乙的手上;间接通信就是乙家门口有一个邮箱,邮差把信放到邮箱里。

    3. 管道通信

    管道通信允许两个进程按生产者-消费者方式进行通信(见图2.4),生产者向管道的一端写,消费者从管道的另一端读。数据在管道中是先进先出的。只要管道非空,读进程就能从管道中读出数据,若数据被读空,则读进程阻塞,直到写进程往管道中写入新的数据,再将读进程唤醒。只要管道不满,写进程就能往管道中写入数据,若管道写满,则写进程阻塞,直到读进程读出数据,再将写进程唤醒。为了协调双方的通信,管道机制必须提供三方面的协调能力:互斥、同步和确定对方的存在。

    5

    在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现如下:

    1)限制管道的大小。管道文件是一个固定大小的缓冲区,在Linux中该缓冲区的大小为4KB,这使得它的大小不像普通文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,这种情况发生时,随后对管道的 write() 调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write() 调用写。

    2)读进程也可能工作得比写进程快。当所有管道内的数据已被读取时,管道变空。当这种情况发生时,一个随后的 read() 调用将默认地被阻塞,等待某些数据被写入,这解决了 read() 调用返回文件结束的问题。

    管道只能由创建进程所访问,当父进程创建一个管道后,由于管道是一种特殊文件,子进程会继承父进程的打开文件,因此子进程也继承父进程的管道,并使用它来与父进程进进行通信。

    注意:从管道读数据是一次性操作,数据一旦被读取,就释放空间以便写更多数据。普通管道只允许单向通信,若要实现父子进程双向通信,则需要定义两个管道。

    0x05 线程和多线程模型

    1. 线程的基本概念

    引入进程的目的是更好地使多道程序并发执行,提高系统资源利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能

    线程最直接的理解就是“轻量级进程”,它是一个基本的 CPU 执行单元,‘也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态

    引入线程后,进程的内涵发生了改变,进程只作为除CPU外的系统资源的分配单元由于一个进程内部有多个线程,若线程的切换发生在同一个进程内部,则只需要很少的时空开销。下面从几个方面对线程和进程进行比较。

    2. 线程与进程的比较

    1. 调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程,每次调度都要进行上下文切换,开销较大。在引入线程的操作系统中,线程是独立调度的基本单位,而线程切换的代价远低于进程。在同一进程中,线程的切换不会引起进程切换。但从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换
    2. 并发性。在引入线程的操作系统中,不仅进程之间可以并发执行,而且一个进程中的多个线程之间也可以并发执行,甚至不同进程之中的线程也能并发执行,从而使操作系统具有更好的并发性,提高了系统资源的利用率和系统的吞吐量。
    3. 拥有资源。进程是系统中拥有资源的基本单位,而线程不拥有系统资源(仅有一点必不可少、能保证独立运行的资源),但线程可以访问其隶属进程的系统资源,这主要表现在属于同一进程的所有线程都具有相同的地址空间。要知道,若线程也是拥有资源的单位,则切换线程就需要较大的时空开销,线程这个概念的提出就没有意义。
    4. 独立性。每个进程都拥有独立的地址空间和资源,除了共享全局变量,不允许其他进程访问。某进程中的线程对其他进程不可见。同一进程中的不同线程是为了提高并发性及进行相互之间的合作而创建的,它们共享进程的地址空间和资源。
    5. 系统开销。在创建或撤销进程时,系统都要为之分配或回收进程控制块PCB及其他资源,如内存空间、I/O设备等。操作系统为此所付出的开销,明显大于创建或撤销线程时的开销。类似地,在进程切换时涉及进程上下文的切换,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此这些线程之间的同步与通信非常容易实现,甚至无须操作系统的干预。
    6. 支持多处理机系统。对于传统单线程进程,不管有多少处理机,进程只能运行在一个处理机上。对于多线程进程,可以将进程中的多个线程分配到多个处理机上执行。

    3. 线程的属性

    多线程操作系统中的进程已不再是一个基本的执行实体,但它仍具有与执行相关的状态。所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。线程的主要属性如下:

    1. 线程是一个轻型实体,它不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。
    2. 不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统把它们创建成不同的线程。
    3. 同一进程中的各个线程共享该进程所拥有的资源。
    4. 线程是处理机的独立调度单位,多个线程是可以并发执行的。在单CPU的计算机系统中,各线程可交替地占用CPU;在多CPU的计算机系统中,各线程可同时占用不同的CPU,若各个CPU同时为一个进程内的各线程服务,则可缩短进程的处理时间。
    5. 一个线程被创建后,便开始了它的生命周期,直至终止。线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。

    为什么线程的提出有利于提高系统并发性?可以这样来理解:由于有了线程,线程切换时,有可能会发生进程切换,也有可能不发生进程切换,平均而言每次切换所需的开销就变小了;因此能够让更多的线程参与并发,而不会影响到响应时间等问题。

    4. 线程的状态与转换

    与进程一样,各线程之间也存在共享资源和相互合作的制约关系,致使线程在运行时也具有间断性。相应地,线程在运行时也具有下面三种基本状态:

    线程这三种基本状态之间的转换和进程基本状态之间的转换是一样的。

    5. 线程的组织与控制

    (1) 线程控制块

    线程控制块通常包括:①线程标识符:②一组寄存器,包括程序计数器、状态寄存器和通用寄存器;③线程运行状态,用于描述线程正处于何种状态;④优先级;⑤线程专有存储区,线程切换时用于保存现场等:⑥堆栈指针,用于过程调用时保存局部变量及返回地址等。

    同一进程中的所有线程都完全共享进程的地址空间和全局变量。各个线程都可以访问进程地址空间的每个单元,所以一个线程可以读、写或甚至清除另一个线程的堆栈。

    (2) 线程的创建

    线程也是具有生命期的,它由创建而产生,由调度而执行,由终止而消亡。相应地,在操作系统中就有用于创建线程和终止线程的函数(或系统调用)。用户程序启动时,通常仅有一个称为“初始化线程”的线程正在执行,其主要功能是用于创建新线程。在创建新线程时,需要利用一个线程创建函数,并提供相应的参数,如指向线程主程序的入口指针、堆栈的大小、线程优先级等。线程创建函数执行完后,将返回一个线程标识符。

    (3) 线程的终止

    用相应的函数执行终止操作。但是有些线程(主要是系统线程)一旦被建立,·便一直运行而不会被终止。通常,线程被终止后并不立即释放它所占有的资源,只有当进程中的其他线程执行了分离函数后,被终止线程才与资源分离,此时的资源才能被其他线程利用。被终止但尚未释放资源的线程仍可被其他线程调用,以使被终止线程重新恢复运行。

    6. 线程的实现方式

    线程的实现可以分为两类:用户级线程(User-Level Thread,ULT)和内核级线程(Kernel-Level Thread,KLT)。内核级线程又称内核支持的线程

    (1) 用户级线程(ULT)

    在用户级线程中,有关线程管理(创建、撤销和切换等)的所有工作都由应用程序在用户空间中完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程开始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。图2.5(a)说明了用户级线程的实现方式。

    对于设置了用户级线程的系统,其调度仍是以进程为单位进行的,各个进程轮流执行一个时间片。假设进程A包含1个用户级线程,进程B包含100个用户级线程,这样,进程A中线程的运行时间将是进程B中各线程运行时间的100倍,因此对线程来说实质上是不公平的。

    这种实现方式的优点如下:

    1. 线程切换不需要转换到内核空间,节省了模式切换的开销。
    2. 调度算法可以是进程专用的,不同的进程可以根据自身的需要,对自己的线程选择不同的调度算法。
    3. 用户级线程的实现与操作系统平台无关,对线程管理的代码是属于用户程序的一部分

    这种实现方式的缺点如下:

    1. 系统调用的阻塞问题,当线程执行一个系统调用时,不仅该线程被阻塞,而且进程内的所有线程都被阻塞。
    2. 不能发挥多处理机的优势,内核每次分配给一个进程的仅有一个CPU,因此进程中仅有一个线程能执行。

    6

    7

    (2) 内核级线程(KLT)

    在操作系统中,无论是系统进程还是用户进程,都是在操作系统内核的支持下运行的,与内核紧密相关。内核级线程同样也是在内核的支持下运行的,线程管理的所有工作也是在内核空间内实现的。内核空间也为每个内核级线程设置一个线程控制块,内核根据该控制块感知某线程的存在,并对其加以控制。图2.5(b)说明了内核级线程的实现方式。

    这种实现方式的优点如下:

    1. 能发挥多处理机的优势,内核能同时调度同一进程中的多个线程并行执行。
    2. 如果进程中的一个线程被阻塞,内核可以调度该进程中的其他线程占用处理机,也可运行其他进程中的线程。
    3. 内核支持线程具有很小的数据结构和堆栈,线程切换比较快、开销小。
    4. 内核本身也可采用多线程技术,可以提高系统的执行速度和效率。

    这种实现方式的缺点如下:同一进程中的线程切换,需要从用户态转到核心态进行,系统开销较大。这是因为用户进程的线程在用户态运行,而线程调度和管理是在内核实现的。

    8

    (3) 组合方式

    有些系统使用组合方式的多线程实现。在组合实现方式中,内核支持多个内核级线程的建立、调度和管理,同时允许用户程序建立、调度和管理用户级线程。一些内核级线程对应多个用户级线程,这是用户级线程通过时分多路复用内核级线程实现的。同一进程中的多个线程可以同时在多处理机上并行执行,且在阻塞一个线程时不需要将整个进程阻塞,所以组合方式能结合 KLT 和 ULT 的优点,并且克服各自的不足。图 2.5(c)展示了这种组合实现方式。

    在线程实现方式的介绍中,提到了通过线程库来创建和管理线程。线程库是为程序员提供创建和管理线程的API。实现线程库主要的方法有如下两种:

    1. 在用户空间中提供一个没有内核支持的库。这种库的所有代码和数据结构都位于用户空间中。这意味着,调用库内的一个函数只导致用户空间中的一个本地函数的调用。
    2. 实现由操作系统直接支持的内核级的一个库。对于这种情况,库内的代码和数据结构位于内核空间。调用库中的一个API函数通常会导致对内核的系统调用。

    目前使用的三种主要线程库是:POSIX PthreadsWindows APIJavaPthreads 作为 POSIX标准的扩展,可以提供用户级或内核级的库。Windows API 是用于 Windows 系统的内核级线程库。Java 线程 API 允许线程在 Java 程序中直接创建和管理。由于 JVM 实例通常运行在宿主操作系统之上,Java 线程API 通常采用宿主系统的线程库来实现,因此在 Windows 系统中 Java 线程通常采用 Windows API 来实现,在类UNIX系统中采用 Pthreads 来实现。

    9

    7. 多线程模型

    有些系统同时支持用户线程和内核线程,由于用户级线程和内核级线程连接方式的不同,从而形成了下面三种不同的多线程模型。

    1. 多对一模型。将多个用户级线程映射到一个内核级线程,如图2.6(a)所示。这些用户线程一般属于一个进程,线程的调度和管理在用户空间完成。仅当用户线程需要访问内核时,才将其映射到一个内核级线程上,但是每次只允许一个线程进行映射。 优点:线程管理是在用户空间进行的,因而效率比较高。 缺点:如果一个线程在访问内核时发生阻塞,则整个进程都会被阻塞;在任何时刻,只有一个线程能够访问内核,多个线程不能同时在多个处理机上运行。
    2. 一对一模型。将每个用户级线程映射到一个内核级线程,如图 2.6(b)所示。 优点:当一个线程被阻塞后,允许调度另一个线程运行,所以并发能力较强。 缺点:每创建一个用户线程,相应地就需要创建一个内核线程,开销较大。
    3. 多对多模型。将 n 个用户线程映射到 m 个内核级线程上,要求 n ≥ m,如图 2.6(c)所示。 特点:既克服了多对一模型并发度不高的缺点,又克服了一对一模型的一个用户进程占用太多内核级线程而开销太大的缺点。此外,还拥有上述两种模型各自的优点

    10

    0x06 本节小结

    本节开头提出的问题的参考答案如下。

    1. 为什么要引入进程?

    在多道程序同时运行的背景下,进程之间需要共享系统资源,因此会导致各程序在执行过程中出现相互制约的关系,程序的执行会表现出间断性的特征。这些特征都是在程序的执行过程中发生的,是动态的过程,而传统的程序本身是一组指令的集合,是一个静态的概念,无法描述程序在内存中的执行情况,即我们无法从程序的字面上看出它何时执行、何时停顿,也无法看出它与其他执行程序的关系,因此,程序这个静态概念已不能如实反映程序并发执行过程的特征。为了深刻描述程序动态执行过程的性质乃至更好地支持和管理多道程序的并发执行,人们引入了进程的概念。

    2. 什么是进程?进程由什么组成?

    进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码本身,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。·一个进程实体由程序段、相关数据段和PCB三部分构成,其中.PCB是标志一个进程存在的唯一标识,程序段是进程运行的程序的代码,数据段则存储程序运行过程中相关的一些数据。

    3. 进程是如何解决问题的?

    进程把能够识别程序运行态的一些变量存放在PCB中,.通过这些变量系统能够更好地了解进程的状况,并在适当时进行进程的切换,以避免一些资源的浪费,甚至划分为更小的调度单位一线程来提高系统的并发度。

     

    0x07 错题记录

    1234567891011121314151617181920
    CACAABCCCDCDBDCBCADBDDCBA
    2122232425262728293031323334353637383940
    AA/BDACCCABDBDA/CDBCDBCA
    414243444546474849505152535455565758
    DA?C/BC/AACADAACDCCBBBD
    1. 系统动态DLL库中的系统线程,被不同的进程所调用,它们是( )的线程。 A. 不同 B. 相同 C. 可能不同,也可能相同 D.不能被调用 我的答案:A 正确答案:B

      解析: 举个例子,windows 库中存在一个 MessageBox 函数,程序调用这个函数后,将会显示一个消息框。 而这个函数实现,则是通过一个 windows 系统中运行着的线程,调用这个函数实际上是在向这个线程发送请求 这个线程处理这些请求,从而实现显示消息框。

    2. 在以下描述中,()并不是多线程系统的特长。 A. 利用线程并行地执行矩阵乘法运算 B. Web 服务器利用线程响应 HTTP 请求 C. 键盘驱动程序为每个正在运行的应用配备一个线程,用以响应该应用的键盘输入 D. 基于 GUI 的调试程序用不同的线程分别处理用户输入、计算和跟踪等操作 我的答案:A 正确答案:C

      解析: 整个系统只有一个键盘,而且键盘输入是人的操作,速度比较慢,完全可以使用一个线程来处理整个系统的键盘输入。

    3. 下面关于用户级线程和内核级线程的描述中,错误的是()。 A. 采用轮转调度算法,进程中设置内核级线程和用户级线程的效果完全不同 B. 跨进程的用户级线程调度也不需要内核参与,控制简单 C. 用户级线程可以在任何操作系统中运行 D. 若系统中只有用户级线程,则处理机的调度对象是进程 我的答案:C 正确答案:B

      解析: 采用轮转调度算法时,如果进程中分别设置100个用户级线程和100个内核级线程,则前者的执行时间是后者的100倍,选项A正确。 在用户级线程中,系统感知不到线程的存在,调度的对象是进程,因此,跨进程的用户级线程调度需要内核参与,选项B错误,选项D正确。 用户级线程不需要内核的支持,与系统平台无关,对线程管理的代码是属于用户程序的一部分,选项C正确。

    4. 在内核级线程相对于用户级线程的优点的如下描述中,错误的是() A. 同一进程内的线程切换,系统开销小 B. 当内核线程阻塞时,CPU将会调度同一进程中的其他内核线程执行 C. 内核级线程的程序实体可以在内核态运行 D.对多处理器系统,核心可以同时调度同一进程的多个线程并行运行 我的答案:C 正确答案:A

      解析: 在内核级线程中,同一进程中的线程切换,需要从用户态转到核心态进行,系统开销较大,选项A错误。 CPU调度是在内核进行的,在内核级线程中,调度是在线程一级进行的,因此内核可以同时调度同一进程的多个线程在多CPU上并行运行(用户级线程则不行),选项 B 正确、选项 D 正确。 当进程中的内核级线程运行在内核态时,说明该进程也运行在内核态,选项C正确。

      纯是题目没读完整导致的😅

    2. 综合应用题

    1. 进程和程序之间可以形成一对一、一对多、多对一、多对多的关系,请分别举例说明在什么情况下会形成这样的关系。

      我的答案: 一对一:只有一个进程使用这个程序进行创建 一对多:一个进程使用多个程序进行创建 多对一:多个进程使用一个程序进行创建 多对多:多个进程,每个进程使用多个程序进行创建

      参考答案: 从进程的概念、进程与程序之间的关系来考虑问题的解答。进程是程序的执行过程,进程代表执行中的程序,因此进程与程序的差别就隐含在“执行”之中。程序是静态的指令集合,进程是程序的动态执行过程。静态的程序除占用磁盘空间外,不需要其他系统资源,只有执行中的进程才需要分配内存、CPU等系统资源。进程的定义说明了两点:

      1. 进程与程序相关,进程包含了程序。程序是进程的核心内容,没有程序就没有进程。
      2. 进程不仅仅是程序,还包含程序在执行过程中使用的全部资源。没有资源,程序就无法执行,因此进程是程序执行的载体。

      运行一个程序时,操作系统首先要创建一个进程,为进程分配内存等资源,然后加入进程队列中执行。对单个进程在某个时刻而言,一个进程只能执行一个程序,进程与程序之间是一对一的关系。但对整个系统中的进程集合及进程的生命周期而言,进程与程序之间可以形成一对一、多对一、一对多、多对多的关系。

      具体解答如下:

      执行一条命令或运行一个应用程序时,进程和程序之间形成一对一的关系。进程在执行过程中可以加载执行不同的应用程序,从而形成一对多的关系;以不同的参数或数据多次执行同一个应用程序时,形成多对一的关系;并发地执行不同的应用程序时,形成多对多的关系。

    2. 父进程创建子进程和主程序调用子程序有何不同?

      我的答案: 父进程创建子进程,在父进程撤销后,子进程也会随之撤销。

      参考答案: 父进程创建子进程后,父进程与子进程同时执行(并发)。主程序调用子程序后,主程序暂停在调用点,子程序开始执行,直到子程序返回,主程序才开始执行。

    3. 某分时系统中的进程可能出现如下图所示的状态变化,请回答下列问题:

      1. 根据图示,该系统应采用什么进程调度策略?

        时间片轮转

      2. 把图中每个状态变化的可能原因填写在下表中。

    11

    我的答案:

    变化原因
    1进程1接受调度进入运行态
    2进入”等待磁盘读文件“的阻塞队列,等待 IO 完成
    3进入”等待打印机输出“的阻塞队列,等待 IO 完成
    4唤醒进程
    5唤醒进程
    6时间片用完,重新进入就绪队列

     

    二、处理机调度

    0x00 调度的概念

    1. 调度的基本概念

    在多道程序系统中,进程的数量往往多于处理机的个数,因此进程争用处理机的情况在所难免。处理机调度是对处理机进行分配,即从就绪队列中按照一定的算法(公平、高效的原则)选择一个进程并将处理机分配给它运行,以实现进程并发地执行。处理机调度是多道程序操作系统的基础,是操作系统设计的核心问题。

    2. 调度的层次

    一个作业从提交开始直到完成,往往要经历以下三级调度,如图2.7所示。

    12

    (1) 高级调度(作业调度)

    按照一定的原则从外存上处于后备队列的作业中挑选一个(或多个),给它(们)分配内存、输入/输出设备等必要的资源,并建立相应的进程,以使它(们)获得竞争处理机的权利。简言之,作业调度就是内存与辅存之间的调度。每个作业只调入一次、调出一次。多道批处理系统中大多配有作业调度,而其他系统中通常不需要配置作业调度

    (2) 中级调度(内存调度)

    引入中级调度的目的是提高内存利用率和系统吞吐量。为此,将那些暂时不能运行的进程调至外存等待,此时进程的状态称为挂起态。当它们已具备运行条件且内存又稍有空闲时,由中级调度来决定把外存上的那些已具备运行条件的就绪进程再重新调入内存,并修改其状态为就绪态,挂在就绪队列上等待。中级调度实际上是存储器管理中的对换功能。

    (4) 低级调度(进程调度)

    按照某种算法从就绪队列中选取一个进程,将处理机分配给它。进程调度是最基本的一种调度,在各种操作系统中都必须配置这级调度。进程调度的频率很高,一般几十毫秒一次。

    3. 三级调度的联系

    作业调度从外存的后备队列中选择一批作业进入内存,为它们建立进程,这些进程被送入就绪队列,进程调度从就绪队列中选出一个进程,并把其状态改为运行态,把CPU分配给它。中级调度是为了提高内存的利用率,系统将那些暂时不能运行的进程挂起来。

    1. 作业调度为进程活动做准备,进程调度使进程正常活动起来。
    2. 中级调度将暂时不能运行的进程挂起,中级调度处于作业调度和进程调度之间。
    3. 作业调度次数少,中级调度次数略多,进程调度频率最高。
    4. 进程调度是最基本的,不可或缺。

    0x01 调度的目标(调度算法评价方法)

    不同的调度算法具有不同的特性,在选择调度算法时,必须考虑算法的特性。为了比较处理机调度算法的性能,人们提出了很多评价标准,下面介绍其中主要的几种:

    1. CPU 利用率。CPU是 计算机系统中最重要和昂贵的资源之一,所以应尽可能使 CPU 保持“忙”状态,使这一资源利用率最高。CPU利用率的计算方法如下:

      CPU=CPUCPU+CPU
    2. 系统吞吐量。表示单位时间内 CPU 完成作业的数量。长作业需要消耗较长的处理机时间,因此会降低系统的吞吐量。而对于短作业,需要消耗的处理机时间较短,因此能提高系统的吞吐量。调度算法和方式的不同,也会对系统的吞吐量产生较大的影响。

    3. 周转时间。指从作业提交到作业完成所经历的时间,是作业等待、在就绪队列中排队、在处理机上运行及输入/输出操作所花费时间的总和。周转时间的计算方法如下:

      =

      平均周转时间是指多个作业周转时间的平均值:

      =(1++n)/n

      带权周转时间是指作业周转时间与作业实际运行时间的比值:

      =

      平均带权周转时间是指多个作业带权周转时间的平均值:

      =(1++n)/n
    4. 等待时间。指进程处于等处理机的时间之和,等待时间越长,用户满意度越低。处理机调度算法实际上并不影响作业执行或输入/输出操作的时间,只影响作业在就绪队列中等待所花的时间。因此,衡量一个调度算法的优劣,常常只需简单地考察等待时间。

    5. 响应时间。指从用户提交请求到系统首次产生响应所用的时间。在交互式系统中,周转时间不是最好的评价准则,一般采用响应时间作为衡量调度算法的重要准则之一。从用户角度来看,调度策略应尽量降低响应时间,使响应时间处在用户能接受的范围之内。

    要想得到一个满足所有用户和系统要求的算法几乎是不可能的。设计调度程序,一方面要满足特定系统用户的要求(如某些实时和交互进程的快速响应要求),另一方面要考虑系统整体效率(如减少整个系统的进程平均周转时间),同时还要考虑调度算法的开销。

     

    0x02 调度的实现

    1. 调度程序 (调度器)

    用于调度和分派CPU的组件称为调度程序,它通常由三部分组成,如图2.8所示。

    13

    1. 排队器。将系统中的所有就绪进程按照一定的策略排成一个或多个队列,以便于调度程序选择。每当有一个进程转变为就绪态时,排队器便将它插入到相应的就绪队列中。
    2. 分派器。依据调度程序所选的进程,将其从就绪队列中取出,将CPU 分配给新进程。
    3. 上下文切换器。在对处理机进行切换时,会发生两对上下文的切换操作:第一对,将当前进程的上下文保存到其PCB中,再装入分派程序的上下文,以便分派程序运行;第二对,移出分派程序的上下文,将新选进程的CPU现场信息装入处理机的各个相应寄存器。

    在上下文切换时,需要执行大量 load 和 store 指令,以保存寄存器的内容,因此会花费较多时间。现在已有硬件实现的方法来减少上下文切换时间。通常采用两组寄存器,其中一组供内核使用,一组供用户使用。这样,上下文切换时,只需改变指针,让其指向当前寄存器组即可。

    2. 调度的时机、切换与过程

    调度程序是操作系统内核程序。请求调度的事件发生后,才可能运行调度程序,调度了新的就绪进程后,才会进行进程切换。理论上这三件事情应该顺序执行,但在实际的操作系统内核程序运行中,若某时刻发生了引起进程调度的因素,则不一定能马上进行调度与切换。

    现代操作系统中,不能进行进程的调度与切换的情况有以下几种:

    1. 在处理中断的过程中。中断处理过程复杂,在实现上很难做到进程切换,而且中断处理是系统工作的一部分,逻辑上不属于某一进程,不应被剥夺处理机资源
    2. 进程在操作系统内核临界区中。进入临界区后,需要独占式地访问,理论上必须加锁,以防止其他并行进程进入,在解锁前不应切换到其他进程,以加快临界区的释放。
    3. 其他需要完全屏蔽中断的原子操作过程中。如加锁、解锁、中断现场保护、恢复等原子操作。在原子过程中,连中断都要屏蔽,更不应该进行进程调度与切换。

    若在上述过程中发生了引起调度的条件,则不能马上进行调度和切换,应置系统的请求调度标志,直到上述过程结束后才进行相应的调度与切换。

    应该进行进程调度与切换的情况如下:

    1. 发生引起调度条件且当前进程无法继续运行下去时,可以马上进行调度与切换。若操作系统只在这种情况下进行进程调度,则是非剥夺调度
    2. 中断处理结束或自陷处理结束后,返回被中断进程的用户态程序执行现场前,若置上请求调度标志,即可马上进行进程调度与切换。若操作系统支持这种情况下的运行调度程序,则实现了剥夺方式的调度

    进程切换往往在调度完成后立刻发生,它要求保存原进程当前断点的现场信息,恢复被调度进程的现场信息。 现场切换时,操作系统内核将原进程的现场信息推入当前进程的内核堆栈来保存它们,并更新堆栈指针。 内核完成从新进程的内核栈中装入新进程的现场信息、更新当前运行进程空间指针、重设PC寄存器等相关工作之后,开始运行新的进程。

    3. 进程调度方式

    所谓进程调度方式,是指当某个进程正在处理机上执行时,若有某个更为重要或紧迫的进程需要处理,即有优先权更高的进程进入就绪队列,此时应如何分配处理机。

    通常有以下两种进程调度方式:

    1. 非抢占调度方式,又称非剥夺方式。‘是指当一个进程正在处理机上执行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在执行的进程继续执行,直到该进程运行完成或发生某种事件而进入阻塞态时,才把处理机分配给其他进程。 非抢占调度方式的优点是实现简单、系统开销小,适用于大多数的批处理系统,但它不能用于分时系统和大多数的实时系统
    2. 抢占调度方式,又称剥夺方式。是指当一个进程正在处理机上执行时,若有某个更为重要或紧追的进程需要使用处理机,则允许调度程序根据某种原则去暂停正在执行的进程;将处理机分配给这个更为重要或紧迫的进程。 抢占调度方式对提高系统吞吐率和响应效率都有明显的好处。但“抢占”不是一种任意性行为,必须遵循一定的原则,主要有优先权、短进程优先和时间片原则等。

    4. 闲逛进程

    在进程切换时,如果系统中没有就绪进程,就会调度闲逛进程(idle)运行,如果没有其他进程就绪,该进程就一直运行,并在执行过程中测试中断。闲逛进程的优先级最低,没有就绪进程时才会运行闲逛进程,只要有进程就绪,

    就会立即让出处理机。闲逛进程不需要CPU之外的资源,它不会被阻塞。

    5. 两种线程的调度

    1. 用户级线程调度。由于内核并不知道线程的存在,所以内核还是和以前一样,选择一个进程,并给予时间控制。由进程中的调度程序决定哪个线程运行。
    2. 内核级线程调度。内核选择一个特定线程运行,通常不用考虑该线程属于哪个进程。对被选择的线程赋予一个时间片,如果超过了时间片,就会强制挂起该线程。

    用户级线程的线程切换在同一进程中进行,仅需少量的机器指令;内核级线程的线程切换需要完整的上下文切换、修改内存映像、使高速缓存失效,这就导致了若干数量级的延迟。

     

    0x03 典型的调度算法

    操作系统中存在多种调度算法,有的调度算法适用于作业调度,有的调度算法适用于进程调度,有的调度算法两者都适用。下面介绍几种常用的调度算法。

    1。 先来先服务(FCFS)调度算法

    FCFS调度算法是一种最简单的调度算法,它既可用于作业调度,又可用于进程调度。在作业调度中,算法每次从后备作业队列中选择最先进入该队列的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。

    在进程调度中,FCFS调度算法每次从就绪队列中选择最先进入该队列的进程,将处理机分配给它,使之投入运行,直到运行完成或因某种原因而阻塞时才释放处理机。

    下面通过一个实例来说明FCFS调度算法的性能。假设系统中有 4 个作业,它们的提交时间分别是 8, 8.4, 8.8, 9,运行时间依次是 2, 1, 0.5, 0.2,系统采用 FCFS 调度算法,这组作业的平均等待时间、平均周转时间和平均带权周转时间见表 2.2。

    14

    FCFS 调度算法属于不可剥夺算法。从表面上看,它对所有作业都是公平的,但若一个长作业先到达系统,就会使后面的许多短作业等待很长时间,因此它不能作为分时系统和实时系统的主要调度策略。但它常被结合在其他调度策略中使用。例如,在使用优先级作为调度策略的系统中,往往对多个具有相同优先级的进程按FCFS 原则处理。

    FCFS调度算法的特点是算法简单,但效率低;对长作业比较有利,但对短作业不利(相对SJF 和高响应比);有利于 CPU 繁忙型作业,而不利于 I/O 繁忙型作业。

    2. 短作业优先 (SJF) 调度算法

    短作业(进程)优先调度算法是指对短作业(进程)优先调度的算法。短作业优先(SF)调度算法从后备队列中选择一个或若干估计运行时间最短的作业,将它们调入内存运行;短进程优先(SPF)调度算法从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给它,使之立即执行,直到完成或发生某事件而阻塞时,才释放处理机。

    例如,考虑表 2.2 中给出的一组作业,若系统采用短作业优先调度算法,其平均等待时间、平均周转时间和平均带权周转时间见下表:

    作业号提交时间运行时间开始时间等待时间完成时间周转时间带权周转时间
    182801021
    28.4110.72.311.73.33.3
    38.80.510.21.410.71.93.8
    490.210110.21.26

    平均等待时间:t=(0+2.3+1.4+1)/4=1.175 平均周转时间:T=(2+3.3+1.9+1.2)/4=2.1 平均带权周转时间:W=(1+3.3+3.8+6)/4=3.525

    SJF调度算法也存在不容忽视的缺点:

    1. 该算法对长作业不利,由表2.2和表2.3可知,SJF调度算法中长作业的周转时间会增加。更严重的是,若有一长作业进入系统的后备队列,由于调度程序总是优先调度那些(即使是后进来的)短作业,将导致长作业长期不被调度(“饥饿”现象,注意区分“死锁”,后者是系统环形等待,前者是调度策略问题)。
    2. 该算法完全未考虑作业的紧迫程度,因而不能保证紧迫性作业会被及时处理
    3. 由于作业的长短是根据用户所提供的估计执行时间而定的,而用户又可能会有意或无意地缩短其作业的估计运行时间,致使该算法不一定能真正做到短作业优先调度。

    注意,SJF调度算法的平均等待时间、平均周转时间最少。

    3. 优先级调度算法

    优先级调度算法既可用于作业调度,又可用于进程调度。该算法中的优先级用于描述作业的紧迫程度。在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它,使之投入运行。

    根据新的更高优先级进程能否抢占正在执行的进程,可将该调度算法分为如下两种:

    1. 非抢占式优先级调度算法。当一个进程正在处理机上运行时,即使有某个优先级更高的进程进入就绪队列,仍让正在运行的进程继续运行,直到由于其自身的原因而让出处理机时(任务完成或等待事件),才把处理机分配给就绪队列中优先级最高的进程。
    2. 抢占式优先级调度算法。当一个进程正在处理机上运行时;若有某个优先级更高的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给优先级更高的进程。

    而根据进程创建后其优先级是否可以改变,可以将进程优先级分为以下两种:

    1. 静态优先级。优先级是在创建进程时确定的,且在进程的整个运行期间保持不变。确定静态优先级的主要依据有进程类型、进程对资源的要求、用户要求。
    2. 动态优先级。在进程运行过程中,根据进程情况的变化动态调整优先级。动态调整优先级的主要依据有进程占有CPU时间的长短、就绪进程等待CPU时间的长短。

    一般来说,进程优先级的设置可以参照以下原则:

    1. 系统进程 > 用户进程。系统进程作为系统的管理者,理应拥有更高的优先级。
    2. 交互型进程 > 非交互型进程(或前台进程 > 后台进程)。大家平时在使用手机时,在前台运行的正在和你交互的进程应该更快速地响应你,因此自然需要被优先处理。
    3. IO 型进程 > 计算型进程。所谓的 I/O 型金恒,是指那些会频繁使用 I/O 设备的进程,而计算型进程是那些频繁使用CPU的进程(很少使用I/O设备)。我们知道,I/O 设备(如打印机)的处理速度要比CPU慢得多,因此若将I/O型进程的优先级设置得更高,就更有可能让I/O设备尽早开始工作,进而提升系统的整体效率。

    4. 高响应比优先调度算法

    高响应比优先调度算法主要用于作业调度,是对 FCFS 调度算法和SJF调度算法的一种综合平衡,同时考虑了每个作业的等待时间和估计的运行时间。在每次进行作业调度时,先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业投入运行。

    响应比的变化规律可描述为

    RP=+

    根据公式可知:

    1. 作业的等待时间相同时,要求服务时间越短,响应比越高,有利于短作业,因而类似于SJF。
    2. 要求服务时间相同时,作业的响应比由其等待时间决定,等待时间越长,其响应比越高,因而类似于FCFS。
    3. 对于长作业,作业的响应比可以随等待时间的增加而提高,当其等待时间足够长时,也可获得处理机,克服了“饥饿”现象。

    5. 时间片轮转调度算法

    时间片轮转调度算法主要适用于分时系统。在这种算法中,系统将所有就绪进程按 FCFS 策略排成-一个就绪队列,调度程序总是选择就绪队列中的第一个进程执行,但仅能运行一个时间片,如 50ms。在使用完一个时间片后,即使进程并未运行完成,它也必须释放出(被剥夺)处理机给下一个就绪进程,而被剥夺的进程返回到就绪队列的末尾重新排队,等候再次运行。

    在时间片轮转调度算法中,时间片的大小对系统性能的影响很大。若时间片足够大,以至于所有进程都能在一个时间片内执行完毕,则时间片轮转调度算法就退化为先来先服务调度算法。若时间片很小,则处理机将在进程间过于频繁地切换,使处理机的开销增大,而真正用于运行用户进程的时间将减少。因此,时间片的大小应选择适当,时间片的长短通常由以下因素确定:系统的响应时间、就绪队列中的进程数目和系统的处理能力。

    6. 多级队列调度算法

    前述的各种调度算法,由于系统中仅设置一个进程的就绪队列,即调度算法是固定且单一的,无法满足系统中不同用户对进程调度策略的不同要求。在多处理机系统中,这种单一调度策略实现机制的缺点更为突出,多级队列调度算法能在一定程度上弥补这一缺点。

    该算法在系统中设置多个就绪队列,将不同类型或性质的进程固定分配到不同的就绪队列。每个队列可实施不同的调度算法,因此,系统针对不同用户进程的需求,很容易提供多种调度策略。同一队列中的进程可以设置不同的优先级,不同的队列本身也可以设置不同的优先级。在多处理机系统中,可以很方便为每个处理机设置一个单独的就绪队列,每个处理机可实施各自不同的调度策略,这样就能根据用户需求将多个线程分配到一个或多个处理机上运行。

    7. 多级反馈队列调度算法(融合了前几种算法的优点)

    多级反馈队列调度算法是时间片轮转调度算法和优先级调度算法的综合与发展,如图2.9所示。通过动态调整进程优先级和时间片大小,多级反馈队列调度算法可以兼顾多方面的系统目标。例如,为提高系统吞吐量和缩短平均周转时间而照顾短进程:为获得较好的 I/O 设备利用率和缩短响应时间而照顾 I/O 型进程;同时,也不必事先估计进程的执行时间。

    15

    多级反馈队列调度算法的实现思想如下:

    1. 设置多个就绪队列,并为每个队列赋予不同的优先级。第1级队列的优先级最高,第2级队列的优先级次之,其余队列的优先级逐个降低。
    2. 赋予各个队列的进程运行时间片的大小各不相同。在优先级越高的队列中,每个进程的时间片就越小。例如,第 i+1级队列的时间片要比第i级队列的时间片长1倍。
    3. 每个队列都采用 FCFS 算法。当新进程进入内存后,首先将它放入第1级队列的末尾,按FCFS原则等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可撤离系统。若它在一个时间片结束时尚未完成,调度程序将其转入第2级队列的末尾等待调度;若它在第2级队列中运行一个时间片后仍未完成,再将它放入第3级队列·····,以此类推。当进程最后被降到第n级队列后,在第n级队列中便采用时间片轮转方式运行。
    4. 按队列优先级调度。仅当第1级队列为空时,才调度第2级队列中的进程运行;仅当第 1i1 级队列均为空时,才会调度第 i 级队列中的进程运行。若处理机正在执行第 i 级队列中的某进程时,又有新进程进入任何一个优先级较高的队列,此时须立即把正在运行的进程放回到第 i 级队列的末尾,而把处理机分配给新到的高优先级进程。

    多级反馈队列的优势有以下几点:

    1. 终端型作业用户:短作业优先。
    2. 短批处理作业用户:周转时间较短。
    3. 长批处理作业用户:经过前面几个队列得到部分执行,不会长期得不到处理。下表总结了几种常见进程调度算法的特点,读者要在理解的基础上掌握。
     先来先服务短作业优先高响应比优先时间片轮转多级反馈队列
    能否是可抢占队列内算法不一定
    能否是不可抢占队列内算法不一定
    优点公平,实现简单平均等待时间最少
    ,效率最高
    兼顾长短作业兼顾长短作业兼顾长短作业,有较好
    的响应时间,可行性强
    缺点不利于短作业长作业会饥饿,估计
    时间不易确定
    计算响应比的开销大平均等待时间较长,
    上下文切换浪费时间
    适用于作业调度
    批处理系统
    分时系统相当通用
    默认决策模式非抢占非抢占非抢占抢占抢占

     

    0x04 进程切换

    对于通常的进程而言,其创建、撤销及要求由系统设备完成的 I/O 操作,都是利用系统调用而进入内核,再由内核中的相应处理程序予以完成的。进程切换同样是在内核的支持下实现的,因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

    1. 上下文切换

    切换CPU到另一个进程需要保存当前进程状态并恢复另一个进程的状态,这个任务称为上下文切换。上下文是指某一时刻CPU寄存器和程序计数器的内容。进行上下文切换时,内核会将旧进程状态保存在其PCB中,然后加载经调度而要执行的新进程的上下文。

    上下文切换实质上是指处理机从一个进程的运行转到另一个进程上运行,在这个过程中,进程的运行环境产生了实质性的变化。上下文切换的流程如下:

    1. 挂起一个进程,保存CPU上下文,包括程序计数器和其他寄存器
    2. 更新 PCB 信息。
    3. 把进程的 PCB移入相应的队列,如就绪、在某事件阻塞等队列。
    4. 选择另一个进程执行,并更新其PCB。
    5. 跳转到新进程PCB中的程序计数器所指向的位置执行。
    6. 恢复处理机上下文。

    2. 上下文切换的消耗

    上下文切换通常是计算密集型的,即它需要相当可观的CPU时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间,所以上下文切换对系统来说意味着消耗大量的CPU时间。有些处理器提供多个寄存器组,这样,上下文切换就只需要简单改变当前寄存器组的指针。

    3. 上下文切换与模式切换

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

    注意:调度和切换的区别。调度是指决定资源分配给哪个进程的行为,是一种决策行为;切换是指实际分配的行为,是执行行为。一般来说,先有资源的调度,然后才有进程的切换。

     

    0x05 错题整理

    1. 【2012 统考真题】若某单处理器多进程系统中有多个就绪态进程,则下列关于处理机调度的叙述中,错误的是()。 A. 在进程结束时能进行处理机调度 B. 创建新进程后能进行处理机调度 C. 在进程处于临界区时不能进行处理机调度 D. 在系统调用完成并返回用户态时能进行处理机调度 我的答案:A 正确答案:C

      选项A、B、D显然属于可以进行处理机调度的情况。对于选项C,当进程处于临界区时,说明进程正在占用处理机,只要不破坏临界资源的使用规则,就不会影响处理机的调度。比如,通常访问的临界资源可能是慢速的外设(如打印机),若在进程访问打印机时,不能进行处理机调度,则系统的性能将非常差。