考研操作系统-2.进程管理(王道考研

目录

程序执行

进程与线程

进程的概念

进程的特征

进程的状态与转换

进程的通信

线程概念和多线程模型

CPU调度

调度的概念

调度的时机、切换

进程调度方式

典型的调度算法

先来先服务(FCFS, First Come First Serve)

短作业优先(SJF, Shortest Job First)

高响应比优先(HRRN, Highest Response Ratio Next)

时间片轮转(RR, Round-Robin)

优先级调度算法

多级反馈队列调度算法

进程同步

进程同步的基本概念

实现临界区互斥

软件实现方法

硬件实现方法

信号量

整型信号量

记录型信号量

信号量实现互斥

信号量实现同步

信号量实现前驱

管程

条件变量


 

程序执行

  1. 程序顺序执行的特征

    顺序性:处理机按照程序所规定的顺序执行,即每一操作结束后开始下一个操作。

    封闭性:程序执行时资源的状态(除初始状态外)只有本程序才能改变它,执行结果不受外界因素影响(程序运行时独占全机资源)。

    可再现性:环境和初始条件相同,当程序重复执行时,都可获得相同的结果。

  2. 程序并行执行的特征

    间断性并发执行时,共享系统资源,以及为完成同一项任务而相互合作,程序之间形成了相互制约的关系

    失去封闭性:并发执行的程序共享资源,任一程序在运行时,其他环境都必然会受到其他程序的影响

    不可再现性:程序在并发执行时,由于失去了封闭性,其计算结果必将与并发程序的执行速度有关,从而使程序的执行失去了可再现性。换言之,程序经过多次执行后,虽然它们执行时的环境和初始条件相同,但得到的结果却各不相同

进程与线程

进程的概念

  1. 由于程序并行执行的特征,引入进程的目的是为了使进程实体并发执行,实现操作系统的并发性和共享性(最基本的两个特征)。

  2. 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,。包括程序的代码本身和当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

  3. 程序和进程的区别:

    程序:是静态的,就是个存放在磁盘里的可执行文件。

    进程:是动态的,是程序的一次执行过程,是一个活动的实体。同一个程序多次执行会对应多个进程。

  4. 进程实体(进程映像)组成——PCB、程序段、数据段

    PCB、程序段、相关的数据段构成了进程实体(又称进程映像)

    PCB是进程存在的唯一标志。操作系统通过PCB来描述进程的基本情况和活动过程,进而控制管理进程。操作系统需要对各个并发运行的进程进行管理,管理时所需要的信息存储在PCB中。为了使参与并发执行的每个程序(含数据)都能独立地运行,在操作系统中登记状态信息的数据结构,称为进程控制块(PCB)。当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的“身份证号”—— PID(Process ID,进程ID)。操作系统区分各个进程就是通过PID以及进程所属用户ID(UID)来区分的。所以操作系统要记录PID、UID,还要记录给进程分配了哪些资源(如:分配了多少内存、正在使用哪些I/O设备、正在使用哪些文件),还要记录进程的运行情况(如:CPU使用时间、磁盘使用情况、网络流量使用情况等)。这些信息都被保存在PCB中。​​

  5. 注意:进程则是动态的,进程映像是静态的。

  6. PCB是给操作系统用的,程序段和数据段是给进程自己用的。所谓创建进程,实质上是创建进程映像中的PCB,而撤销进程,实质上是撤销进程的PCB。

  7. 进程典型的定义有:
    ①进程是程序的一次执行过程
    ②进程是一个程序及其数据在处理机上顺序执行时所发生的活动
    ③进程是具有独立功能的程序在一个数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单位

  8. 引入进程实体后,进程可定义为:“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
    要注意这里说的系统资源是指处理机、存储器和其他设备服务于某个进程的“时间”,例如把处理机资源理解为处理机的时间片才是准确的。因为进程是这些资源分配和调度的独立单位,即“时间片”分配的独立单位,这就决定了进程一定是一个动态的、过程性的概念。

进程的特征

进程是由多道程序的并发执行而引出的,和程序是两个不同的概念。拥有以下特征:

并发性:指多个进程实体同时存于内存中,能在一段时间内运行。并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是是程序能与其他进程的程序并发执行,以提高资源利用率。

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

独立性:指进程实体(PCB,程序段,数据段)是能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序,都不能作为一个独立的单位参与运行。

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

结构性每个进程都配置一个PCB对其进行描述。

进程的状态与转换

  1. 进程的三种基本状态

    由于多个进程在并发执行时共享系统资源,致使它们在运行过程中呈现间断性的运行规律。

    • 就绪态。进程获得了除处理机外的一切所需资源,一旦得到处理机,便可立即运行。系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
    • 执行态。进程正在处理机上运行。在单处理机环境下,每个时刻最多只有一个进程处于运行态。而在多处理机系统中,则有多个进程处于执行状态。
    • 阻塞态。又称等待态或封锁态。指正在执行的进程由于发生某事件(如I/O请求、申请缓冲区失败等)暂时无法继续执行时的状态,亦即进程的执行受到阻塞。此时引起进程调度,OS把处理机分配给另一个就绪进程,而让受阻进程处于暂停状态,这种暂停状态称为阻塞态。通常系统将处于阻塞态的进程也排成一个队列,称该队列为阻塞队列。在较大的系统中,为了提高系统效率,根据阻塞原因的不同会设置多个阻塞队列。

    注意:区别就绪态和阻塞态。就绪态是指进程仅缺少处理机,只要获得处理机资源就立即运行;而阻塞态是指进程需要其他资源(包括处理机)或等待某一事件

    之所以把处理机和其他资源划分开,是因为在分时系统的时间片轮转机制中,进程在运行过程中实际上要频繁地转换到就绪态;而其他资源(如外设)的使用和分配或某一事件的发生(如I/O操作的完成)对应的时间相对来说很长,进程转换到等待态的次数也相对较少。

  2. 创建状态和终止状态

    • 创建态。进程正在被创建,尚未转到就绪态。创建进程通常需要多个步骤:首先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息;然后又系统为该进程分配运行时所必需的资源;最后把该进程转入就绪态。
    • 结束态。进程正从系统中消失,可能是进程正常结束或其他原因中断退出运行。进程需要结束运行时,系统首先必须将该进程置为结束态,然后进一步处理资源释放和回收等。

    进程PCB中,会有一个变量 state 来表示进程的当前状态。方便对同一个状态下的各个进程进行统一的管理,操作系统会将各个进程的PCB组织起来。

  3. 挂起操作和进程状态的转换

    挂起操作:当该操作作用于某个进程时,该进程将被挂起,意味着此时该进程处于静止状态。与挂起操作对应的操作是激活操作。

    正在执行的进程挂起后,暂停执行

    就绪状态的进程挂起后不接受调度

    阻塞进程挂起后不能直接转换为就绪(需先激活)

  4. 挂起操作的引入是基于系统和用户的如下需要:

    ①终端用户的需要。当终端用户在自己的程序运行期间发现有可疑问题,希望暂停自己的程序的运行。
    ②父进程请求。有时父进程希望挂起自己的某个子进程。
    ③负荷调节的需要。系统把一些不重要的进程挂起。
    ④操作系统的需要。操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。

  5. 挂起和阻塞态的区别:

    挂起:由系统或程序发出,移至辅存中去。(不释放CPU,可能释放内存,移到外存去)

    阻塞态:在抢占资源中得不到资源,被动的挂起在内存,等待某种资源或信号量将它唤醒。(释放CPU,不释放内存)

    其中,创建→活动就绪:在当前系统的性能和内存的容量均允许的情况下,完成对进程创建的必要操作后,相应的系统进程将进程的状态转换为活动就绪状态。

    创建→静止就绪:考虑到系统当前资源状况和性能的要求,不分配给新建进程所需资源,主要是内存,相应的系统将进程状态转为静止就绪状态,被安置在外存,不参与调度,此时进程创建工作尚未完成。

  6. 进程管理中的数据结构

    在操作系统中登记状态信息的数据结构,在操作系统中登记状态信息的数据结构,用于控制和管理进程的称为进程控制块(PCB)。

    PCB作为进程实体的一部分,记录了操作系统所需的,用于描述进程的当前情况以及管理进程运行的全部信息,是操作系统中最重要的记录型数据结构。它使一个在多道程序环境下不能独立运行的程序(含数据)成为一个能独立运行的基本单位,一个能与其他进程并发执行的进程。

  7. PCB的具体作用:
    ①作为独立运行基本单位的标志。PCB是进程存在的唯一标志
    ②能实现间断性运行方式。在多道程序环境下,当进程因阻塞而暂停运行时,它必须保留自己运行时的CPU现场信息。在有了PCB后,系统就可将CPU现场信息保存在被中断进程的PCB中,供该进程再次被调度执行时恢复CPU现场时使用
    ③提供进程管理所需信息。
    ④提供进程调度所需信息。
    ⑤实现了其他进程的同步通信。

  8. PCB中的信息:
    ①进程标识符。进程标识符用于唯一地标识一个进程。其中包括外部标识符(创建者提供)和内部标识符(OS设置)。
    ②处理机状态。也称为处理机的上下文。主要由处理机中各种寄存器中的内容组成,包括通用寄存器、指令计数器、程序状态字PSW、用户栈指针等。
    ③进程调度信息。包括进程状态、进程优先级、事件和进程调度所需的其他信息。
    ④进程控制信息。包括程序和数据的地址、进程同步和通信机制、资源清单和链接指针。

  9. 进程的组织方式

    线性方式。即将系统中所有的PCB都组织在一张线性表中,将该表的首地址存放在内存的一个专用区域中。该方法实现简单、开销小,但每次查找时的需要扫描整张表,因此适合数目不多的系统。

    链接方式。即把具有相同状态进程的PCB分别通过PCB中的链接字链接成一个队列。

    索引方式。即系统根据所有进程状态的不同,建立几张索引表。

进程的通信

  1. 进程通信是指进程之间的信息交换。进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立。为了保证安全,一个进程不能直接访问另一个进程的地址空间。

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

  2. 共享存储,在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换。为避免出错,各个进程对共享空间的访问应该是互斥的。各个进程可使用操作系统内核提供的同步互斥工具(如P、V操作)。操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。

    注:通过“增加页表项/段表项”即可将同一片共享内存区映射到各个进程的地址空间中。用户进程空间一般都是独立的,进程运行期间一般不能访问其他进程的空间,要想让两个用户进程共享空间,必须通过特殊的系统调用实现,而进程内的现场是自然共享进程空间的。

    • 基于数据结构的共享

      比如共享空间里只能放一个长度为10的数组。这种共享方式仅适用于传递相对少量的数据,速度慢、限制多,是一种低级通信方式。

    • 基于存储区的共享

      操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式。

  3. 消息传递最广泛的使用高级通信方式

    进程间的数据交换以格式化的消息(Message)为单位(不同环境下,消息格式不同)。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方式实现进程通信。

    在计算机网络中,消息又称为报文;在微内核与服务器之间的通信也都是采用了消息传递机制。
    基于消息传递的通信方式属于高级通信方式,因其实现方式的不同,可进一步分为两类:

    • 直接通信方式(点名道姓的消息传递)

      发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。

    • 注:由于PCB是保存在操作系统内核中,所以消息队列也是在内核中。

    • 间接通信方式(以“信箱”作为中间实体进行消息传递)

      发送进程把消息发送到某个中间实体,接收进程从中间实体取得消息。这种中间实体一般称为信箱,这种通信方式又称信箱通信方式。该通信方式广泛应用于计算机网络中,相应的通信系统称为电子邮件系统。

  4. 管道通信

    管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。

    各进程要互斥地访问管道(由操作系统实现)。管道的底层数据结构是一个循环队列

    当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。
    当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
    管道中的数据一旦被读出,就彻底消失。因此,当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:①一个管道允许多个写进程,一个读进程;②允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据(Linux 的方案)。

    向管道(共享文件)提供输入的发送进程(即写进程),以字节流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程)则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供三方面的协调能力:同步、互斥、确定对方的存在

    写进程往管道写数据,即便管道没被写满,只要管道没空,读进程就可以从管道读数据。读进程从管道读数据,即便管道没被读空,只要管道没满,写进程就可以往管道写数据。

    从管道读取数据是一次性操作,数据一旦被读取,它就从管道中被抛弃,释放空间以便写更多的数据,管道只能采用半双工通信,即某一时刻只能单向传输,要实现父子进程双方互动通信,需要定义两个管道。

线程概念和多线程模型

  1. 线程的引入,还没引入进程之前,系统中各个程序只能串行执行。

    OS中引入进程的目的是为了使多个程序能并发执行,以提高资源利用率和系统吞吐量

    OS中引入线程,是为了减少程序在并发执行时所付出的时空开销,使OS具有更好的并发性,增加了并发度

  2. 线程的基本概念

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

    进程是拥有资源的基本单位,而线程不拥有系统资源(只有一点必不可少的资源),同一个进程的线程共享进程所拥有的全部资源。

    一个线程可以创建和撤销另一个线程,同个进程中的多个线程之间并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性

    线程有就绪、阻塞和运行三种基本状态。

  3. 引入线程之后,进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务(如QQ视频、文字聊天、传文件等可以并发执行)。引入线程后,进程只作为除CPU之外的系统资源的分配单元(如打印机、内存地址空间等都是分配给进程的)。线程则作为处理机的分配单元

  4. 线程与进程的比较

    • 调度:在传统的OS中,进程是资源分配、调度的基本单位。引入线程后,进程是资源分配的基本单位,线程是调度的基本单位在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,会引起进程切换。
    • 拥有资源:进程是拥有资源的基本单位,而线程不拥有系统资源(只有一点必不可少的资源),同一个进程的线程共享进程所拥有的全部资源。
    • 并发性:在引入线程后,各线程间也能并发,提升了并发度。
    • 系统开销:传统的进程间并发时,需要切换进程的运行环境,系统开销很大。而线程间并发时,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小
  5. 线程的属性【总结】

    线程是处理机调度的单位

    多CPU计算机中,各个线程可占用不同的CPU

    每个线程都有一个线程ID、线程控制块(TCB)

    就绪、阻塞、运行三种基本状态

    线程几乎不拥有系统资源
    同一进程的不同线程间共享进程的资源

    线程共享内存地址空间,线程没有自己独立的地址空间同一进程中的线程间通信甚至无需系统干预,可以直接通过它们共享的存储空间进行通信

    同一进程中的线程切换,不会引起进程切换
    切换同进程内的线程,系统开销很小
    切换进程,系统开销较大

  6. 线程的实现方式

    线程的实现可以分为两类:用户级线程(User-Level Thread,ULT)和内核级线程(Kernel-Level Thread,KLT)。内核级线程又称内核支持的线程。(按切换时是否依赖内核进行区分

    • 用户级线程(ULT)

      在用户级线程中,有关线程管理(线程的创建、撤销和切换等)的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。在用户级线程的系统中,其调度仍是以进程为单位

      很多编程语言提供了强大的线程库,可以实现线程的创建、销毁、调度等功能。

      1)用户级线程由应用程序通过线程库实现,所有的线程管理工作都由应用程序负责(包括线程切换)
      2)用户级线程中,线程切换可以在用户态下即可完成,无需操作系统干预。
      3.)在用户看来,是有多个线程。但是在操作系统内核看来,并意识不到线程的存在。“用户级线程”就是“从用户视角看能看到的线程”。
      4.)优缺点
      优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。用户级线程的实现与OS无关,因为对于线程管理的代码是属于用户程序的一部分,所有的应用程序都可以对之进行共享。因此,用户级线程甚至可以在不支持线程机制的操作系统平台上实现。
      缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行,因为内核每次分配给一个进程的仅有一个CPU,因此,进程中仅有一个线程能执行,在该线程放弃CPU1之前,其他线程只能等待。

    • 内核级线程(KLT,又称“内核支持的线程”)
      在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为基础及其内部的每个线程维护上下文信息,调度也在内核基于线程架构的基础上完成,调度是以线程为单位

      1)内核级线程的管理工作由操作系统内核完成。
      2)线程调度、切换等工作都由内核负责,因此内核级线程的切换必然需要在核心态下才能完成
      3)操作系统会为每个内核级线程建立相应的TCB(Thread Control Block,线程控制块),通过TCB对线程进行管理。“内核级线程”就是“从操作系统内核视角看能看到的线程”。
      4)优缺点
      优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
      缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态(用户进程的线程在用户态运行,而线程调度和管理在内核实现的),因此线程管理的成本高,开销大
      总结:不论是进程还是线程,都必须直接或间接地取得内核的支持。由于内核级线程可以直接利用系统调用为它服务,故线程的控制相当简单;而用户级线程必须借助于某种形式的中间系统的帮助才能取得内核的服务,故在对线程的控制上要稍微复杂些

  7. 多线程模型

    根据用户级线程和内核级线程的映射关系,可以划分为几种多线程模型。

    一对一模型一个用户级线程映射到一个内核级线程。每个用户进程有与用户级线程同数量的内核级线程。
    优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
    缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大

    • 多对一模型

      多个用户级线程映射到一个内核级线程。这些用户线程一般属于一个进程,运行在该进程的用户空间,对这些线程的调度和管理也是在该进程的用户空间中完成。仅当用户线程需要访问内核时,才将其映射到一个内核控制线程上,但每次只允许一个线程进行映射。
      优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。
      缺点:当一个用户级线程被阻塞后(一个阻塞全体阻塞),整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行。
      重点重点重点:
      操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位

    • 多对多模型

      n 用户及线程映射到 m 个内核级线程(n >= m)。每个用户进程对应 m 个内核级线程。
      克服了一对一模型中一个用户进程占用多个内核级线程,开销太大的缺点。又克服了多对一模型并发度不高的缺点(一个阻塞全体阻塞)
      可以这么理解:
      用户级线程是“代码逻辑”的载体。
      内核级线程是“运行机会”的载体。
      内核级线程才是处理机分配的单位。例如:多核CPU环境下,上面这个进程最多能被分配两个核。一段“代码逻辑”只有获得了“运行机会”才能被CPU执行。内核级线程中可以运行任意一个有映射关系的用户级线程代码,只有两个内核级线程中正在运行的代码逻辑都阻塞时,这个进程才会阻塞。

    补充

    每个进程包含独立的地址空间,进程各种的地址空间是私有的,只能指向自己地址空间中的程序,且只能访问自己地址空间中的数据,相互访问会导致指针的越界错误。因此,进程之间不能直接交换数据,但可利用操作系统提高的共享文件、消息传递、共享存储区等进行通信。

    进程与程序的根本区别是静态和动态特点。

    进程调度时,优先级分静态和动态两种,动态优先级是根据运行情况而随时调整的;静态优先级则是在创建进程时确定,且在进程的整个运行期间是保持不变的。

    单处理器系统中,并不是任何时刻都有一个进程处于运行态。因为在系统发生死锁时有可能进程全部都处于阻塞态或无进程任务。
    在任何时刻,一个进程的状态变化不一定引起另一个进程的状态变化。例如,一个进程时间片用完,可能会引起另一个就绪进程的运行。但一个进程由阻塞态转变为就绪态就不会引起其他进程的状态变化。

    在单处理机系统中,若同时存在10个进程,则处于就绪队列中的进程最多有9个,1个正在运行。但处于阻塞队列中的进程最多就有可能是10个。

    系统进程所请求的一次I/O操作完成前,进程处于阻塞态;当I/O操作完成后,就转变为就绪态。

    进程在处理机上执行时,进程之间可能是无关的,有可能是有交互性的

    C语言编写的程序在使用内存时一般分为三个段,正文段(即代码和赋值数据段)、数据堆段和数据栈段。

    二进制代码和常量,全局赋值变量存放在正文段;临时变量,未赋值的局部变量和实参传递在数据栈段,动态内存分配在数据堆段。进程的优先级只能在PCB中

    一个进程是程序在一个数据集上的一次运行过程。程序运行于不同的数据集,将会形成不同的进程。

    系统动态DLL库中的系统线程,被不同的进程所调用,它们是相同的进程。进程是暂时的,程序是永久的;进程由PCB、程序段、数据段组成,程序仅需代码和数据即可;程序代码经过多次创建可对应不同的进程,而同一个系统的进程(或线程)可以由系统调用的方法被不同的进程(或线程)多次使用。

    一个计算机系统中,进程的最大数目主要受到内存大小的限制。进程创建需要占用系统内存来存放PCB的数据结构,所以一个系统能够创建的进程总数是有限的,进程的最大数目取决于系统内存的大小

    整个系统只有一个键盘,而且键盘输入是人的操作,速度比较漫,完全可以使用一个线程来处理整个系统的键盘输入,无须利用多线程。

    不同进程拥有不同的代码段和数据段,全局变量是对同一进程而言的,在不同的进程中是不同的变量,没有任何联系,所以不能用全局变量来交换数据

    引起进程从运行态转换为就绪态的事件可能是时间片用完或者是出现优先级更高的进程

    进程时间片用完可以调整进程优先级,因为可以让其他进程被调度进入执行状态。当进程刚完成I/O操作,进入就绪队列等待被处理机调度时,为了让其尽快处理I/O结果,就应该提高优先级。当进程长期处于就绪队列时,为了不至于产生饥饿现象,也应提高优先级。

    多线程是指一个程序中可以定义多个线程并同时运行它们,每个线程可以执行不同的任务。多线程与多任务的区别多任务是针对操作系统而言的,代表操作系统可以同时执行的(相同或不同)程序个数多线程是针对一个程序而言的,代表一个程序可以同时执行的线程个数,而每个线程可以完成不同的任务

    若系统中没有运行进程,则一定没有就绪进程。因为若系统中未运行进程,则系统很快会选择一个就绪进程运行,只有就绪队列中无进程时,CPU才可能处于空闲状态。

    在采用优先级进程调度时,运行进程不一定是系统中优先级最高的进程。因为高优先级的进程有可能正处于等待队列中,进程调度会从就绪队列中选择一个进程占用CPU,这个被选中的进程可能优先级较低。

CPU调度

调度的概念

  1. 确定某种规则来决定处理任务的顺序,调度的实质是一种资源分配。

    一个作业从提交开始直到完成,往往要经历以下三种调度:

    • 高级调度(作业调度)

      调度对象是作业。其主要功能是根据某种算法,决定将外存上处于后备队列中的哪几个作业调入内存,为它们创建进程、分配资源,将它们放入到就绪队列。高级调度主要用于多道批处理系统中,而其他系统通常不配置作业调度,作业调度的执行效率较低,通常为几分钟一次。对于每个作业只调入一次,调出一次。

    • 中级调度(内存调度)

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

    • 低级调度(进程调度)

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

  2. 进程的挂起态与七状态模型

    暂时调到外存等待的进程状态为挂起状态(挂起态,suspend)。挂起态又可以进一步细分为阻塞挂起、就绪挂起两种状态。

    注意“挂起”和“阻塞”的区别,两种状态都是暂时不能获得CPU的服务,挂起态是将进程映像调到外存去了,而阻塞态下进程映像还在内存中。

​​

调度的时机、切换

  1. 进程调度的时机

    进程调度(低级调度),就是按照某种算法从就绪队列中选择一个进程为其分配处理机

    1)需要进行进程调度与切换的情况

    • 当前运行的进程主动放弃处理机
      ①进程正常终止
      ②运行过程中发生异常而终止
      ③进程主动请求阻塞(如 等待I/O)
    • 当前运行的进程被动放弃处理机
      ①分给进程的时间片用完
      ②有更紧急的事需要处理(如 I/O中断)
      ③有更高优先级的进程进入就绪队列

    2)不能运行进程调度与切换的情况
    ①在处理中断的过程中。中断处理过程复杂,与硬件密切相关,很难做到在中断处理过程中进行进程切换。
    ②进程在操作系统内核程序临界区中。(注意:进程调度和切换程序都是操作系统内核程序)
    ③在原子操作过程中(原语)。原子操作不可中断,要一气呵成(如之前讲过的修改PCB中进程状态标志,并把PCB放到相应队列就属于原子操作)

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

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

    在访问临界区时不一定能调度。在访问普通临界区时可以进行调度与切换,在访问内核程序临界区期间时不能进行调度与切换;

  1. 进程切换过程

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

    “狭义的进程调度”与“进程切换”的区别:

    狭义的进程调度指的是从就绪队列中选择一个要运行的进程。(这个进程可以是刚刚被暂停执行的进程,也可能是另一个进程,后一种情况就需要进程切换)

    进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程

    广义的进程调度包含了选择一个进程和进程切换两个步骤。

    进程切换的过程主要完成了
    ①对原来运行进程各种数据的保存

    ②对新进程的数据恢复(如:程序计数器、程序状态字、各种数据寄存器等处理机现场信息,这些信息一般保存在进程控制块)

    注意:进程切换是有代价的,因此如果过于频繁的进行进程调度、切换,必然会使整个系统的效率降低,使系统大部分时间都花在了进程切换上,而真正用于执行进程的时间减少。

进程调度方式

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

    通常由以下两个进程调度方式:

    • 非剥夺调度方法

      非剥夺调度方式,又称非抢占方式。即,只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终止或主动要求进入阻塞态。在非剥夺调度方式下,一旦把CPU分配给一个进程,该进程就会保持CPU直到终止或转换到阻塞态

      这种方法实现简单,系统开销小但是无法及时处理紧急任务,适合于早期的批处理系统,不能用于分时系统和大多数的实时系统。

    • 剥夺调度方法
      剥夺调度方式,又称抢占方式。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。

      这种方法可以优先处理更紧急的进程,也可实现让各进程按时间片轮流执行的功能(通过时钟中断)。适合于分时操作系统、实时操作系统。采用剥夺式的调度,对提高系统吞吐率和响应效率都有明显的好处。但“剥夺”不是一种任意性行为,必须遵循一定的原则,主要由优先权、短进程优先和时间片原则等。

对于周转时间相同的两个作业,实际运行时间长的作业在相同时间内被服务的时间更多,带权周转时间更小,用户满意度更高。
对于实际运行时间相同的两个作业,周转时间短的带权周转时间更小,用户满意度更高

等待时间

等待时间,指进程/作业处于等待处理机状态时间之和

对于进程来说,等待时间就是指进程建立后等待被服务的时间之和,在等待I/O完成的期间其实进程也是在被服务的,所以不计入等待时间。
对于作业来说,不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待的时间
一个作业总共需要被CPU服务多久,被I/O设备服务多久一般是确定不变的,因此调度算法只会影响作业/进程的等待时间。当然,与前面指标类似,也有“平均等待时间”来评价整体性能。
 

响应时间

对于计算机用户来说,会希望自己的提交的请求(比如通过键盘输入了一个调试命令)尽早地开始被系统服务、回应。
响应时间,指从用户提交请求到首次产生响应所用的时间。

典型的调度算法

作业的概念

作业是一个比程序更为广泛的概念,不仅包含了通常的程序和数据,而且还应配有一份作业说明书,系统根据该说明书来对程序的运行进行控制。在批处理系统中,是以作业为基本单位从外存调入内存的。

作业从进入系统到运行结束,通常需要经历收容、运行和完成三个阶段。相应的作业也就有“提交状态”、“后备状态”、“运行状态”和“完成状态”。

调度算法:

先来先服务(FCFS, First Come First Serve)

最简单的调度算法,可用于作业调度和进程调度。

用于作业调度时,考虑的是哪个作业先到达后备队列。在作业调度中,算法每次从后备作业队列中选择最先进入该队列的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。

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

算法思想: 主要从“公平”的角度考虑

算法规则: 按照作业/进程到达的先后顺序(等待时长)进行服务。

是否可抢占? 非抢占式的算法

优缺点:FCFS算法对长作业有利,对短作业不利。 优点:公平、算法实现简单。缺点:排在长作业(进程)后面的短作业需要等待很长时间,带权周转时间很大。

是否会导致饥饿(某进程/作业长期得不到服务): 不会

短作业优先(SJF, Shortest Job First)

 

短作业优先(SJF)调度算法从后备队列中选择一个或者若干估计运行时间最短的作业,将它们调入内存运行;

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

算法思想: 追求最少的平均等待时间,最少的平均周转时间、最少的平均带权周转时间

算法规则: 最短的作业/进程优先得到服务(所谓“最短”,是指要求服务时间最短)

用于作业/进程调度: 即可用于作业调度,也可用于进程调度(短进程优先SPF, Shortest Process First算法)。

是否可抢占? SJF和SPF是非抢占式的算法。但是也有抢占式的版本——最短剩余时间优先算法(SRTN, Shortest Remaining Time Next)

优缺点: 优点:“最短的”(这里的最短的是得看条件的)平均等待时间、平均周转时间。

缺点:不公平。对短作业有利,对长作业不利,且不一定能做到真正的短作业优先。作业/进程的运行时间是由用户提供(用户估计的)的,并不一定真实。

是否会导致饥饿: 会。如果源源不断地有短作业/进程到来,可能使长作业/进程长时间得不到服务,产生“饥饿”现象。如果一直得不到服务,则称为“饿死”。

注意:
“在所有进程同时可运行时,采用SJF调度算法的平均等待时间、平均周转时间最少”;或者说“在所有进程都几乎同时到达时,采用SJF调度算法的平均等待时间、平均周转时间最少”;
如果不加上述前提条件,则应该“抢占式的短作业/进程优先调度算法(最短剩余时间优先, SRNT算法)的平均等待时间、平均周转时间最少”
虽然严格来说,SJF的平均等待时间、平均周转时间并不一定最少,但相比于其他算法(如 FCFS),SJF依然可以获得较少的平均等待时间、平均周转时间。如果选择题中遇到“SJF 算法的平均等待时间、平均周转时间最少”的选项,那最好判断其他选项是不是有很明显的错误。

高响应比优先(HRRN, Highest Response Ratio Next)

既考虑作业的等待时间,又考虑作业运行时间的调度算法,因此既照顾了短作业,又不致使长作业的等待时间过长,从而改善了处理机调度的性能。

算法思想: 要综合考虑作业/进程的等待时间和要求服务的时间

算法规则: 在每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务。

用于作业/进程调度: 即可用于作业调度,也可用于进程调度

是否可抢占? 非抢占式的算法。因此只有当前运行的作业/进程主动放弃处理机时,才需要调度,才需要计算响应比。

优缺点: 优点:综合考虑了等待时间和运行时间(要求服务时间);

等待时间相同时,要求服务时间短的优先(SJF 的优点);

要求服务时间相同时,等待时间长的优先(FCFS 的优点);对于长作业来说,随着等待时间越来越久,其响应比也会越来越大,从而避免了长作业饥饿的问题。

缺点:每次要进行调度之前,都需要先做响应比的计算,显然会增加系统开销。

是否会导致饥饿: 不会

时间片轮转(RR, Round-Robin)

时间片轮转调度算法主要适用于分时系统,更加注重“响应时间”。

算法思想: 公平地、轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到响应。

算法规则: 按照各进程到达就绪队列的顺序,轮流让各个进程执行一个时间片(如 100ms)。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列队尾重新排队。

用于作业/进程调度: 用于进程调度,没有作业调度(作业要先放入内存建立了相应的进程后,才能被分配处理机时间片,所以没有作业调度)。

是否可抢占?抢占式的算法。 若进程未能在时间片内运行完,将被强行剥夺处理机使用权,由时钟装置发出时钟中断来通知CPU时间片已到。

优缺点: 优点:公平;响应快,适用于分时操作系统;缺点:由于高频率的进程切换,因此有一定开销;不区分任务的紧急程度。

是否会导致饥饿: 不会

补充: 若选择很小的时间片,将有利于短作业,因为它能在该时间片内完成。但时间片小,意味着会频繁地执行进程调度和进程上下文的切换,这无疑会增加系统的开销。反之,若时间片选择得太长,且为使每个进程都能在一个时间片内完成,RR算法便退化为FCFS算法,无法满足短作业和交互式用户的需求。

优先级调度算法

优先级调度算法又称优先权调度算法,可用于作业调度和进程调度。

算法思想: 随着计算机的发展,特别是实时操作系统的出现,越来越多的应用场景需要根据任务的紧急程度来决定处理顺序。

算法规则: 每个作业/进程有各自的优先级,调度时选择优先级最高的作业/进程。

用于作业/进程调度: 既可用于作业调度,也可用于进程调度。甚至,还会用于在之后会学习的I/O调度中。

是否可抢占? 抢占式、非抢占式都有。

做题时的区别在于:非抢占式只需在进程主动放弃处理机时进行调度即可,而抢占式还需在就绪队列变化时,检查是否会发生抢占。

优缺点: 优点:用优先级区分紧急程度、重要程度,适用于实时操作系统。可灵活地调整对各种作业/进程的偏好程度。缺点:若源源不断地有高优先级进程到来,则可能导致饥饿。

是否会导致饥饿: 会
补充: 就绪队列未必只有一个,可以按照不同优先级来组织。另外,也可以把优先级高的进程排在更靠近队头的位置。

根据优先级是否可以动态改变,可将优先级分为静态优先级和动态优先级两种。
静态优先级:创建进程时确定,之后一直不变。
动态优先级:创建进程时有一个初始值,之后会根据情况动态地调整优先级。

如何合理地设置各类进程的优先级?

通常: 系统进程优先级高于用户进程
前台进程优先级高于后台进程
操作系统更偏好 I/O型进程(或称 I/O繁忙型进程)( I/O设备和CPU可以并行工作。如果优先让I/O繁忙型进程优先运行的话, 则越有可能让I/O设备尽早地投入工作,则资源利用率、系统吞吐量都会得到提升)
注:与I/O型进程相对的是计算型进程(或称 CPU繁忙型进程)

如果采用的是动态优先级,什么时候应该调整?

可以从追求公平、提升资源利用率等角度考虑
如果某进程在就绪队列中等待了很长时间,则可以适当提升其优先级
如果某进程占用处理机运行了很长时间,则可适当降低其优先级
如果发现一个进程频繁地进行I/O操作,则可适当提升其优先级

多级反馈队列调度算法

算法思想: 对其他调度算法的折中权衡

算法规则: ①设置多级就绪队列,各级队列优先级从高到低,时间片从小到大。
②新进程到达时先进入第1级队列,按FCFS原则排队等待被分配时间片,若用完时间片进程还未结束,则进程进入下一级队列队尾。如果此时已经是在最下级的队列,则重新放回该队列队尾。
③只有第 k 级队列为空时,才会为 k+1 级队头的进程分配时间片。
用于作业/进程调度: 用于进程调度

是否可抢占? 抢占式的算法。在 k 级队列的进程运行过程中,若更上级的队列(1~k-1级)中进入了一个新进程,则由于新进程处于优先级更高的队中,因此新进程会抢占处理机,原来运行的进程放回 k 级队列队尾。

优缺点: 对各类型进程相对公平(FCFS的优点);每个新到达的进程都可以很快就得到响应(RR的优点);短进程只用较少的时间就可完成(SPF的优点);不必实现估计进程的运行时间(避免用户估计出错); 可灵活地调整对各类进程的偏好程度,比如CPU密集型进程、I/O密集型进程(拓展:可以将因I/O而阻塞的进程重新放回原队列,这样I/O型进程就可以保持较高优先级)

是否会导致饥饿: 会

补充

先来先服务调度算法有利于CPU繁忙型的作业,而不利于I/O繁忙型的作业。FCFS调度算法比较有利于长作业,而不利于短作业。所谓CPU繁忙型的作业,是指该类作业需要大量的CPU时间进行计算,而很少请求I/O操作。I/O繁忙型的作业是指CPU处理时,需要频繁地请求I/O操作。所以CPU繁忙型作业更接近于长作业。

在同一台处理器上以单道方式运行时,多道作业同时到达的情况下,要想获得最短的平均周转时间,用短作业优先调度算法会有较好的效果。
作业是从用户角度出发的,它由用户提交,以用户任务为单位;进程是从操作系统出发的,由系统生成,是操作系统的资源分配和独立运行的基本单位。

中断向量本身是用于存放中断服务例行程序的入口地址,而中断向量地址就应是该入口地址的地址。

短任务优先调度不管是抢占式的还是非抢占的,当系统总是出现新来的短任务时,长任务会总是得不到处理机,产生饥饿现象。

多级反馈队列调度算法能较好地满足各种类型用户的需要。对于终端型作业用户而言,由于它们提交的作业大多属于交互型作业,作业通常比较短小,系统只要能使这些作业在第一级队列所规定的时间片内完成,便可使终端型作业用户感到满意;对于短批处理作业用户而言,它们的作业开始时像终端型作业一样,若仅在第一级队列中执行一个时间片即可完成,便可获得与终端型作业一样的响应时间;对于稍长的作业,通常也只需要在第2级队列和第3级队列中各执行一个时间片即可完成,其周转时间仍然较短;对于长批处理作业用户而言,它们的长作业将依次在第1,2,……,n级队列中运行,然后按时间片轮转方式运行,用户不必担心其作业长期得不到处理。

进程同步

在OS中引入进程后,如果不能对多个进程的运行进行妥善的管理,必然会因为对系统资源的争夺,致使每次处理的结果存在着不确定性,即显示出其不可再现性。即进程具有异步性的特征。异步性是指,各并发执行的进程以各自独立的、不可预知的速度向前推进。

为保证多个进程能有条不紊地运行,在多道程序系统中,必须引入进程同步机制。

单处理机系统中的进程同步机制——硬件同步机制、信号量机制、管程机制等,利用它们来保证程序执行的可再现性。

进程同步的基本概念

进程同步机制的主要任务,使并发执行进程之间能按照一定的规则(或时序)共享系统资源,从而使程序的执行具有可再现性。

进程同步

同步亦称直接制约关系,是指为完成某种任务而建立的多个进程,因为需要协调工作次序而产生的制约关系。进程间的直接制约关系源于它们之间的相互合作。

进程互斥

互斥又称间接制约关系。指当一个进程访问临界资源时,另一个想要访问该临界资源的进程必须等待。访问临界资源结束,释放该资源之后,另一个进程才能去访问。


临界资源

一个时间段内只允许一个进程使用的资源称为临界资源。许多物理设备(比如摄像头、打印机)都属于临界资源。此外还有许多变量、数据、内存缓冲区等都属于临界资源。对临界资源的访问,必须互斥地进行。

在每个进程中,访问临界资源的那段代码称为临界区,也称为临界段。,

可把临界资源的访问过程分成4个部分:

注意:
临界区是进程中访问临界资源的代码段。
进入区和退出区是负责实现互斥的代码段。

同步机制应遵循的规则

空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区;

忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待;

有限等待。对请求访问的进程,应保证能在有限时间内进入临界区(保证不会饥饿);

让权等待。当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。

实现临界区互斥

软件实现方法

1)单标志法

算法思想:两个进程在访问完临界区后会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予。

turn 的初值为 0,即刚开始只允许 0 号进程进入临界区。
若 P1 先上处理机运行,则会一直卡在 ⑤。直到 P1 的时间片用完,发生调度,切换 P0 上处理机运行。代码 ① 不会卡住 P0,P0 可以正常访问临界区,在 P0 访问临界区期间即时切换回 P1,P1依然会卡在 ⑤。只有 P0 在退出区将 turn 改为 1 后,P1才能进入临界区。
因此,该算法可以实现“同一时刻最多只允许一个进程访问临界区”。

而这种算法只能按 P0 → P1 → P0 → P1 →……这样轮流访问。这种必须“轮流访问”带来的问题是,如果此时允许进入临界区的进程是 P0,而 P0 一直不访问临界区,那么虽然此时临界区空闲,但是并不允许 P1 访问。

单标志法存在的主要问题是:违背“空闲让进”原则。

​​

2)双标志先检查法

算法思想:设置一个布尔型数组 flag[],数组中各个元素用来标记各进程想进入临界区的意愿,比如 “flag[0] = ture”意味着 0 号进程 P0 现在想要进入临界区。每个进程在进入临界区之前先检查当前有没有别的进程想进入临界区,如果没有,则把自身对应的标志 flag[i] 设为 true,之后开始访问临界区。

若按照 ①⑤②⑥③⑦….的顺序执行,P0 和 P1 将会同时访问临界区。

双标志先检查法的主要问题是:违反“忙则等待”原则。不能实现对临界资源的互斥访问。
原因在于,进入区的“检查”和“上锁” 两个处理不是一气呵成的。“检查”后,“上锁”前可能发生进程切换。

3)双标志后检查法

算法思想:双标志先检查法的改版。前一个算法的问题是先“检查”后“上锁”,但是这两个操作又无法一气呵成,因此导致了两个进程同时进入临界区的问题。因此,人们又想到先“上锁”后“检查”的方法,来避免上述问题。

若按照 ①⑤②⑥….的顺序执行,P0 和 P1 将都无法进入临界区。
因此,双标志后检查法虽然解决了“忙则等待”的问题,但是又违背了“空闲让进”和“有限等待”原则,会因各进程都长期无法访问临界资源而产生“饥饿”现象。
两个进程都争着想进入临界区,但是谁也不让谁,最后谁都无法进入临界区,就发生了死锁。

4)Peterson算法

算法思想:结合双标志法、单标志法的思想。如果双方都争着想进入临界区,那可以让进程尝试“孔融让梨”(谦让)。做一个有礼貌的进程。

Peterson 算法用软件方法解决了进程互斥问题,遵循了空闲让进、忙则等待、有限等待三个原则,但是依然未遵循让权等待的原则。
Peterson 算法相较于之前三种软件解决方案来说,是最好的,但依然不够好。

硬件实现方法

通过硬件支持实现临界段问题的方法称为低级方法,或称元方法。

1)中断屏蔽方法

利用“开/关中断指令”实现(与原语的实现思想相同,即在某进程开始访问临界区到结束访问为止都不允许被中断,也就不能发生进程切换,因此也不可能发生两个同时访问临界区的情况)。

优点:简单、高效。

缺点:不适用于多处理机(因为一个处理机的关中断指令对另外一个处理机并不影响,所以有可能多个处理机同时进入临界区的情况),只适用单处理机;只适用于操作系统内核进程,不适用于用户进程(因为开/关中断指令只能运行在内核态,这组指令如果能让用户随意使用会很危险,滥用)

2)TestAndSet指令

简称 TS 指令,也有地方称为 TestAndSetLock 指令,或 TSL 指令。
TSL 指令是用硬件实现的,执行的过程不允许被中断,只能一气呵成。以下是用C语言描述的逻辑:

若刚开始 lock 是 false,则 TSL 返回的 old 值为 false,while 循环条件不满足,直接跳过循环,进入临界区。若刚开始 lock 是 true,则执行 TLS 后 old 返回的值为 true,while 循环条件满足,会一直循环,直到当前访问临界区的进程在退出区进行“解锁”。

相比软件实现方法(双标志先检查法),TSL 指令把“上锁”和“检查”操作用硬件的方式变成了一气呵成的原子操作。

优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境。

缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。

3)Swap指令

有的地方也叫 Exchange 指令,或简称 XCHG 指令。
Swap 指令是用硬件实现的,执行的过程不允许被中断,只能一气呵成。以下是用C语言描述的逻辑:

逻辑上来看 Swap 和 TSL 并无太大区别,都是先记录下此时临界区是否已经被上锁(记录在 old 变量上),再将上锁标记 lock 设置为 true,最后检查 old,如果 old 为 false 则说明之前没有别的进程对临界区上锁,则可跳出循环,进入临界区。

优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境。
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。

信号量

​​

1965年,荷兰学者Dijkstra提出了一种实现进程互斥、同步的方法——信号量机制。
用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。

信号量其实就是一个变量,值反映了剩余资源数。
原语是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。

软件解决方案的主要问题是“进入区的各种操作无法一气呵成”。

一对原语:wait(S) 原语和 signal(S) 原语,它们在执行时是不可中断的,可以把原语理解为我们自己写的函数,函数名分别为 wait和 signal,括号里的信号量 S 其实就是函数调用时传入的一个参数。
wait、signal 原语常简称为 P、V操作(来自荷兰语 proberen 和 verhogen)。常把wait(S)、signal(S) 两个操作分别写为 P(S)、V(S)。

整型信号量

用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。(与普通整数变量的区别:对信号量的操作只有三种,即 初始化、P操作、V操作)

存在的问题:不满足“让权等待”原则,会发生“忙等”。

记录型信号量

整型信号量的缺陷是存在“忙等”问题,因此人们又提出了“记录型信号量”,即用记录型数据结构表示的信号量。

wait(S)、signal(S) 也可以记为 P(S)、V(S),这对原语可用于实现系统资源的“申请”和“释放”。
S.value 的初值表示系统中某种资源的数目。

对信号量 S 的一次P操作意味着进程请求一个单位的该类资源,因此需要执行 S.value–,表示资源数减1,当S.value < 0 时表示该类资源已分配完毕,因此进程应调用 block 原语进行自我阻塞(当前运行的进程从运行态→阻塞态),主动放弃处理机,并插入该类资源的等待队列 S.L 中。可见,该机制遵循了“让权等待”原则,不会出现“忙等”现象。

对信号量 S 的一次 V 操作意味着进程释放一个单位的该类资源,因此需要执行 S.value++,表示资源数加1,若加1后仍是 S.value <= 0,表示依然有进程在等待该类资源,因此应调用 wakeup 原语唤醒等待队列中的第一个进程(被唤醒进程从阻塞态→就绪态)。

注:若考试中出现 P(S)、V(S) 的操作,除非特别说明,否则默认 S 为记录型信号量。都采用记录型信号量进行解题。
一个信号量对应一种资源。
信号量的值 = 这种资源的剩余数量(信号量的值如果小于0,说明此时有进程在等待这种资源)
P( S ) —— 申请一个资源S,如果资源不够就阻塞等待
V( S ) —— 释放一个资源S,如果有进程在等待该资源,则唤醒一个进程

信号量实现互斥

分析并发进程的关键活动,划定临界区(如:对临界资源打印机的访问就应放在临界区)
设置互斥信号量 mutex,初值为 1(理解:信号量 mutex 表示“进入临界区的名额”)
在进入区 P(mutex)——申请资源
在退出区 V(mutex)——释放资源

注意:对不同的临界资源需要设置不同的互斥信号量。P、V操作必须成对出现。缺少P(mutex) 就不能保证临界资源的互斥访问。缺少 V(mutex) 会导致资源永不被释放,等待进程永不被唤醒。

信号量实现同步

进程同步:要让各并发进程按要求有序地推进。

用信号量实现进程同步:
分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作(或两句代码)
设置同步信号量 S, 初始为 0
在“前操作”之后执行 V(S)
在“后操作”之前执行 P(S) (技巧口诀:前V后P)

信号量实现前驱

前驱关系是表示进程之间执行的先后顺序。P1→P2表示P2开始执行之前P1必须完成。
进程 P1 中有句代码 S1,P2 中有句代码 S2 ,P3中有句代码S3 …… P6 中有句代码 S6。这些代码要求按如下前驱图所示的顺序来执行:

其实每一对前驱关系都是一个进程同步问题(需要保证一前一后的操作)因此,
要为每一对前驱关系各设置一个同步信号量
在“前操作”之后对相应的同步信号量执行 V 操作
在“后操作”之前对相应的同步信号量执行 P 操作 (前V后P)

管程

进程同步工具——管程(管理临界资源的进程)。管程的特性保证了进程互斥,无须程序员自己实现互斥,从而降低了死锁发生的可能性。同时管程提供了条件变量,可以让程序员灵活地实现进程同步。

管程的定义

代表共享资源的数据结构以及其实施管理操作的资源管理程序组成共同构成了一个操作系统的资源管理模块,我们称之为管程(monitor)。
管程是一种特殊的软件模块,组成:
① 局部于管程的共享数据结构说明
② 对该数据结构进行的操作过程(也可以理解为“函数”);
③ 对局部于管程的共享数据设置初始值的语句;
管程名

管程的基本特征

① 局部于管程的数据只能被局部于管程的过程所访问;
② 一个进程只有通过调用管程内的过程才能加入管程访问共享资源
③ 每次仅允许一个进程加入管程,从而实现进程互斥。(各个进程只能串行执行管理内的过程,保证了进程“互斥”访问共享数据结构。)

管程的定义描述举例如下:

① 管程把对共享资源的操作封装起来(整体上很像一个类),一个进程只有通过调用管程内的过程才能加入管程访问共享资源。对于上例,外部进程只能通过调用take_away()过程来申请一个资源;归还资源也一样。

② 每次仅允许一个进程加入管程,从而实现进程互斥(注意:这种互斥特性是由编译器负责实现的,程序员不用关心)。若多个进程同时调用take_away(),give_back(),则只有某个进程运行完它调用的过程后,下个进程才能开始运行它调用的过程。也就是说,各个进程只能串行执行管理内的过程,这一特性保证了进程“互斥”访问共享数据结构S。

管程和进程的不同:

① 虽然两者都定义了数据结构,但进程定义的是私有数据结构PCB管程定义的是公共数据结构,如消息队列等;
② 两者都存在对各自数据结构上的操作;但进程是由顺序程序执行有关操作,而管程是进行同步操作和初始化操作
③ 设置进程的目的在于实现系统的并发性,而管程保证进程互斥
④ 进程通过调用管程中的过程对共享数据结构实行操作,进程是主动工作方式,而管程为被动工作方式
⑤ 进程之间能并发执行,而管程则不能与其调用者并发;
⑥ 进程具有动态性,由“创建”而诞生,由“撤销”而消亡,而管程则是操作系统中的一个资源管理模块,供进程调用

条件变量

当一个进程进入管程后被阻塞,直到阻塞原因解除是,在此期间,如果该进程不释放管程,你们其他进程无法进入管程,被迫长时间的等待。

为此,将阻塞原因定义为条件变量 condition。通常,一个进程被阻塞的原因可以有多个,因此在管程中设置了多个条件变量。每个条件变量保存了一个等待队列,用于记录该条件变量对应被阻塞的所有进程,对条件变量只能进行两种操作,即wait和signal。
x.wait:当x对应的条件不满足时,正在调用管程的进程调用x.wait将自己插入x条件的等待队列,并释放管程。此时其他进程可以使用该管程。
x.signal:x对应的条件发生了变化,则调用x.signal,唤醒一个因x条件而阻塞的进程。

条件变量和信号量的比较:
相似点:条件变量的wait/signal操作类似于信号量的P/V操作,可以实现进程的阻塞/唤醒。
不同点:条件变量是“没有值”的,仅实现了“排队等待”功能;而信号量是“有值”的,信号量的值反映了剩余资源数,而在管程中,剩余资源数用共享数据结构记录

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值