操作系统(二)处理器管理

处理器管理

  • 处理器管理的主要任务是对处理器进行分配,并对其运行进行有效的控制和管理。在现代操作系统中,处理器的分配和运行都是以进程为基本单位的,因而对处理器的管理也可以视为对进程的管理。

  • 处理器管理包括以下功能:

  1. 进程控制。在并发运行环境中,要使程序运行,必须先为它创建一个或几个进程,并给它分配必要的资源。程序运行结束时,要撤消这些进程,并回收这些进程所占用的各类资源。进程控制的主要任务就是为程序创建进程,撤消已结束的进程,以及控制进程在运行过程中的状态转换。
  2. 进程同步。在并发环境中,进程是以异步方式工作的,并且以不可预知的速度向前推进。为了使多个进程能有条不紊地运行,系统中必须设置进程同步机制。
    进程同步的主要任务是对众多的进程运行进行协调。协调方式有两种:
    (1)进程互斥方式。
    (2)进程同步方式。
  3. 进程通信。在系统中,经常会有多个进程需要相互配合去完成一个共同的任务,而在这些进程之间,往往需要相互交换信息。进程通信的任务就是用来实现相互合作进程之间的信息交换。进程的通信方式有:
    1)当相互合作的进程处于同一台计算机系统时,通常采用直接通信方式。由源进程利用发送命令直接将消息发送到目标进程的消息队列上,然后由目标进程利用接收命令从其消息队列中取出消息。
    2)当相互合作的进程处于不同计算机系统时,通常采用间接通信方式。由源进程利用发送命令将信息发送到一个专门存放消息的中间实体中,然后由目标进程利用接收命令从中间实体中取出消息。这个中间实体通常称为“邮箱”,相应的通信系统称为电子邮件系统。
  4. 调度。等待在后备队列上的作业,通常要经过处理器调度才能执行。处理器调度包括作业调度(也称为高级调度)、进程调度(也称为低级调度)和中级调度。
    1)作业调度的基本任务是从后备队列中按照一定的算法,选择出若干个作业,将它们调入主存,建立进程,分配必要的资源,使之成为就绪进程,并按照一定的算法将其插入到就绪队列。
    2)进程调度的基本任务是从进程的就绪队列中,按照一定的调度算法选出一个进程,把处理器分配给它,使进程投入运行。
    3)中级调度的基本任务是把那些暂时不能运行的进程从主存移到外存上,释放其所占有的宝贵资源。当移到外存上的进程具备运行条件时,再由中级调度把它们重新调入主存,等待运行。

2.1 程序执行

  • 程序的执行有两种方式:顺序执行和并发执行。
  • 顺序执行是单道批处理系统的执行方式,也用于简单的单片机系统;
  • 现在的操作系统多为并发执行,具有许多新的特征。引入并发执行的目的是为了提高资源利用率。

前趋图

  • 前趋图:是一个有向无循环图。图中的每个结点可用于表示一条语句、一个程序段等;结点间的有向边表示在两个结点之间存在的前趋关系。
  • 如Pi → Pj,称Pi是Pj的前趋,而Pj是Pi的后继。
  • 在前趋图中,没有前趋的结点称为初始结点,没有后继的结点称为终止结点。应当注意的是,前趋图中不能存在循环。

例:在下图所示的前趋图中存在下述前趋关系:
P1 → P2,P1 → P3,P2 → P5
P3 → P4,P4 → P5,P5 → P6

在这里插入图片描述

前趋图——顺序执行
  • 程序的顺序执行:I-输入 C-计算 P-输出

在这里插入图片描述

  • 顺序执行的程序特点
    • 顺序性
    • 封闭性
    • 可在线性
前趋图——并发执行
  • 程序的并发执行:

在这里插入图片描述

  • 并发执行的程序特点:
    • 间断(异步)性:程序可能走到中途停下来,失去原有的时序关系。
    • 相互作用和制约性:系统中并发执行的程序互相独立,但有时也会直接或间接的发生相互依赖和相互制约。
    • 失去可再现性:程序经过多次运行,虽然其各次的环境和初始条件相同,但得到的结果却各不相同。
    • 失去封闭性:一个程序到存储器中的数据可能被另一个程序修改,失去原有的不变特征。

【例】程序A和程序B为并发执行,它们共享变量M,假设M初值为3;程序A执行M=M+1;程序B执行print M;M=1。程序A和程序B执行的顺序若不相同,M的结果将产生不同的变化。
顺序1:M = M +1;print M;M = 1。M值依次为4、4、1。
顺序2:print M;M = M +1;M = 1。M值依次为3、4、1。
顺序3:print M;M = 1;M = M +1。M值依次为3、1、2。
按照顺序1执行,M的输出结果为4;按照顺序2执行,M的输出结果为3;按照顺序3执行,M的输出结果为3。所以,当执行的条件不同时,并发程序有可能产生不同的执行顺序,也就会得到不同的执行结果。这样并发程序就形成了结果的不可再现性。

【例】已知一个求值公式(a²+3b)/(b+5a),a、b已赋值,画出该公式求值过程的前趋图,并判断哪些过程可以并发执行。
解析:把公式(a²+3b)/(b+5a)按照运算顺序分解,可以产生如下运算步骤:s1~s6,如图(a)所示;根据分解的运算顺序画出它的前趋图,如图(b)所示。根据前趋图,可以看出能够并发执行的运算是:s1与s2、s1与s3、s2与s3、s1与s5、s2与s5、s3与s4、s4与s5,其余运算不能并发执行。

在这里插入图片描述
思考题:

  • 并发执行环境下,我们如何得知一个程序何时执行?
  • 何时停顿?
  • 我们如何知道他与其他程序之间的关系?
  • 多道程序并发执行引发了一系列新情况??

2.2 进程概念与状态

  • 并发意味着互相要竞争资源
  • 停停走走意味着要保留和纪录中间形态
  • 竞争资源意味着要分辨所有对象

2.2.1 进程概念

一、进程的概念
        进程最根本的属性是动态性和并发性

  • 进程定义:
    程序在并发环境中的执行过程。
    进程可以定义为一个数据结构及能在其上进行操作的一个程序。
    一个程序段在一个数据集合上的一次运行过程。
  • 进程是程序在一个数据集合上的运行过程,是系统资源分配和调度的一个独立单位。

举一个例子来说明程序和进程:
例如从北京南站发往上海虹桥的G101次列车,它有自己的运行步骤:始发时间、站台,中间停靠的车站及停靠时间,到达终点站的时间等,这相当于一个程序。
2020年10月1日从北京南站发往死上海虹桥的G101次列车就相当于一个进程,它是一个过程,它从2020年10月1日早上6:36北京南站出发开始,到中午12:40到达上海虹桥结束。

二、进程的特征

  1. 动态性:进程因创建而产生,因调度而执行,因得不到资源而暂停,因撤销而消亡。
  2. 并发性:引入进程的目的正是为了使其程序和其它程序并发执行;而程序(没有建立进程)是不能并发执行的。
  3. 独立性:进程是独立获得资源、独立调度运行的基本单位。凡未建立进程的程序,都不能作为一个独立的单位参加运行。
  4. 异步性:进程按照各自独立的、不可预知的速度向前推进,导致执行的不可再现性。
  5. 结构性:程序段、数据段和进程控制块(PCB)。

三、进程与程序的对比

  1. 程序是指令的集合,是静态的概念;而进程是动态的,是一个执行的过程。
  2. 进程具有并发执行的特征,而程序没有执行特性,也就没有并发性。
  3. 程序是纪录在介质上的静态集合,作为资料可以长期保存(人为删除),而进程由数据段、程序段、PCB三部分组成,是有生命周期的一个有限的过程。
  4. 进程是现代计算机工作的基本单位。既是独立调度和执行的基本单位,又是资源分配和拥有资源的基本单位。
  5. 不同进程可以执行多个独立程序,也可以包含同一个程序。一个程序可以多次被不同的进程包含执行。
  • 进程与作业:作业的概念主要用于批处理系统。进程的概念用于所有多道系统。
    一个作业可以有多个进程组成,但至少有一道进程。而一个进程只能属于一个作业。

  • 进程类型:系统进程和用户进程

2.2.2 进程的状态

一、进程的三种基本状态

  1. 就绪状态:进程已经获得了除去CPU外其它运行所需的资源时的状态。所谓万事俱备,只欠东风。
  2. 运行状态(执行状态):处于就绪状态的某个进程获得了CPU的调度,获得了CPU,此时进程进入执行状态。
  3. 阻塞状态(等待状态):正在运行的进程因为需要输入输出数据等自身原因,不能继续运行,此时退出CPU,进入阻塞状态。

在这里插入图片描述

  • 1)就绪 转 运行
  • 2)运行 转 就绪
  • 3)运行 转 阻塞
  • 4)阻塞 转 就绪

二、进程的五种基本状态
    增加了其他两种状态:
4. 新状态:一个进程刚刚建立,还未放入就绪队列时的状态。
5. 终止状态:当一个进程已经正常结束或异常结束,操作系统已将其从系统队列中移除,但尚未撤销,这时为终止状态。
状态转换(增加两个转换):

  • 5)运行 转 终止
  • 6)新状态 转 就绪

在这里插入图片描述
三、进程的挂起状态
为什么会有挂起:

  • 1)用户的需求。当用户在进程运行期间,发现有可疑问题时,希望进程暂时停止下来,但是,并不终止进程。若进程处于执行状态,则暂停执行;若进程处于就绪状态,则暂时不接受调度,以便研究进程执行情况或对程序进行修改。这种静止状态称为挂起状态。
  • 2)父进程的需求。父进程往往希望考查和修改子进程,或者协调各个子进程之间的活动,此时需要挂起自己的子进程。
  • 3)操作系统的需求。操作系统有时需要挂起某些进程,然后检查系统中资源的使用情况,进行记账控制,以便改善系统运行的性能。
  • 4)对换的需求。为了缓和主存与系统其他资源的紧张情况,并且提高系统性能,有些系统希望将处于阻塞状态的进程从主存换到外存。而换到外存的进程,当等待的事件完成,它仍然不具备执行的条件,不能进入就绪队列,所以需要一个有别于阻塞状态的新状态来表示,即挂起状态。

具有挂起操作的进程状态转换图:
在这里插入图片描述

在操作系统中,进程是一个具有一定独立功能程序在某个数据集合上的一次 A ,进程是一个 B 概念,而程序是一个 C 的概念。在一单处理机中,若有5个用户进程,在非管态的某一时刻,处于就绪状态的用户进程最多有 D 个,最少有 E 个。
A: (1)并发活动 (2)并行活动(3)单独操作 (4)关联操作
B,C:(1)组合态 (2)关联态 (3)运行态(4)等待态 (5)静态 (6)动态
D,E:(1)1;(2)2;(3)3;(4)4;(5)5;(6)0
答案:1,6,5,4,6

在一个单处理机的系统中,OS的进程有运行、就绪、阻塞三个基本状态。假如某时刻该系统中有10个进程并发执行,在略去调度程序所占用时间情况下试问:
这时刻系统中处于运行态的进程数最多有几个?最少有几个?(1,0)
这时刻系统中处于就绪态的进程数最多有几个?最少有几个?(9,0)
这时刻系统中处于阻塞态的进程数最多有几个?最少有几个?(10,0)

2.3 进程控制

2.3.1 进程控制块PCB

一、进程控制块PCB

  • 也称进程描述块,其中含有进程的描述信息和控制信息,是进程动态特性的集中反映,是系统对进程识别和控制的依据。

二、进程控制块的作用

  • 每个进程有唯一的进程控制块,PCB与进程一一对应,是进程存在的唯一标志。
  • 操作系统根据PCB对进程实施控制和管理。
  • 进程的动态、并发等特征是利用PCB表现出来的。

三、进程控制块应包含的主要内容:

  • 1)进程标识信息:进程名、进程号
    2)进程说明信息(进程调度信息):特征信息、进程状态、调度优先权
    3)进程现场信息(处理器状态信息):现场保护区
    4)进程管理信息(进程控制信息):资源需求、进程通信、相关关系、其他信息
    在这里插入图片描述
    四、进程控制块的组织
  • PCB作为系统非常重要的数据,常驻内存,放在内存系统区特定空间
  • PCB队列示意图:
    在这里插入图片描述
    PCB的组织方式分为两种:
    1)链接方式
    在这里插入图片描述
    2)索引方式
    在这里插入图片描述

2.3.2 操作系统内核

  • 操作系统的控制结构:对系统资源进行管理和控制,每种资源都有对应的结构。
    · 存储器资源:内存表
    · 输入输出设备:I/O表
    · 软件资源:文件表
    · 处理器:进程表
  • 这些表结构实际上可能都不止一个。
  • 进程控制是对系统中全部进程实施有效管理,包括:
    • 创建新进程
      终止已结束进程
      终止由于某事件而无法运行下去的进程
      负责进程的状态转换
  • 这些功能一般由操作系统的内核实现,操作系统的内核是基于硬件的第一次扩充。把与硬件紧密相关的模块、运行频率较高的模块及一些公用的基本操作安排在靠近硬件的软件层次。
    在这里插入图片描述
    一、操作系统内核
  • 操作系统一般划分为若干层次,再将OS的不同功能设置在不同的层次中。通常将一些与硬件紧密相关的模块(如中断处理程序等)、各种常用及运行频率较高的模块(如时钟管理、进程调度和许多模块所公用的基本操作),都安排在紧靠硬件的软件层次中,将它们常驻内存,这通常被称为OS内核。
  • 这种安排方式的目的在于两方面:一是便于对这些软件进行保护,防止遭受其他应用的破坏;二是提高OS的运行效率。
  • 操作系统内核都包含了以下两大方面的功能:
    1)支撑功能:该功能是提供给OS其它众多模块所需要的一些基本功能,以便支撑这些模块工作。其中三种最基本的支撑功能是中断处理、时钟管理和原语操作。
    2)资源管理功能:主要包括进程管理、存储器管理、设备管理。

二、处理器执行状态
为了防止OS本身及关键数据遭到用户应用进程有意或无意的破坏,通常将处理机的执行状态分成系统态和用户态两种。

  • 1)内核态:又称为管态、系统态、核心态、特权态等。具有较高的特权,能执行一切指令,访问所有寄存器和存储区,0S都在系统态运行。
  • 2)用户态:又称为目态、非特权态。具有较低特权的执行状态,只能执行规定的指令,访问指定的寄存器和存储区。

· 如果一个进程运行在内核态,则该进程就可以访问计算机的任何资源,即它的资源访问权限不受限制。如果一个进程运行在用户态,则其资源需求将受到一定的限制。区分内核态和用户态两种操作方式的目的是为了保护操作系统,提高操作系统的可靠性和安全性。
· 处理器的程序状态字寄存器(PSW)中有一个状态位用于记录进程的运行状态,即当前代码是内核态还是用户态,这种状态是处理器的一种状态,而不是进程的状态。
· 用户进程运行在用户态,它不能执行OS指令,不能访问OS区域,防止对OS的破坏。OS内核运行在系统态。进程控制包含在OS的内核中,它常驻内存,执行效率高。

三、用户态切换到内核态主要有如下3种方式:

  • 1、系统调用。这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的的服务程序完成工作。系统调用机制的核心还是使用了操作系统为用户特别开放的一个中断来实现。
  • 2、异常。当CPU在执行用户态进程时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关进程中,也就转到了内核态。
  • 3、I/O设备的中断。当I/O设备完成用户请求操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令,转而去执行与中断信号对应的的处理程序,如果先前执行的指令是用户态下的进程,那么这个转换的过程自然也就发生了由用户态到内核态的切换。例如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续的操作。
  • 系统调用可以认为是用户进程主动发起的,异常和外部设备中断则是被动的。

四、系统调用

  • 系统调用也称程序接口,是程序级的接口,即用户程序中可以利用系统调用提供的一组系统调用命令去调用操作系统内核中的一个(或一组)过程来完成自己所需要的功能。系统调用可以看作是操作系统内核和应用程序之间进行交互的接口,操作系统向用户程序提供系统调用的原因是为了对系统进行“保护”。当用户程序需要系统服务,也就是要调用系统内核中的某些程序时,只能从规定的位置进入内核,这样才能保证系统的安全。
  • 用户程序如何使用系统调用:访管指令是一条可在用户态下执行的指令,用户程序在编译时将调用操作系统的逻辑要求转换成一条访管指令,并设置一些参数;当执行到访管指令,产生访管中断,转入内核态执行系统调用程序,实现指定功能;系统调用完成后回到用户态执行用户程序。

2.3.3 进程控制原语

一、原语(Primitive)

  • 由若干条机器指令构成的并用以完成特定功能的一段代码,而且这段代码在执行期间不允许中断。
  • 原语又称为“原子操作(Atomic Operation)”过程,作为一个整体而不可分割——要么全都完成,要么全都不做。
  • 原语分为两类:
    1、机器指令级:执行期间不能中断
    2、功能级:作为原语的程序段不允许并发执行

二、内核中包含的进程控制的原语:

  • 1、进程创建原语
    2、进程撤消原语
    3、阻塞原语、唤醒原语
    4、挂起原语、激活(解挂)原语

  • 进程的层次关系:
    在这里插入图片描述

三、进程创建

1、引发创建进程的事件:

  • 调度新作业(批处理系统)
  • 用户登录(分时系统)
  • 操作系统提供特定服务
  • 正在执行的进程创建新进程

2、例如:

  • 系统初始化后,Windows将自动产生如对话管理SMSS、登陆管理WINLOGON、安全管理LSASS等系统进程。
  • 启动程序,如双击一个.exe文件。
  • 用户请求创建新进程,进行系统调用,生成新进程,如fork()或者createprocess()。

3、进程的创建过程:一旦发现了要求创建新进程的事件,OS便调用创建原语(如UNIX/Linux系统中的fork), 按以下过程创建新进程。

  • 其主要操作过程有如下四步:
    1)申请一个空闲的PCB
    2)为新进程分配资源
    3)将新进程的PCB初始化
    4)将新进程加到就绪队列中

4、进程创建过程的详细描述:

  • 分配一个唯一的进程标识符,索取一个空白PCB
  • 为新进程的程序和数据分配内存空间
  • 初始化进程控制块
  • 初始化标识符信息(填入)、处理机的状态信息(指令指针, 栈指针)和控制信息(状态,优先级…)
  • 设置相应的链接。如: 把新进程加到就绪队列的链表中

四、进程终止

1、引发终止进程的事件:

  • 1)正常结束(寿终):批处理系统中,进程已运行完成遇到Halt指令;分时系统中, 用户退出登录。
  • 2)异常结束(自杀):
    • 进程发生出错和故障事件
    • 存储区越界、保护性错(如:写只读文件)、特权指令错、非法指令(如:程序错转到数据区)、算术运算错、运行超时、等待超过时、I/O 失败等
  • 3)外界干预(他杀、处决):操作系统干预、父进程请求、父进程终止

2、进程的终止过程:

  • 1)找到指定进程的PCB
    2)终止该进程的运行
    3)回收该进程所占用的全部资源
    4)终止其所有子孙进程,回收它们所占用的全部资源
    5)将被终止进程的PCB从原来队列中摘走
    6)将回收的PCB加到空闲队列中

五、进程的阻塞和唤醒

  1. 进程的阻塞:处于运行状态的进程,在其运行过程中期待某一事件发生(如:请求系统服务、等待键盘输入、等待数据传输完成、等待其它进程发送消息),当被等待的事件未发生时, 由进程调用阻塞原语(block), 将自己阻塞。
  • 进程阻塞过程:
    • 阻塞原语使运行态的进程停止,将现场保存在其PCB的CPU现场保护区。
    • 将PCB中的现行状态由运行态变为阻塞态,并将该进程插入到相应事件的阻塞队列中。
    • 最后,转进程调度程序重新调度,将处理机分配给一个就绪进程,按新进程PCB中的处理机状态设置CPU环境,使它投入运行。
  1. 进程的唤醒:当被阻塞进程期待的事件到来时, 由中断处理进程或其它产生该事件的进程调用唤醒原语(Wakeup),将期待该事件的进程唤醒。
  • 进程唤醒过程:
    • 唤醒原语执行时, 将被阻塞进程从相应等队列中移出, 并将其 PCB中的现行状态由阻塞改为就绪态, 然后将该进程插入就绪队列中。
    • 若事件是等待 I/O 完成, 则由硬件提出中断请求, CPU响应中断, 暂停当前进程的执行, 转去中断处理。检查有无等待该 I/O完成的进程。若有, 则将它唤醒。然后结束中断处理。返回被中断进程或重新调度。
    • 若事件是等待某进程发一个信息, 则由发送进程把该等待进程唤醒。

六、进程的挂起和激活

  1. 进程的挂起:当进程请求将自己挂起或父进程请求将子进程挂起时, 调用挂起原语(suspend),将指定进程挂起。
  • 执行过程:检查要挂起进程的状态,若处于活动就绪态就将其改为静止就绪态,对于活动阻塞态的进程则将其改为静止阻塞态。如果被挂起的进程正在执行则还要转到调度程序重新调度。
  1. 进程的激活:激活指定进程,调用激活原语(active)将它激活。
  • 执行过程:将要激活的进程调入内存,并检查它的状态,若是静止就绪态则将其改为活动就绪态,若为静止阻塞态就将其改为活动阻塞态。如果采用的是抢占调度策略,被激活的进程优先级高则引起重新调度。

从静态角度看,进程由 A 、 B 和 C 三部分组成,用户可通过 D 建立和撤消进程,通常用户进程被建立后, E 。
A:(1)JCB (2)DCB (3)PCB (4)PMT
B: (1)程序段 (2)文件体 (3)I/O (4)子程序
C:(1)文件描述块(2)数据部分 (3)EOF (4)I/O缓冲区
D:(1)函数调用 (2)宏指令 (3)系统调用 (4)过程调用
E:(1)便一直存在于系统中,直到被操作人员撤消
(2)随着作业运行正常或不正常结束而撤消
(3)随着时间片轮转而撤消与建立
(4)随着进程的阻塞或唤醒而撤消与建立
答案:3,1,2,3,2

正在执行的进程由于时间片完而被暂停执行,此时进程从运行态变为 A 状态;处于静止阻塞状态的进程,在进程等待的事件出现后,应转变为 B 状态;若进程正处于运行态时,应终端的请求而暂停下来以便研究其运行情况(执行挂起进程原语),这时进程应转变为 C 状态,若进程已处于阻塞状态,则此时应转变为 D 状态,若进程已处于就绪状态,则此时应转变为 E 状态;执行解除挂起进程原语后,如挂起进程处于就绪状态,则应转变为 F 态,如处于阻塞状态,则应转变为 G 态;一个进程刚被创建时,它的初始状态为 H 。
A–H:(1)静止阻塞 (2)活动阻塞 (3)静止就绪
(4)活动就绪 (5)执行 (6)就绪
答案:4,3,3,1,3,4,2,6

2.4 线程

  • 提出线程的动机
    · WEB服务器需要同时处理多个客户请求
    · 创建多个进程会降低响应时间
    · 进程开销较大(上下文切换)
  • 什么是线程?
    · 线程,也形象的称为轻型进程
    · 是CPU调度执行的基本单位

2.4.1 线程的基本概念

一、线程概念:现代操作系统中,进程只作为资源拥有者,而调度和运行的属性赋予新的实体 —— 线程。

  • 线程(Thread)是进程中的一个实体,是被系统独立调度和执行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但是它可以与同属于一个进程的其他线程共享进程所拥有的全部资源。
  • 一个线程可以创建和撤消另一个线程,同一进程中的多个线程间可以并发执行。线程之间也会相互制约,使其在运行中呈现异步性。因此,线程同样具有就绪、执行、阻塞三种基本状态。就绪状态是指线程具备运行的所有条件,在等待处理机;运行状态是指线程占有处理机运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。

· 线程必须在某个进程内执行,一个线程只属于一个进程
· 一个进程可以包含一个线程或多个线程

在这里插入图片描述
三、线程的状态:
1、运行状态
2、就绪状态
3、阻塞状态
4、终止状态
在这里插入图片描述
四、线程和进程的关系
1、从属关系:进程可以创建进程,也可以创建线程;线程可以创建其它的线程。一个进程至少要有一个可执行线程,但可以有多个线程;而一个线程只能在一个进程的地址空间内活动。
2、系统管理:进程是系统资源分配和拥有的单位,资源分配给进程,同一进程的所有线程共享该进程的所有资源。线程是系统调度执行的单位,处理机分配给线程,即真正在处理机上运行的是线程。
3、工作效率:创建线程比创建进程快,进程内的线程切换也比进程快,且节省开销。solaris中创建线程比进程快30倍,线程切换比进程切换快5倍。从而获得快速的用户响应,如在C/S模式下,web server为每个用户连接运行一个线程。
4、并发效果:用线程实现并发/并行比用进程实现更方便更有效,特别是进程内的并发/并行:进程可创建多个线程来执行同一个程序的不同部分,从而实现进程内的并发。在多处理器体系结构,可以使一个进程中的线程在不同的处理器上运行,提高进程执行的并行性。

2.4.2 线程的实现

一、线程的实现方式主要有两种:

1、用户级线程:在用户空间实现,用户要自己再编写一个执行系统,由该执行系统进行线程的管理和执行。系统选择进程,用户执行系统管理线程。

  • 1、优点:
    • 1)不需要在用户态和系统态间进行切换,切换速度快;
      2)线程相应算法由应用程序确定,调度算法可专用;
      3)可运行在任何操作系统上,不依赖于系统内核。
  • 2、缺点:
    • 1)用户级线程的切换最终都可能导致系统的进程切换,
      则进程中的所有线程都会被阻塞;
      2)不能实现线程级的并发执行,不能充分利用多处理器
      系统。

2、内核级线程(系统级线程):在核心空间实现线程。

  • 优点:克服了用户级线程方式的两个主要缺陷;本身也可以是多线程。
  • 缺点:控制转移开销大。

3、混合线程(组合模式):将用户级线程与系统级线程结合。

  • 1)多对一模型:多对一模型把多个用户级线程映像到一个核心线程上。线程管理是在用户空间完成的,如果其中有一个线程因执行系统调用而阻塞,那么整个进程都被阻塞。另外每次只能有一个线程访问核心,所以多个线程不能在多处理器上并行。在Solaris2的线程库中就采用这种模型。
    2)一对一模型:一对一模型是每个用户线程映像到一个核心线程。当一个线程因执行系统调用而阻塞时,允许调度另一个线程运行,也允许多个线程在多处理器上并行,因而并行能力比多对一模型强。不足之处是创建一个用户线程就要相应地创建一个核心线程,因而开销大。在实现这种模型的系统上多数都限制线程的数量。Windows NT,Windows2000和OS/2系统上都实现了一对一模型。
    3)多对多模型:多对多模型把多个用户级线程映像到较少或相等个数的核心线程上。核心线程的个数可随应用程序或所用机器而变化,如一个应用程序在多处理器系统上分配的线程数就比单处理器系统的多。多对多模型克服了上述两种模型的缺点:开发者可以根据需要创建多个用户线程,而且相应的核心线程可以在多处理器上并行。也就是说,当一个线程因执行系统调用而阻塞时,核心可以调度另一个线程运行。Solaris2,IRIX , HP-UX 和Tru64 UNIX系统都支持这种模型。

二、应用举例:

  • 用户级线程(用户态线程)(进程管理)
    POSIX Pthreads
    Mach C-threads
    Solaris threads(一种UNIX的操作系统,采用微内核)
  • 系统级线程(内核态线程)(OS管理)
    Windows 95/98/NT/2000
    Solaris
    Tru64 UNIX
    BeOS

三、Windows中的进程与线程

  • windows是内核级的线程模型。进程是分配资源的单位,有自己独立的数据结构。而线程作为CPU调度的基本单位,也有自己的数据结构。
  • windows中的进程涉及到EPROCESS和KPROCESS两个结构,EPROCESS是执行体层的对象,而KPROCESS是内核层的对象。线程也涉及两个结构,ETHREAD是执行体对象,KTHREAD是内核层对象。
  • 内核层对象EPROCESS和ETHREAD都是相应执行体对象的首个内嵌结构,即实际上进程和线程的内核层对象和执行体层对象在内核中的地址是相同的。
  • 内核部分实现的基本是和进程或者线程本身相关的属性,而执行体层更多的注重于管理。

四、Linux的进程管理

1、Linux进程结构:

  • 1)栈区:存放的是子程序的返回地址、子程序的参数、以及程序的局部变量
  • 2)堆区:存放动态分配的数据 例如:new 和malloc
  • 3)全局区:存放全局变量、常量,其中包括.data段(存放静态初始化的全局变量或常量)和.bss段(存放未初始化的全局变量)
  • 4)代码区:存放程序代码数据(二进制)

2、Linux进程通过一个task_struct结构体描述,里面包括:

  • 进程状态(State)
  • 进程调度信息(Scheduling Information)
  • 各种标识符(Identifiers)
  • 进程通信有关信息(IPC:Inter_Process Communication)
  • 时间和定时器信息(Times and Timers)
  • 进程链接信息(Links)
  • 文件系统信息(File System)
  • 虚拟内存信息(Virtual Memory)
  • 页面管理信息(page)
  • 和处理器相关的环境(上下文)信息(Processor Specific Context)
  • 其他信息

3、进程状态:
在这里插入图片描述

  • TASK_RUNNING (可运行状态):处于这种状态的进程,要么正在运行、要么正准备运行。正在运行的进程就是当前进程(由current所指向的进程),而准备运行的进程只要得到CPU就可以立即投入运行,CPU是这些进程唯一等待的系统资源。
  • TASK_INTERRUPTIBLE(可中断的等待状态):表示进程被阻塞(睡眠),直到某个条件达成,进程的状态就被设置为TASK_RUNNING。处于该状态的进程正在等待某个事件(event)或某个资源,而被挂起。对应的task_struct结构被放入对应事件的等待队列中。处于可中断等待态的进程可以被信号唤醒,如果收到信号,该进程就从等待状态进入可运行状态,并且加入到运行队列中,等待被调度。
  • TASK_STOPPED(暂停状态):此时的进程暂时停止运行来接受某种特殊处理。通常当进程接收到SIGSTOP、SIGTSTP、SIGTTIN或 SIGTTOU信号后就处于这种状态。例如,正接受调试的进程就处于这种状态。
  • TASK_UNINTERRUPTIBLE(不可中断的等待状态):该状态与 TASK_INTERRUPTIBLE 状态类似,也表示进程被阻塞,处于睡眠状态。当进程等待的某些条件被满足了之后,内核也会将该进程的状态设置为 TASK_RUNNING。但是,处于这个状态下的进程不能在接收到某个信号之后立即被唤醒。这时该状态与 TASK_INTERRUPTIBLE 状态唯一的区别。
  • TASK_TRACED(跟踪状态):当前进程正在被另一个进程所监视。
  • EXIT_ZOMBIE(僵死状态):进程虽然已经终止,但由于某种原因,父进程还没有执行wait()系统调用,终止进程的信息也还没有回收。顾名思义,处于该状态的进程就是死进程,这种进程实际上是系统中的垃圾,必须进行相应处理以释放其占用的资源。
  • EXIT_DEAD:一个进程的最终状态。

2.4.3 其他相关技术

  • 线程池
  • 同步多线程技术
  • 超线程
  • 双核及多核技术

一、线程池

  • 实现线程池的思想是:在进程建立时就创建若干线程,把它们放在一个池中等待工作。当服务器接到一个请求时,就唤醒池中的一个线程,并把那个请求传给它,由该线程进行服务。一旦它完成工作,便返回池中,等待进行下面的服务。如果池中没有可用的线程,服务器就要等待,直至有一个线程被释放。
  • 使用线程池有以下两个好处 :
    • ① 利用池中现存的线程去实现服务比等待创建一个线程后再去服务更快。
    • ② 线程池限定了任何时刻存在的线程数量。对于那些不支持同时存在大量线程的系统来说,这一点尤为重要。
  • 确定池中线程的个数,要考虑系统中CPU个数、物理内存容量,以及预计同时到来的客户请求的数量等。还可以动态调整池中线程的数量,但这需要更复杂的线程池构造。

二、SMT同步多线程技术

  • 同步多线程技术(SMT)是一种体系结构模型,其目的是在现有硬件条件下,通过提高计算能力来提高处理器的性能。SMT结构中,不仅在一周期内启动多条指令,而且也能在同一周期内启动来自相互独立的线程(或上下文)的指令。在理论上,这种改进运用了指令级并行性(ILP)和线程级并行性(TLP)技术来提高执行功能单元的效率。
  • SMT可以最有效的利用多重启动和多线程结构,而不必实质性的增加开销和晶体管数量。DEC/Compaq的Alpha EV-8处理器就是利用SMT技术的典型机器。EV-8的设计者之一 Joel Emer说“它看上去像4个芯片,用起来像两个,而实际上只有一个,但它相比于一个非多线程的只增加了5%的晶体管。”

三、超线程

  • 超线程是同时多线程技术(SMT)的一种,这种技术可经由复制处理器上的结构状态,让同一个处理器上的多个线程同步执行并共享处理器的执行资源。

  • 超线程技术就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行效率。因此支持Intel超线程技术的cpu,打开超线程设置,允许超线程运行后,在操作系统中看到的cpu数量是实际物理cpu数量的两倍,就是1个cpu可以看到两个,两个可以看到四个。

  • 对支持多处理器功能的应用程序而言,超线程处理器被视为两个分离的逻辑处理器。应用程序不须修正就可使用这两个逻辑处理器。同时,每个逻辑处理器都可独立响应中断。第一个逻辑处理器可追踪一个软件线程,而第二个逻辑处理器则可同时追踪另一个软件线程。由于两个线程共同使用同样的执行资源,不会产生一个线程执行的同时,另一个线程闲置的状况。可以大大提升每个实体处理器中的资源使用率。

  • 有超线程技术的CPU需要芯片组、软件支持

  • 操作系统如:Microsoft Windows XP、Microsoft Windows 2003,Linux kernel 2.4.x以后的版本也支持超线程技术。

  • 当两个线程都同时需要某一个资源时,其中一个要暂时停止,并让出资源,直到这些资源闲置后才能继续。

  • 超线程的性能并不等于两颗CPU的性能。

  • 如果有软件不支持超线程运行,那么打开超线程后运行可能会导致某些问题。速度也不见得能提高。

  • 据intel解释,超线程技术能提高30%以上的性能。

  • 电脑开机时按Del+Ctrl+Alt这三个热键或F2进入BIOS,进入BIOS后,找到Hyper-Threading,把它Disabled,就可以把超线程关了。
    在这里插入图片描述
    四、双核及多核技术

  • “双核技术”,就是在处理器上拥有两个一样功能的处理器核心,即将两个物理处理器核心整合到一个内核中。两个处理核心在共享芯片组存储界面的同时,可以完全独立地完成各自地工作,从而在平衡功耗的基础上提高CPU性能。多核CPU是指在一个芯片上集成多个微处理器核心,可以并行地执行程序代码,在不提升CPU工作频率的情况下,降低CPU的功耗,并获得很高的聚合性能

  • 多核CPU可分为同构和异构两种:同构多核CPU大多由通用的处理器组成,核心相同,核心间平等;异构多核CPU使用不同的核心组成,分为主处理器和协处理器。同构原理简单,结构对称,较易实现;异构通常含有一个主处理器和多个协处理器。主处理器负责控制和管理,协处理器主要用来运算。

  • 第一个商用的多核CPU是2001年IBM推出的双核RISC处理器Power 4。2004年IBM又推出Power 5,并在双核的基础上引入多线程技术。同时,HP也推出多核CPU产品PA-RISC8800,SUN也发布双核产品UltraSPARC IV。

  • 2005年后,多核CPU得到全面发展,AMD迅速推出面向服务器、支持x86指令集的双核心Opteron处理器。Intel则推出面向桌面系统的双核CPU,Pentium D及Pentium Extreme Edition。

  • CMP单芯片多核心处理器:是指在一个芯片上集成多个微处理器核心,每个微处理器都是一个相对简单的单线程微处理器,而且这多个核心间联系非常紧密,甚至共享L1、L2和L3等。其核间通过高速总线连接在一起。

  • 同构CMP大多数由通用的处理器组成,多个处理器执行相同或者类似的任务。异构CMP除含有通用处理器作为控制、通用计算之外,多集成DSP、ASIC、媒体处理器、VLIW处理器等针对特定的应用提高计算的性能。

  • CMP的结构可根据微处理器在存储层次上互连分为三类:共享一级Cache的CMP,共享二级Cache的CMP以及共享主存的CMP。通常,CMP采用共享二级Cache的CMP结构,即每个处理器核心拥有私有的一级Cache,且所有处理器核心共享二级 Cache。

  • 双核心技术与超线程技术:双核心技术理解为两个“物理”处理器,是一种“硬”的方式;而超线程技术只是两个“逻辑”处理器,是一种“软”的方式。

  • 超线程技术可以让单CPU拥有处理多线程的能力,而物理上只使用一个处理器。超线程中的两个逻辑处理器并没有独立的执行单元、整数单元、寄存器甚至缓存等等资源。它们在运行过程中仍需要共用执行单元、缓存和系统总线接口。在执行多线程时两个逻辑处理器均是交替工作,如果两个线程都同时需要某一个资源时,其中一个要暂停并要让出资源,等待那些资源闲置时才能继续。

  • 双核心技术则是通过“硬”的物理核心实现多线程工作:每个核心拥有独立的指令集、执行单元,与超线程中所采用的模拟共享机制完全不一样。在操作系统看来,它是实实在在的双处理器,可以同时执行多项任务,能真正实现并行处理模式,其效率和性能提升要比超线程技术高得多。

2.5 处理器调度

2.5.1 调度的概念

一、作业

  • 作业:用户提交给系统完成的一系列工作的集合。
  • 作业步:每个作业包括若干相对独立、且互相关联的加工步骤,每个加工步骤称为一个作业步。例如:一个作业包括编译、链接、装入、运行四个作业步。
  • 批处理系统的作业:设备输入(提交),存入外存(后备),被选中装入内存(执行),完成后退出内存(完成)。
  • 分时系统中的作业:用户通过终端直接输入进入内存,也就不存在传统所说的作业。

在这里插入图片描述
二、三级调度(按调度层次分类)

1、高级调度:又称为作业调度或长程调度,用于决定把外存上处于后备队列中的哪些作业调入主存,并为它们创建进程、分配必要的资源,然后将新创建的进程排入就绪队列,准备执行。

  • 在批处理系统中,作业进入系统后,首先放在外存上,所以需要进行作业调度,将它们装入主存。但在分时或实时系统中,作业往往通过终端直接输入主存,因此无需进行作业调度。
  • 在执行作业调度时,需要解决的问题:一是接纳多少个作业。即允许有多少个作业同时在主存中运行,太多会影响系统服务性能与质量,太少会使得系统资源利用率和吞吐量太低,因此要根据系统具体情况作出折衷地选择。二是接纳哪些作业。应将哪些作业从外存调入主存,这将取决于采用的调度算法。

2、低级调度:又称为进程调度或短程调度。它决定主存中就绪队列上的哪个进程(单处理器系统)将获得处理器,然后把处理器分配给该进程,使其执行。进程调度是最基本的一种调度,运行频率也最高。

  • 进程调度在执行时可以采用非抢占式和抢占式两种方式:
    1)非抢占方式:采用这种调度方式时,一旦把处理器分配给某个进程后,便让该进程一直执行,直到该进程终止或阻塞时,才退出处理器,系统这时才能把处理器再分配给其它进程。在某个进程正在占用处理器执行时,不允许其它任何进程抢占处理器。
    2)抢占方式:采用这种调度方式时,把处理器分配给某个进程后,在该进程尚未终止或阻塞时,允许系统调度程序根据某种原则,暂停正在执行的进程,回收已经分配的处理器,并将处理器重新分配给其它更为紧急的进程。

在采用抢占方式时,要基于某种原则,通常的原则有:

  • ① 时间片原则。各个进程按规定的时间片轮流执行。
    ② 优先权原则。各个进程按各自优先权的高低,允许优先权高的进程抢占优先权低的进程所占用的处理器。
    ③ 短进程原则。各个进程按各自的长短,允许短进程抢占长进程所占用的处理器。

3、中级调度:又称中程调度,为了进一步提高主存的利用率和系统吞吐量,系统将那些暂时不能运行的进程从主存调到外存(仍然保持进程状态)上的特定区域,这些在外存存放的进程所处的状态称为就绪驻外状态或挂起状态。当这些进程的运行条件具备,且主存又有空闲时,在中级调度的控制下,再将处于外存上的那些合适的进程调入主存,并将其状态修改为就绪状态,放入就绪队列,等待进程调度。

在上述三种调度中,进程调度的运行频率最高,因此进程调度算法不能太复杂,以免占用太多的CPU时间。而作业调度的执行周期较长频率低。所以,允许作业调度算法花费较多的时间。中级调度则处于进程调度与作业调度之间。

三、作业调度与进程调度

  • 作业调度是宏观调度
  • 进程调度是微观调度
  • 两级调度队列(只有作业调度和进程调度):
    在这里插入图片描述
    四、选择调度算法的原则
    1、面向用户的原则:为满足用户的需求而遵循的一些准则。包括:
    • 1)周转时间短。所谓周转时间,是指从进程提交给系统开始,到进程完成为止的这段时间。它包括等待时间和运行时间,主要用于评价批处理系统。
    • 2)响应时间快。响应时间是指从用户通过键盘提交一个请求开始,直至系统首次产生响应为止的时间(进程首次运行前的等待时间),即系统在屏幕上显示出结果为止的一段时间间隔。它主要用于评价分时操作系统。
    • 3)截止时间有保证。截止时间是指进程必须开始执行的最迟时间,或必须完成的最迟时间。对于严格的实时系统,其调度方式和调度算法必须保证这一点。否则,有可能引起灾难性的后果。它主要用于评价实时操作系统。
    • 4)优先权原则。采用优先权原则,目的是让某些紧急的进程,得到及时处理。在要求严格的系统中,还要使用抢占调度方式,才能保证紧急进程得到及时处理。它用于批处理、分时和实时系统。

2、面向系统的原则:为满足系统要求而遵循的一些准则。它包括:

  • 1)系统吞吐量高。系统吞吐量是指系统在单位时间内所完成的进程数量。显然进程的平均长度将直接影响到系统吞吐量的大小。另外进程调度的方式与算法,也会对系统吞吐量产生较大的影响。它主要用于评价批处理系统。
  • 2)处理器利用率高。对于大、中型系统,由于CPU价格昂贵,所以处理器的利用率就成为十分重要的指标。在实际系统中,CPU的利用率一般在40%~90%之间。但是,该准则一般不用于微机系统和某些实时系统,它主要用于大、中型系统。
  • 3)各类资源的平衡利用。对于大、中型系统,不仅要使处理器利用率高,而且还应能有效地利用其他各类资源,保持系统中各类资源都处于忙碌状态。同样,该准则一般不用于微机系统和某些实时系统,它主要用于大、中型系统。

3、调度算法本身的调度性能准则

  • 1)易于实现
  • 2)执行开销比

要设计一个理想的调度算法是一件十分困难的事,在实际系统中, 调度算法往往折衷考虑。大多数操作系统都采用比较简单的调度算法。

五、相关一些指标的计算
1、周转时间

  • 1)周转时间:从作业/进程提交到作业/进程完成的时间间隔
    Ti = tci – tsi
    tsi表示作业/进程i的提交时间,即作业/进程i到达系统的时间
    tci表示作业/进程i的完成时间

  • 2)平均周转时间
    在这里插入图片描述

  • 3)带权周转时间W
    W = T/R
    T为周转时间,R为实际运行时间

  • 4)平均带权周转时间
    在这里插入图片描述

2.5.2 作业调度

一、作业状态

  • ① 提交状态 ② 后备状态
  • ③ 执行状态 ④ 完成状态
    在这里插入图片描述

二、作业控制块JCB

  • 系统为每个作业设置了一个作业控制块(JCB)
  • 它记录该作业的有关信息
  • JCB是作业在系统中存在的标志
    在这里插入图片描述

三、作业调度的功能

  • 1、记录系统中各个作业的情况
    2、按照某种调度算法从后备作业队列中挑选作业
    3、为选中的作业分配内存和外设等资源
    4、为选中的作业建立相应的进程,并把该进程放入就绪队列
    5、作业结束后进行善后处理工作

设计目标是最大限度地发挥各种资源的利用率和保持系统内各种活动的充分并行

四、常用作业调度算法
1、先来先服务算法(FCFS)

  • 先来先服务调度算法是一种最简单的调度算法,系统开销最少,总体性能尚可。
  • 系统采用这种调度算法时,是选择最先进入后备队列的一个或若干个作业,把作业调入内存,分配资源,创建进程。
  • 先来先服务调度算法比较有利于长作业,对短作业不够有利。

2、短作业优先算法(SJF)

  • 短作业优先调度算法是对短作业优先调度,是从后备队列中选择一个或若干个估计运行时间最短的作业,将其调入内存,分配资源,创建进程。
  • 短作业优先调度算法照顾到了系统中占大部分的短作业,有效地降低了作业的平均等待时间,提高了系统的吞吐量,但对长作业不利,长作业可能长时间得不到执行,出现“饥饿”或“饿死”现象;同时难以准确估计作业运行时间,影响调度性能。

3、高响应比优先调度算法

  • 高响应比优先调度算法是一个比较折衷的算法,它是从后备队列中选择响应比高的作业,将其调入内存,分配资源,创建进程。
    其中响应比 = 响应时间 / 要求服务时间
                       = (等待时间 + 要求服务时间)/ 要求服务时间
                       = 1 + 等待时间 / 要求服务时间
  • 由上式可以看出:
  • ① 如果作业的等待时间相同,则要求服务时间越短,其优先权越高。因此,该算法有利于短作业。
  • ② 当要求服务的时间相同时,作业的优先权取决于其等待时间,因而实现的是先来先服务。
  • ③ 对于长作业,当其等待时间足够长时,其优先权便可以升到最高,从而也可获得调度。
  • 高响应比优先调度算法既照顾了短作业,又考虑了作业到达的先后次序,也不会使长作业长期得不到服务,因此是一个比较全面考虑的算法,但每次进行调度时,都需要对各个作业计算响应比。所以系统开销很大,比较复杂。

    4、优先权(数)调度算法

  • 为每个作业确定一个优先数,当系统采用优先权调度算法时,选择后备队列优先权最高的一个或若干个作业,把作业调入内存,分配资源,创建进程。
  • 优先数的确定:可以由用户设定,也可以由系统设定。
  • 优先数一般取决于估计运行时间、资源需求量、用户需求、作业类型等因素。

2.5.3 进程调度

一、有关进程调度

  • 进程调度(scheduling)是OS的一个重要组成部分,其任务是选择下一个要运行的进程。那么选择什么进程最合适???
    首先要搞清:
    1、不同OS的不同目标
    2、不同性质的进程的执行模式
    • 1)CPU导向进程 科学计算
    • 2)I/O导向进程 游戏、PPT
    • 3)平衡型进程 网页浏览、视频

    1、进程调度的时机:当发现以下情况时,当前运行进程的CPU被收回,需要重新进行进程调度。

  • 1)正在执行的进程正确完成, 或由于某种错误而终止运行(陷阱或中断)
  • 2)执行中的进程提出I/O请求, 等待I/O完成时
  • 3)在分时系统中,分给进程的时间片用完时
  • 4)按照优先级调度时, 有更高优先级进程变为就绪时(抢占方式)
  • 5)在进程通讯中, 执行中的进程执行了某种原语操作, 如wait操作、阻塞原语和唤醒原语时, 都可能引起进程调度

    2、进程调度的工作

  • 保存现场:暂停正在执行的进程,保存进程的相关信息到进程控制块
  • 挑选进程:按照某种调度算法从进程就绪队列中选择进程
  • 恢复现场:从选中进程的PCB中恢复CPU现场信息

二、中断
    1、中断的概念:指当出现需要时,CPU暂时停止当前进程的执行,转而执行处理新情况的过程。
在这里插入图片描述

    2、中断系统的作用

  • ① 提高主机的利用率
  • ② 及时进行事故处理
  • ③ 实现分时操作
  • ④ 实现实时操作
  • ⑤ 方便程序调试

    3、中断中的一些概念

  • 中断源:引起中断的事件或发出中断请求的来源。
  • 中断请求:中断源向CPU发出处理请求。
  • 中断响应:由系统硬件对中断请求作出反应的过程。

    4、中断类型:

  • 1)按中断来源中断可分为:

    • ① 外中断:通常指硬中断,指来自CPU外的外部设备产生的中断
      · 外中断可以发生在用户态,也可以发生在内核态
      · 根据来源又分为外部中断(键盘上的中断键)、I/O中断
      · 外中断在一条指令结束之后、下一条指令开始之前进行响应
      · 根据外中断是否可以屏蔽,又可以分为可屏蔽中断、不可屏蔽中断,通过中断允许标志位进行相应的中断屏蔽设置
    • ② 内中断:也称为异常,是执行指令时检测到错误或异常引起的,例如溢出、除数为0、校验错、地址越界等
      · 内中断通常发生在用户态,内中断一般不可屏蔽,必须立即响应,在指令执行期间进行中断响应,并且允许多次响应
  • 2)按中断性质中断可分为:

    • ① 强迫性中断:由外部请求、或来自CPU内部的事件、或执行中的异常事件引起的过程,不是当前运行进程期望的。
      · 主要包括:硬件故障中断、程序(错误)中断、外部中断、输入输出(I/O)中断
    • ② 自愿性中断:是由执行预先编写的代码(如执行访管指令或系统调用)引起,用以主动请求系统服务。
  • 各种中断优先级:从高到低一般为硬件故障中断、自愿性中断、程序中断、外部中断、I/O中断

5、中断的硬件结构
在这里插入图片描述
6、中断响应

  • 中断系统主要由两部分:中断装置(硬件)、中断处理程序(软件)
  • 中断响应由中断装置(硬件)实施
    • 中断寄存器:保存各个中断源的中断请求,每一位称为一个中断位,发送中断请求,就设置对应的中断位
    • 同时有多个中断请求,选优先级高的优先响应
    • 中断扫描机构:设置在CPU控制部件中,按规则扫描中断寄存器,判断中断信号,并按优先级响应

7、中断向量

  • 每个中断有一个唯一对应的中断向量号(通常为中端类型号)
  • 所有的中断向量号按顺序存放在中断向量表
  • 中断向量表一般放在内存固定区域,一般在低地址部分
  • 通过中断向量表查找中断向量号,再由中断向量号可以计算得到相应的中断处理程序的地址,从而执行
  • 中断向量表:
    在这里插入图片描述

8、中断处理

  • 中断处理由中断处理程序(软件)进行相应处理
  • 中断处理过程:
    • ① 保存被中断进程的现场信息
    • ② 分析中断原因
    • ③ 转入执行相应中断处理程序进行处理
    • ④ 恢复被中断进程现场(即中断返回)
      ● 选取可以立即执行的进程
      ● 恢复工作现场

中断处理的一般过程:
在这里插入图片描述
9、中断优先级

  • 中断响应优先级:由硬件排队线路或中断查询程序的查询顺序决定的,不可动态改变
  • 中断处理优先级:中断处理优先级可以由中断屏蔽字来改变

10、中断屏蔽和中断禁止

  • CPU在其关键控制寄存器有一位用于控制中断的开关,中断禁用就是把这一位关闭,整个系统中断就不可能发生。
  • 中断控制器里有个中断屏蔽寄存器MASK,每一个中断源在寄存器里对应有屏蔽位,如果这一位被设置,这个中断源就被屏蔽了,即使这个中断源产生中断信号,也不会被处理。

11、多重中断

  • 1)顺序处理方式
    2)嵌套处理方式
    在这里插入图片描述

三、常用的进程调度算法

  • 先来先服务调度算法FCFS
  • 短进程优先调度算法SPF与最短剩余时间优先调度算法SRTF
  • 时间片轮转调度算法RR
  • 最高优先权(级)调度算法法FPF
  • 高响应比优先调度算法HRRF
  • 多级队列调度算法MQ
  • 多级反馈队列调度算法MFQ
  • 公平共享调度算法FSS

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

  • 先来先服务调度算法是一种最简单的调度算法,系统开销最少,总体性能尚可。系统采用这种调度算法时,总是从就绪队列中选择一个最先进入就绪队列的进程,把处理器分配给该进程,使之得到执行。进程一旦占有了处理器,就一直运行下去,直到完成或因发生某种事件而阻塞,才退出处理器。
  • 先来先服务调度算法是非抢占式的,这种调度算法有利于长进程,对短进程效果不太理想。
  • 设有三个作业(进程),编号分别为1, 2, 3。各作业分别对应一个进程。各作业依次到达,相差一个时间单位。
    在这里插入图片描述
  • FCFS调度算法性能指标:
    在这里插入图片描述
  • 比较有利于长作业(进程),而不利于短作业(进程)
  • 容易实现,但总体效率不太理想

2、短进程优先算法(SPF)

  • 短进程优先调度算法是从就绪队列中选择一个估计运行时间最短的进程,将处理器分配给该进程,使之占有处理器并执行。直到该进程完成或因发生某种事件而阻塞,才退出处理器。
  • 短进程优先调度算法是非抢占式的,这种算法照顾到了系统中的短进程,有效地降低了进程的平均等待时间,提高了系统的吞吐量。
  • 算法对长进程不利,可能长时间得不到执行,甚至有可能一直得不到执行,称为“饥饿”或“饿死”现象;该算法也未能依据进程的紧迫程度来划分执行的优先级;同时实际中难以准确估计进程的执行时间,从而影响调度性能。

3、最短剩余时间优先算法(改进的短进程优先)(SRTF)

  • 最短剩余时间优先调度算法是短进程优先调度算法的抢占式的动态版本。它总是将CPU分配给当前就绪队列中还需要最少时间可以完成的进程。
  • 当有新进程加入就绪队列时,如果它需要的运行时间比当前运行的进程所需的剩余时间还短,则执行切换。
  • 最短剩余时间优先调度算法是抢占式的,进程的优先权是动态的,随着进程运行和完成时间的减少而增加,这种调度算法充分照顾到了剩余运行时间短的进程。

例如有4个进程,如下表所示:
在这里插入图片描述
在这里插入图片描述

4、时间片轮转调度算法(RR)

  • 在分时系统中,为了保证人机交互的及时性,系统使每个进程依次按照时间片方式轮流执行,即时间片轮转调度算法。在该算法中,系统将所有的就绪进程按照进入就绪队列的先后次序排列。每次调度时把CPU分配给队首进程,让其执行一个时间片。当时间片用完,由计时器发出时钟中断,调度程序暂停该进程的执行,使其退出处理器,并将它送到就绪队列的末尾,等待下一轮调度执行。然后,把CPU分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程,在一定的时间(可以接受的等待时间)内,均能获得一个时间片的执行时间。
  • 时间片轮转调度算法是面向时间片的,所有就绪进程是轮转规则,这种调度算法适用于交互进程的调度。
  • 时间片轮转法(Round - Robin, RR)主要用于分时系统
  • 时间片是一个小的时间单位,通常为10 ~~ 100ms数量级

例如:4个进程A,B,C和D依次(同时)进入就绪队列,分别需要运行12, 5, 3,6个时间单位

  • 下图为时间片q=1个时间单位和q=4个时间单位时进程运行情况
    在这里插入图片描述
  • 计算时间片轮转调度算法的性能指标:
    在这里插入图片描述
  • 【时间片长度变化的影响】
    过长->退化为FCFS算法,进程在一个时间片内都执行完,响应时间长。
    过短->用户的一次请求需要多个时间片才能处理完,CPU现场切换次数增加,响应时间长。
  • 【对响应时间的要求】T(响应时间)= N(进程数目)* q(时间片)
  • 【如何选择合适的时间片】
    ① 系统的响应时间 ② 就绪队列进程的数目
    ③ 进程的转换时间 ④ CPU运行指令速度

5、最高优先权调度算法(FPF)

  • 为了照顾紧迫型进程获得优先处理,引入了优先权调度算法,也称为外部优先权调度算法。它是从就绪队列中选择一个优先权最高的进程,获得处理器并执行。
  • 优先权调度算法分为非抢占式优先权调度和抢占式优先权调度。
  • 优先级确定:可由系统内部定义或由外部指定。
  • 对于优先权调度算法,其关键在于确定进程优先级的方式:是采用静态优先权,还是动态优先权
  • 1)静态优先权:静态优先权是在创建进程时确定的,并且规定它在进程的整个运行期间保持不变。
    • 确定进程优先权的依据有:
      ① 进程的类型 ② 进程对资源的需求 ③ 用户的要求
    • 静态优先权方法简单易行、系统开销小,但缺点是不够精确,很可能出现优先权低的进程,因系统中总有比其优先权高的进程要求调度,导致该进程很长时间内得不到调度,甚至一直得不到调度,出现“饥饿”或“饿死”现象。静态优先权可以支持非抢占式算法,也可以支持抢占式算法。
  • 2)动态优先权:动态优先权要配合抢占调度方式使用,它是指在创建进程时所赋予的优先权,可以随着进程的推进而发生改变,以便获得更好的调度性能。
    • 在就绪队列中等待调度的进程,随着其等待时间的增加,其优先权以某个速率增加。因此,对于优先权初值很低的进程,在等待足够长的时间后,其优先权也会升为最高,从而获得调度,占用处理器执行。
    • 正在执行的进程,其优先权将随着执行时间的增加而逐渐降低,使其优先权可能不再是最高,从而暂停其执行,将处理器回收并分配给其它优先权更高的进程。可以防止一个长进程长期占用处理器的现象。

例如:有一组进程,它们都在时刻0到达,依次为p1, p2 ,…, p5,各自运行时间和优先数如下所示,优先级调度算法执行顺序如图:
在这里插入图片描述
在这里插入图片描述

6、高响应比优先调度算法(HRRF)

  • 高响应比优先调度算法是一个比较折衷的算法,它是从就绪队列中选择一个响应比最高的进程,让其获得处理器并执行,直到该进程完成或因等待某种事件退出处理器为止。
        响应比 = 响应时间 / 要求服务时间
  • 高响应比优先调度算法是非抢占式的,这种调度算法既照顾了短进程,又考虑了进程到达的先后次序,也不会使长进程长时间得不到服务。因此,它是一个考虑比较全面的算法。但每次进行调度时,都需要对各个进程计算响应比。所以,系统开销很大,比较复杂。

7、多级队列调度算法(MQ)

  • 目前的计算机系统,不少都具有多种操作方式,既有批处理操作方式,用于处理批量型作业;又有分时操作方式,用于处理交互型作业。通常为批处理作业建立的进程将排入后台的就绪队列;为交互型作业所建立的进程则排入前台的就绪队列。前台采用时间片轮转调度算法,后台可采用优先权调度算法或短进程优先调度算法等。
  • 一般来说,多级队列调度算法是根据进程的类型或性质不同,将就绪进程分为若干个独立队列,不同类型或性质的进程固定地属于一个队列。每个队列可以采用适合自己要求的调度算法。不同的队列可以使用不同的调度算法。
  • 采用多级队列调度算法时,通常有两种方式处理各队列间的关系。一种是规定每个队列的优先权,优先权高的队列优先获得调度执行。另一种是为各队列分配一定比例的处理器时间,然后分别调度使用。
    在这里插入图片描述

8、多级反馈队列调度算法(MFQ)

  • 前面所介绍的各种进程调度算法,都存在一定的局限性。如短进程优先调度算法,不利于长进程,而且如果没有有效地表明进程的长度,其算法将无法正常使用。而多级反馈队列调度算法,不必事先知道各个进程所需的执行时间,又可以满足各种类型进程的需要,是目前一种较好的进程调度算法。
  • 多级反馈队列调度算法可以是采用时间片轮转为基础的抢占式调度算法,也可以是基于先后次序的非抢占式算法。
  • 这种调度算法具有较好的性能,能满足各种类型用户的需求。
  • 多级反馈队列调度算法工作基本步骤:
    ◆ ① 系统中设置多个就绪队列,每个队列对应一个优先级。
    ◆ ② 各就绪队列中进程的运行时间片不同,高优先级队列的时间片小,低优先级队列的时间片大。
    ◆ ③ 新进程进入系统后,先放入第1个队列的末尾。
    ◆ ④ 系统先运行第1个队列中的进程。
    ◆ ⑤ 在规定的时间片长度内进程不能执行结束,则暂停执行并放入下一级就绪队列。
    ◆ ⑥ 只有上一级就绪队列没有进程,CPU才会调度下一级队列中的进程。
    这种调度算法基于时间片的抢占式算法。

在这里插入图片描述
在这里插入图片描述

  • I/O型进程:让其进入最高优先级队列,以及时响应I/O交互。通常执行一个小时间片,要求可处理完一次I/O请求的数据,然后转入到阻塞队列。

  • 计算型进程:每次都执行完时间片,进入更低级队列。最终采用最大时间片来执行,减少调度次数。

  • 为适应一个进程在不同时间段的运行特点,I/O完成时,提高优先级;时间片用完时,降低优先级。

  • 算法优点:
    ◆ 为提高系统吞吐量和缩短平均周转时间而照顾短进程。
    ◆ 为获得较好的I/O设备利用率和缩短响应时间而照顾I/O型进程。
    ◆ 不必估计进程的执行时间,动态调节。
    ◆ 多级反馈队列调度算法,则不必事先知道各种进程所需的执行时间,仍能基本满足短进程优先和I/O频繁的进程优先的需要,因而是目前公认的较好的一种进程调度算法。
    ◆ 在UNIX系统、WindowsNT、OS/2中都采用了类似的调度算法。

9、公平共享算法(FSS)

  • 系统为每个用户分配一定比例的CPU时间,然后按照这个比例在各用户之间挑选相应的进程。

四、常用的进程调度算法对比

  • 三个量的含义:
    w — 至今在系统中用于等待和执行所花费的时间
    e — 至今在CPU上执行用去的时间。
    s — 进程所需的总体运行时间(包括e)

在这里插入图片描述

在所学的调度算法中,对所有进程和作业都是公平合理的调度算法是 A ;最有利于提高系统吞吐量的作业调度算法是 B ;能兼顾作业等待时间和作业执行时间调度算法是 C ;最有利于提高资源的使用率、能使短作业、长作业及交互作业用户都比较满意的调度算法是 D ;为实现人机交互作用应采用调度算法是 E ;能对紧急作业进行及时处理的调度算法是 F 。
A – F:(1)FCFS调度算法
(2)时间片轮转法
(3)短作业/进程优先调度算法
(4)多级反馈队列调度算法
(5)高响应比优先算法
(6)基于优先权的剥夺调度算法
答案:1,3,5,4,2,6

  • 假设在单道批处理环境下有四个进程,已知它们进入系统的时间、估计运行时间。要求:应用先来先服务、短进程优先、高响应比优先算法,分别给出进程的执行顺序、并计算4个进程的平均周转时间和平均带权周转时间。
    在这里插入图片描述
    1、先来先服务调度算法(FCFS)— 计算结果:
    在这里插入图片描述
    2、短进程优先算法(SPF)— 计算结果:
    在这里插入图片描述
    3、最高响应比优先算法(FPF)— 计算结果:
    在这里插入图片描述

五、线程调度
1、用户级线程

  • 线程是在用户级实现的,操作系统内核并不知道线程的存在,所以系统内核不负责线程的调度,只为进程提供服务,即从就绪队列中挑选一个进程(例如A),为它分配一个时间片(例如采用时间片轮转调度算法),然后由进程A内部的线程调度决定让A的哪个线程(如A1)运行。线程A1将一直运行下去,不受时钟中断的干扰,直至它用完进程A的时间片。之后系统内核将再选择另一个进程运行。当进程A再次获得时间片时,线程A1将恢复运行。如此反复,直到A1完成工作,进程A内部的线程调度再调度另一个线程运行。一个进程内线程的行为不影响其它进程。系统内核只对进程进行调度,而不管进程内部的线程。
  • 如果进程分到的时间片长,而单个线程每次运行时间短,那么,在A1让出CPU后,A的线程调度就调度A的另一个线程(如A2)运行。这样,在系统内核切换到进程B之前,进程A内部的线程就会发生多次切换。运行时选择线程的调度算法可以是进程调度算法中的任何一种算法。实际上,最常用的算法是轮转法和优先级法,惟一的限制是时种中断对运行线程不起作用。

2、系统级线程

  • 在操作系统内核支持线程的情况下,由系统内核调度线程,即内核从就绪线程池中选出一个线程,(不考虑它是哪个进程的线程,当然,内核知道是哪个进程的)分给该线程一个时间片,当它用完时间片后,内核把它暂停。如果线程在给定的时间片内阻塞,内核就调度另一个线程运行,后者可能和前者同属一个进程,也可能属于另外一个进程。
  • 用户级线程和系统级线程的主要区别如下:
    • ① 性能。用户级线程切换可用机器指令,速度快;而系统级线程切换需要全部上下文切换,因而速度慢。
    • ② 挂起。系统级线程方式下,一个线程因等待I/O而阻塞时,不会挂起整个进程;而用户级线程方式下却会挂起整个进程。

在这里插入图片描述
六、多处理机调度
1、多处理机系统分为以下两种类型:

  • ① 松散耦合多处理器系统(或称集群系统):是一组相对自主系统的集合体,每台处理器有自己的内存和I/O通道,往往通过通信线路互连在一起,每台处理器可以有自己的操作系统。
  • ② 紧密耦合多处理器系统:一组处理器共享一个内存,并且在一个操作系统的集中控制下工作。该结构中所有处理器都是同构的。

2、多处理机操作系统分为以下两种类型:

  • ① 对称多处理器系统:各个处理器地位平等,各自独立进行进程调度。
  • ② 主从多处理器系统:操作系统放在一个称做主控机(Master)的处理器上,而其他处理器是从机(Slaves)。主控机是通用处理器,它控制各从机的工作和执行I/O操作;而从机仅执行主控机指派的计算任务,即运行用户进程,为主控机提供服务。所有的系统调用都重定向到主控机,由它予以处理。

3、多处理机操作系统按控制方式分为三种类型:

  • ① 主从式操作系统:由一台主处理机记录、控制其它从处理机的状态,并分配任务给从处理机。
  • ② 独立监督式:每一个处理机均有各自的管理程序(核心)。采用独立监督式操作系统的多处理机系统有IBM 370/158等。
  • ③ 浮动监督式:每次只有一台处理机作为执行全面管理功能的“主处理机”,但根据需要,“主处理机”是可浮动的,即从一台切换到另一台处理机。这是最复杂、最有效、最灵活的一种多处理机操作系统,常用于对称多处理机系统,适用于紧密耦合多处理机体系。

4、多处理机调度方法

  • 多处理器调度包括如下三个相关的这方面:
    1)给处理器分配进程,即把进程加到某个处理器所对应的就绪队列中。
    2)在单个处理器上是否使用多道程序技术。
    3)实际分派进程。

1)给处理器分配进程
如果多处理器的体系结构是相同的,最简单的调度方法就是把所有处理器看做一个资源池,根据要求把进程分配给处理器。常用的分配方式有静态和动态分配两种。
采用静态分配就是把一个进程固定地分给一个处理器,从开始活动直至完成。这样每个处理器就保持一个就绪进程队列。其优点是调度开销小,缺点是各处理器可能忙闲不均。
为了克服静态分配的缺点,可采用动态分配方式,即把系统中所有就绪进程放入一个全局队列,从中选出进程并分派到任何可用的处理器上。这样,一个进程在其生存期内可能在不同时间、不同处理器上执行。
2)在单个处理器上是否使用多道程序技术
在传统的多处理器中,每个单一的处理器应在若干进程之间进行切换,以便获得高利率和良好性能。如果一个应用程序由多线程的单个进程实现,它运行在多处理器上,那么保持每个处理器尽可能地忙不再是最重要的因素。在这种情况下,一般考虑的焦点是为应用程序提供更好的性能。如果组成一个应用程序的所有线程同时运行,其性能最好。
3)分派进程
在多处理器系统中,不需采用复杂的调度算法,方法越简单,开销越少,效率就越高。在双CPU系统中,进程采用FCFS调度算法与采用轮转法、最短剩余时间优先法相比较,系统吞吐量的变化很小。所以,对于多处理器系统来说,采用简单的FCFS算法或带有静态优先级的FCFS算法是合适的。
为了防止多个处理器同时从一个就绪队列中选择进程时出现的竞争问题,可以采用转锁方式实现互斥。但简单的转锁方式存在“循环等待”问题,当一个进程占用转锁时,如果其他进程也要用该锁,就得循环测试并等待,直至那个进程释放该锁。
有些系统采用“灵巧”调度,即得到转锁的进程设置一个全局标志,声明它当前占用该锁。当它释放该锁时,就清除该标志。调度程序不让占有转锁的进程停止,而是多给它一点儿运行时间,使之尽快完成临界区工作,从而释放该锁。

七、实时调度
1、实时任务类型

  • 实时系统中存在着若干实时任务,它们对时间有着严格的要求。通常,一个特定任务都与一个截止时间相关联。截止时间分为开始时间和完成时间。根据对截止时间的要求,实时任务可分为硬实时任务和软实时任务。硬实时任务是指系统必须满足任务对截止时间的要求,否则会导致无法预测的后果或对系统产生致命的错误。软实时任务是指任务与预期的截止时间相关联,但不是绝对严格的,即使已超出任务的截止时间,仍然可以对它实施调度并且完成。
  • 按照任务执行是否呈现周期性规律来分,实时任务分为周期性任务和非周期性任务。周期性任务是指以固定的时间间隔出现的事件。非周期性任务是指事件的出现无法预计,但规定了必须完成或者开始的截止时间,或者二者都被规定好。

2、实时调度算法

  • 实时调度算法分为静态和动态两种方式。静态调度是在系统开始运行之前做出调度决定。仅在预先提供有关需要完成的工作和必须满足的截止时间等信息的情况下,静态调度才能起作用。动态调度是在运行时做出调度决定。动态调度算法不受上述条件的限制。
  • 1)优先级随速率单调的调度算法(RMS):是针对可抢占的周期性进程采用的经典静态实时调度算法,它用于满足下述条件的进程:
    ◆ ① 每个周期性进程必须在其周期内完成。
    ◆ ② 进程间彼此互不依存。
    ◆ ③ 每个进程在每次运行时需要相同的CPU时间。
    ◆ ④ 非周期性进程都没有截止时间限制。
    ◆ ⑤ 进程抢占瞬间完成,开销可以不计。
  • 前4个条件是必须的,最后一个条件虽不是必须的。RMS为每个进程分配一个固定的优先级,它等于触发事件发生的频度。运行时,调度程序运行优先级最高的进程。如果需要的话,抢占当前正在运行的进程。
  • 2)最早截止时间优先调度算法(EDF):是流行的动态实时调度算法。它不要求被调度进程具有周期性,也不要求每次占用CPU运行的时间相同。
  • **核心思想:**每当一个进程需要占用CPU时,它要表明自己的存在和截止时间等信息。调度程序把所有可以运行的进程按照其截止时间先后顺序放在一个表格中。执行调度时,就选择该表中的第一个进程——它如果更近,新进程就抢占当前运行进程。
  • 需要说明的是,静态优先级算法在CPU利用率不很高的情况下才适用。与RMS不同,EDF对任何可调度的进程集合问题都适用,甚至CPU的利用率可达到100%,但算法更为复杂。

1
1
1
1
    

2.5.4 操作系统调度实例

一、UNIX操作系统
1、UNIX基本介绍:UNIX系统是分时系统,所以没有设置作业调度。
进程调度采用的算法:多级反馈队列轮转调度算法。
2、UNIX基本结构与要求:

  • 在UNIX系统中进程被定义为映像的执行,映像是计算机的执行环境,它包括各种寄存器及存储器的值、打开文件的状态及现行目录等等。进程映像的组成部分:寄存器、进程控制块proc结构和user结构、进程数据区ppda (共享正文段(由text结构控制)、数据段和栈段(含用户栈和核心栈))。对于一个进程的映像来说,proc结构、进程页表、text结构是常驻内存的,而user结构、正文段、数据段是非常驻内存部分。它们在用户虚拟空间形成一个整体,一起换进换出。
  • 在Unix操作系统中,所以程序,不论是用户级上还是在内核级上执行的,都出现在某个进程的现场内,所有的用户程序都在它们自己的进程现场中运行。当这些用户进程通过系统调用请求内核服务的时候,实现该系统调用的内核代码继续在请求进程的现场内执行,这就能让内核方便的访问进程的所有状态及其他地址空间。
  • 因为系统的所有活动,无论是用户级上的还是内核级上的,都发生在某个进程的现场内,所有UNIX内核只调度需要执行的进程。当使用传统的分时调度策略的时候,在用户级执行的进程不会被分入时间内执行。只有当前内核进程明确允许,才切换到内核执行的另一个进程。

3、UNIX进程状态与状态转换:

在这里插入图片描述
4、UNIX调度系统:UNIX系统是一个多用户分时系统,其分时性是通过对用户进程频繁的调度来实现的,系统的调度程序分成两部分,即处理机调度程序(swtch)和进程对换程序(sched)。

  • 进程调度时机:
    ① 若一个进程已到达它不能超过的某个点,这时要调用sleep程序
    ② 进程终止
    ③ 进程从系统调用返回到用户态时,但它并不是最适宜运行的进程
    ④ 核心处理完中断后,进程回到用户态,但存在更适宜运行的进程
    进程调度是由swtch程序实现的
 swtch( ) 
 {    while(没有进程被选中执行)
      {    for (所有在就绪队列中的进程)
           选出优先级最高且在内存的一个进程;
           if(没有合适进程可以执行)
              机器作空转;
              /*当中断发生后,使机器摆脱空转状态*/
       }
       从就绪队列中移走该选中进程;
       恢复选中进程的现场,令其投入运行;}

5、进程优先级的级别示意图:
在这里插入图片描述

用户优先级类和核心优先级类

二、Linux操作系统
1、Linux基本介绍:Linux进程调度是将优先级调度、时间片轮转法调度、先进先出调度综合起来应用,基本方式为抢占式优先级。Linux系统中,不同类型的进程调度策略也不一样。减。
2、Linux基本结构:Linux系统中的进程用task_struct结构来描述,进程调度的依据是task_struct结构中的policy、priority、counter和rt_priority。

  • PCB中设置Policy数据项,其值用于反映针对不同类型的进程而采用的调度策略。SCHED_RR和SCHED_FIFO用于实时进程,分别表示轮转调度策略和先进先出调度策略;SCHED_OTHER表示普通进程,也按照轮转调度策略处理,这三类调度策略均基于优先级。
  • PCB中设置Priority数据项,其值为普通进程的调度优先级,普通进程的可用时间片的初始值即为该值,该值通过系统调用是可以改变的。
  • PCB中设置rt_priority数据项,其值是实时进程专用的调度优先级,实时进程的可用时间片的初始值即为该值,该优先级也可以用系统调用来修改。
  • PCB中设置counter数据项。用于进程可用时间片时值的计数,初始值为rt_priority或Priority,进程启动后该值随时钟周期递减。

3、Linux进程状态与状态转换:
在这里插入图片描述
4、Linux调度策略:三种不同的调度策略

  • SCHED_FIFO(实时调度策略,先到先服务),适合于短实时进程。
  • SCHED_RR(实时调度策略,时间片轮转),适合于每次运行需要较长时间的实时进程。
  • SCHED_OTHER(分时调度策略)是传统的UNIX调度策略,适合于交互式的分时进程。
  • 系统规定,实时进程的优先级高于其他类型进程的优先级,将得到优先调用,实时进程根据实时优先级决定调度权值。分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。
  • SHCED_RR和SCHED_FIFO的不同点:
    ▶当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并将其置于就绪队列的队尾。放在队列尾部保证了所有具有相同优先级的RR任务的调度公平。
    ▶SCHED_FIFO一旦占用CPU则一直运行。一直运行直到有更高优先级任务到达或自己放弃。
    ▶如果有相同优先级的实时进程(根据优先级计算的权值一样)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。而RR可以让每个任务都执行一段时间。
  • SHCED_RR和SCHED_FIFO的相同点:
    ▶RR和FIFO都只用于实时任务。
    ▶创建时优先级大于0(1-99)。
    ▶按照可抢占优先级调度算法进行。
    ▶就绪态的实时任务立即抢占非实时任务。

5、Linux调度时机

  • ① 当前进程调用系统调用nanosleep( )或pause( )
  • ② 进程终止
  • ③ 在时钟中断处理程序执行过程中,发现当前进程连续运行的时间过长
  • ④ 当唤醒一个睡眠进程
  • ⑤ 一个进程通过执行系统调用来改变调度策略或者降低自身的优先级

三、Windows操作系统
1、Windows线程调度基本特征

  • 1)调度单位是线程
  • 2)基于优先级的抢占式处理器调度,依据优先级和分配时间片来调度
  • 3)优先级和时间片轮转混合
    Windows操作系统的调度系统总是运行优先级最高的就绪线程。在同一优先级的各线程按时间片轮转算法进行调度。如果一个高优先级的线程进入就绪状态,当前运行的线程可能在用完它的时间片之前就被暂停,高优先级的线程将获得CPU,进而执行。

2、Windows可触发线程调度的事件

  • 1)一个线程进入就绪状态
  • 2)一个线程的时间片结束
  • 3)线程由于调用系统服务而改变优先级或被系统本身改变其优先级
  • 4)正在运行的线程被改变了所运行的处理器(在多处理器系统中)

3、Windows线程优先级

  • 1)Windows内部使用32个线程优先级,范围从0到31,数值越大,优先级越高。
  • 2)实时线程(如设备监控线程)优先级:16—31
  • 3)可变线程优先级:1—15
  • 4)级别0保留为系统使用,仅用于对系统中空闲物理页面进行清零的零页线程。
  • 5)线程的基本优先级 = [进程的基本优先级-2,进程的基本优先级+2] ,由应用程序控制。
  • 6)线程的动态优先级 = [进程的基本优先级-2, 31] ,由Windows NT核心控制。
    在这里插入图片描述

4、Windows线程时间配额(Quantum)

  • 1)光有线程优先级的话,相同优先级的线程中只有一个执行到底,其它都处于等待,所以有时间配额制度。
  • 2)当一个线程被调度进入运行状态时,它运行一个称为时间配额的时间片,时间配额是Windows2000/XP允许一个线程连续运行的最大时间长度。
  • 3)时间配额不是一个时间长度值,而一个称为配额单位(quantum unit)的整数。缺省时,在Windows 2000专业版中线程时间配额为6;而在Windows 2000/XP服务器中线程时间配额为36。
  • 4)一个线程用完了自己的时间配额时,如果没有其它相同优先级线程,Windows 2000将重新给该线程分配一个新的时间配额,并继续运行。

5、Windows基于线程实现优先级驱动的抢先式多任务的方式如下:

  • 1)主动切换:一个线程可能因为等待某个对象而主动放弃处理器的使用。当该线程等待的事件出现时,它会排到相应优先级的就绪队列尾。通常进入等待状态线程的时间配额不会被重置,而是在等待事件出现时,线程的时间配额被减1,相当于1/3个时钟间隔;如果线程的优先级大于等于14,在等待事件出现时,线程的优先级被重置。

  • 2)抢先:当一个高级优先级线程进入就绪状态时,正在处于运行状态的低优先级线程被抢先。调度器只是根据线程优先级判断一个线程是否被抢先,而不管线程是处于用户态还是内核态。当线程被抢先时,它被放回相应优先级的就绪队列的队首。
    ▶ 处于实时优先级的线程在被抢先时,时间配额被重置为一个完整的时间片;处于动态优先级的线程在被抢先时,时间配额不变,重新得到处理机使用权后将运行到剩余的时间配额用完。

  • 3)时间配额用完:处于运行状态的线程用完它的时间配额时,Windows会中断该线程的运行,判断是否需要降低该线程的优先级,并查找是否有其他高优先级或相同优先级的线程等待运行。
    ▶ a)如果该线程的优先级降低了,系统会寻找一个优先级高于该线程的新就绪线程来调度。b)如果该线程的优先级没有降低,并且有其他优先级相同的就绪线程,系统将选择相同优先级的就绪队列中的下一个线程进入运行状态,该线程被排到就绪队列队尾。c)如果没有优先级相同的就绪线程可运行,该线程将得到一个新的时间配额并继续运行。

2.6 进程同步和互斥

在这里插入图片描述
在这里插入图片描述

2.6.1 进程同步与互斥概念

一、进程间的相互关系

  • 进程间的相互关系主要分为如下形式:
    1、无关关系
    2、相关关系
    • ① 互斥 — 竞争同一资源而发生相互制约
    • ② 同步 — 协同完成一项任务
    • ﹡ 通信 — 交换信息
      在这里插入图片描述

二、同步关系

  • 同步进程通过共享资源来协调活动,在执行时间的次序上有一定约束。虽然彼此不直接知道对方的名字,但知道对方的存在和作用。在协调动作的情况下,多个进程可以共同完成一项任务。即进程间存在相互合作关系,这时要保证相互合作的进程在执行次序上要同步。

三、互斥关系

  • 在逻辑上这两个进程本来完全独立,毫无关系,只是由于竞争同一个物理资源而相互制约。它们的运行不具有时间次序的特征。
  • 也就是资源共享关系,多个进程需要访问共同的资源,即当一个进程访问共享资源时,访问该共享资源的其他进程必须等待,当这个进程使用完后,其他进程才能使用。这时要求进程应互斥地访问共享资源。

2.6.2 一些基本概念

一、临界资源和临界区
1、临界资源:次仅允许一个进程使用的资源称为临界资源。

  • 许多物理设备(如打印机等)和软件资源(如变量、数据、队列等)都具有这种独占性的特点。

2、临界区:不论是硬件临界资源,还是软件临界资源,多个并发进程都必须互斥地访问或使用,这时把每个进程中访问临界资源的那段代码称为临界区。而这些并发进程中涉及临界资源访问的那些代码段称为相关临界区。

二、进程互斥与同步
1、进程同步:进程同步是指多个相关进程在执行次序上的协调,这些进程相互合作,在一些关键点上需要相互等待或相互通信。通过临界区可以协调进程间相互合作的关系,这就是进程同步。
2、进程互斥:进程互斥是指当一个进程进入临界区使用临界资源时,另一个进程必须等待。当占用临界资源的进程退出临界区后,另一个进程才被允许使用临界资源。通过临界区协调进程间资源共享的关系,就是进程互斥。进程互斥是同步的一种特例。

三、进程的一般结构

在这里插入图片描述
四、进程同步机制应遵循的原则
进程A和进程B互斥使用临界区的过程:
在这里插入图片描述

  • 1、空闲让进(单独进入)。当无进程处于临界区时,临界资源处于空闲状态,可以允许一个请求进入临界区的进程进入自己的临界区,有效地使用临界资源。
  • 2、忙则等待(独占临界区)。当已有进程进入自己的临界区时,意味着临界资源正被访问,因而其他试图进入临界区的进程必须等待,以保证进程互斥地使用临界资源。
  • 3、有限等待(限时退出)。对要求访问临界资源的进程,应保证该进程在有效的时间内进入自己的临界区,以免陷入“死等”状态。
  • 4、让权等待。当进程不能进入自己的临界区时,应立即释放处理器,以免陷入“忙等”。

五、互斥实现方式
1、利用硬件方法解决进程互斥问题

  • 禁止中断:进入临界区之后立即关闭所有的中断
  • 专用机器指令:TSL(Test and Set Lock,测试并上锁)指令:
    TSL RX,LOCK
  • 汇编代码示例:
     enter_region:              leave_region:
        TSL REGISTER,LOCK          MOVE LOCK,#0
        CMP REGISTER,#0            RET
        JNE enter_region
        RET

2、利用软件方法解决进程互斥问题

  • 可用一个变量来代表临界资源的状态,称它为锁,通常用“0”表示资源可用,相当于锁打开。用“1”表示资源已被占用,相当于锁闭合。
  • 当某个进程进入临界区之后,就给临界资源加锁,也就是将临界区锁上,直到它退出临界区时为止。
  • 并发进程在申请进入临界区时,首先测试该临界区是否是上锁的。如果该临界区已被锁住,则该进程要等到该临界区开锁之后才有可能进入临界区。
  • 关锁原语:
             lock(w)
             {  test:if (w==1) goto  test;  
                else w=1; }              
  • 开锁原语:
             unlock(w)
             {   w=0; }

在这里插入图片描述

  • 关于锁机制问题
    1)加锁是原子操作,里面的操作不能分开,否则会造成锁失败
    2)锁机制的主要缺点是进程繁忙,等待时间太长,降低系统效率
    3)可以通过睡觉与叫醒原语,sleep & wake up
  • 锁机制只能判断临界资源是否被占用,所以解决了互斥问题;但是不能确定前面的进程是否完成,所以不能用于同步问题中。

2.6.3 信号量机制

  • 系统中使用专门的机制来保证进程执行中的互斥进入。其中最常见的机制就是信号量。

  • 什么是信号量?
    信号量最常见的是一个二元组(s, q),其中:s是一个有非负初值的整型变量,q是一个初始状态为空的队列指针。

  • 通过对信号量专用的P、V操作,可以实现进程间的互斥与同步。

  • 信号量机制

    • 1965年,荷兰学者Dijkstra提出的信号量(Semaphores)机制是一种有效的进程同步工具,所以P、V分别是荷兰语的test(proberen)和increment(verhogen)。
    • 信号量机制已从整型信号量发展为记录型信号量,又进一步发展为信号量集。目前,信号量机制已广泛应用于单处理机、多处理机以及计算机网络中
    • 信号量是OS提供的管理系统相应资源的有效手段。
    • 信号量的值代表可用资源实体的数量。
  • 迪杰斯特拉(Dijkstra)(1930.5.11 – 2002.8.6)
    1、提出“goto有害论”
    2、提出信号量和PV原语
    3、解决了有趣的“哲学家聚餐”问题
    4、发明最短路径算法(SPF)
    5、发明银行家算法

  • 信号量示例:

    • 以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
    • 在这个停车场系统中,车位是公共资源,每辆车好比一个进程,看门人起的就是信号量的作用。

一、信号量类型

  • 1、整型信号量:信号量是一个整型变量。
    • P操作最初源于荷兰语proberen,表示测试;V操作源于荷兰语verhogen,表示增加。
    • 在有些书上将P操作称做wait或者DOWN操作,将V操作称做signal或者UP操作。
    • 对信号量的操作有以下限制:
      ① 信号量可以赋初值,且初值为非负数。
      ② 在使用过程中,信号量的值可以修改,但只能由P和V操作来访问,不允许通过其他方式来查看或操纵信号量。
    • P操作和V操作都是原语在这里插入图片描述
    • P、V操作的定义:
   void P(S)                   void V(S)
   {                             { 
      while ( S <= 0 );            S = S + 1; 
        S = S - 1;
    }                             }  
  • P操作中,只要信号量S<=0,就会不断地测试。因此,该机制并未遵循“让权等待” 的准则,而是使进程处于“忙等”的状态。

2、结构型信号量:又称记录型信号量。

  • 一般是由两个成员组成的数据结构。其中一个成员是整型变量,表示该信号量的值;另一个是指向PCB的指针。
    typedef struct{
    int value;
    struct PCB *list;
    } semaphore;

  • 记录型信号量是不存在“忙等”现象的进程同步机制。除了需要一个用于代表资源数目的整型变量value外,增加了一个进程链表,用于链接所有等待该资源的进程,因釆用记录型数据结构而得名。

  • 信号量的值与相应资源的使用情况有关

  • 信号量的一般结构及PCB队列:
    在这里插入图片描述

  • 信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的进程(车辆)都会将该整数减1(通过它是为了使用资源),当该整数值为零时,所有试图通过它的进程都将处于等待状态。

  • 在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个进程调用Wait操作时,它要么得到资源然后将信号量减1,要么一直等下去(指放入阻塞队列),直到信号量大于等于1时。Release(释放)实际上是在信号量上执行加1操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。

  • 对信号量有4种操作(#include<semaphore.h>):
    1)初始化(initialize),也叫做建立(create):
    int sem_init(sem_t *sem, int pshared, unsigned int value)
    2)等信号(wait),也可叫做挂起(suspend):
    int sem_wait(sem_t *sem)
    3)给信号(signal)或发信号(post):
    int sem_post(sem_t *sem)
    4)清理(destroy):
    int sem_destory(sem_t *sem)

  • P、V操作原语的含义和实现(又称wait、signal操作):
    在这里插入图片描述
    在这里插入图片描述

  • P、V操作的定义:

   void P(semaphore S)             void V(semaphore S)
   {                                 { 
      S.value--;                         S.value++; 
      if(S.value<0)                     if (S.value<=0)
      { 把这个进程加到S.list队列;           { 从S.list队列中移走进程Q; 
        block( );                        wakeup(Q); 
      }                                   }  
   }                                 }                                               
  • 在具体实现时应注意,P操作、V操作、block()、wakeup()都是原语,都作为一个整体实施,不允许分割或相互穿插执。

    • P操作,S.value–,表示进程请求一个该类资源,当S.value<0时,表示该类资源已分配完毕,因此进程应调用block原语,进行自我阻塞,放弃处理机,并插入到该类资源的等待队列S.L中,可见该机制遵循了“让权等待”的准则。
    • V操作,表示进程释放一个资源,使系统中可供分配的该类资源数增1,故S.value++。若加1后仍是S.value<=0,则表示在S.L中仍有等待该资源的进程被阻塞,故还应调用wakeup原语,将S.L中的第一个等待进程唤醒。
    • s.value的含义:
      · 当s.value > 0 时,表示资源可用,其数值表示某类可用资源的数量。
      · 当s.value < 0 时,表示资源已分配完,无资源可用,若有进程请求使用该类资源,则被阻塞,其绝对值等于等待该类资源的进程数。
      · 当s.value = 0 时,表示无资源可用,同时也没有进程等待使用该资源。
      · 每次的P(s)操作,意味着进程请求分配该类资源的一个单位资源。相反,执行一次V(s) 操作意味着进程释放相应资源的一个单位资源。当值小于等于0时,表明有进程被阻塞,需要唤醒。

例如:mutex(mutual exclusion)为互斥信号量,取值范围为(1,0,-1),有两个并发的进程PA、PB。
mutex = 1表示? 进程PA、PB都没有进入类名为S的临界区
mutex = 0表示? 进程PA、PB中的一个已经进入临界区
mutex = -1表示? 进程中,一个进程已经进入临界区,另一个进程阻塞,等待进入临界区

3、二值信号量:是一种特例,它的值只能在0和1之间选择。

typedef struct{
  enum{false,true}value;  /*枚举量*/
  struct PCB *list;
}B_semaphore;
void P_B(B_semaphore S)          void V_B(B_semaphore S)
{  if(S.value==true)             {  if(S.list==NULL)
       S.value=false;                     S.value=true;
    else{                             else{ 
         把该进程放入S.list队列;           从S.list队列中移走进程Q;
          block( ); }                     wakeup(Q); }
   }                                  }

4、AND信号量:有些进程互斥问题是针对一个临界资源而言的,但在有些应用场合,一个进程需要同时获得两个或者更多的资源。AND信号量可以解决多临界资源申请问题。假设有S1,…Sn,N个资源,进程必须申请到所有资源后才可执行。

void wait(S1, S2, ... , Sn)
   { if (S1>=1 && S2>=1 && ... && Sn>=1 )
       for (int i=1; i<n; i++) 
           Si = Si - 1; 
        else 
           place this process//将当前进程放在第一个不满足Si>=1的阻塞队列中
    } 
 void signal(S1, S2, ... , Sn)
   { for (int i=1; i<n; i++)  
       Si = Si + 1; 
       romove all the process waiting in the queue associated with Si into ready queue }

5、信号量集:

  • 在记录型信号量机制中,wait和signal操作只能进行加一减一的操作。当需要一次性需要申请N个同类资源时,需要进行N次操作,这显然是低效的。为方便对资源的控制,每种资源在分配前需要检查其数量是否在其极限值之上。为此,对AND信号量进行扩充,称为信号量集。
  • 其中S为信号量,d为需求量,t为下限值。
void wait(S1, d1, t1, S2, d2, t2, ... , Sn, dn, tn)
    { if (S1>=t1 && S2>=t2 && ... && Sn>=tn )
          for (int i=1; i<n; i++) 
             Si = Si - dn;
          else 
             place this process//将当前进程放在第一个不满足Si>=1的阻塞队列中
     } 
void signal(S1, d1, S2, d2, ... , Sn, dn,)
    { for (int i=1; i<n; i++)  
          Si = Si + dn;
      romove all the process waiting in the queue associated with Si into ready queue }

Linux系统示例:

  • Linux系统命令ipcs可查看当前的系统IPC(进程间通信)的状态,在命令后使用-s参数。使用函数semget可以创建或者获得一个信号量集ID,函数原型如下:
    #include <sys/shm.h>
    int semget( key_t key, int nsems, int flag);

  • 函数中参数key用来变换成一个标识符,每一个IPC对象与一个key相对应。当新建一个共享内存段时,使用参数flag的相应权限位对ipc_perm结构中的mode域赋值,对相应信号量集的shmid_ds初始化的值如表所示。
    在这里插入图片描述

  • 参数nsems是一个大于等于0的值,用于指明该信号量集中可用资源数(在创建一个信号量时)。当打开一个已存在的信号量集时该参数值为0。函数执行成功,则返回信号量集的标识符(一个大于等于0的整数),失败,则返回–1。

  • 函数semop用以操作一个信号量集,函数原型如下:
    #include <sys/sem.h>
    int semop( int semid, struct sembuf semoparray[], size_t nops );

  • 函数中参数semid是一个通过semget函数返回的一个信号量标识符,参数nops标明了参数semoparray所指向数组中的元素个数。参数semoparray是一个struct sembuf结构类型的数组指针,结构sembuf来说明所要执行的操作,其定义如下:
    struct sembuf{ unsigned short sem_num;
    short sem_op;
    short sem_flg;}

  • 在sembuf结构中,sem_num是相对应的信号量集中的某一个资源,所以其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。sem_op指明所要执行的操作,sem_flg说明函数semop的行为。sem_op的值是一个整数。

  • 下面演示关于信号量操作的基本流程。程序中使用semget函数创建一个信号量集,并使用semop函数在这个信号集上执行了一次资源释放操作。并在shell中使用命令查看系统IPC的状态。

1)create_sem.c 使用semget函数创建一个信号量
    #include <sys/types.h>   #include <sys/ipc.h>
    #include <sys/sem.h>     #include <stdio.h>    #include <stdlib.h>
    int main(void )
    { int sem_id;  int nsems = 1; int flags = 0666;  struct sembuf buf;
      sem_id = semget(IPC_PRIVATE, nsems, flags); /*创建一个新信号量集*/
      if ( sem_id < 0 ){ 
         perror( "semget ") ;
         exit (1 ); }   /*输出相应的信号量集标识符*/
      printf ( "successfully created a semaphore : %d\n", sem_id );
      buf.sem_num = 0; /*定义一个信号量操作*/
      buf.sem_op = 1; /*执行释放资源操作*/
      buf.sem_flg = IPC_NOWAIT; /*定义semop函数的行为*/
      if ( (semop( sem_id, &buf, nsems) ) < 0) { /*执行操作*/   
         perror ( "semop");    exit (1 ); }
      system ( "ipcs -s " ); /*查看系统IPC状态*/
      exit ( 0 ); }
2)在vmware中编译该程序如下:
      gcc -o a.o testc_semaphore.c
3)在shell中运行该程序如下:
      ./a3.o
      successfully created a semaphore : 0
      ------ Semaphore Arrays --------
       key semid owner perms nsems 
       0x00000000 0 zcr 666 1 
在上面中,用semget函数创建了一个信号量集,定义信号量集的资源数为1,接下来用semop函数进行资源释放操作。在程序的最后使用shell命令ipcs来查看系统IPC的状态。
%注意:命令ipcs参数-s标识查看系统IPC的信号量集状态。

2.6.4 信号量的应用(结构型信号量)

一、用信号量实现进程互斥

  • 1、【例1】在一个只允许单向行驶的十字路口,分别有若干由东向西,由南向北的车辆在等待通过十字路口。为了安全,每次只允许一辆车通过。当有车辆通过时其它车辆必须等候,当无车辆在路口行驶时允许一辆车通过。请用信号量和PV操作实现十字路口安全行驶的自动管理系统。
    • 【解】这是一个互斥问题,十字路口即为临界资源,要求车辆每次最多通过一辆。由东向西,由南向北行驶的车辆为2个(类)进程。
      设互斥信号量S=1,表示临界资源十字路口,初值为“1”表示只允许一辆车(一个进程)使用通过。
Pew()// 由东向西行驶车辆     Psn() // 由南向北行驶车辆
{ ......                       { ......
  P(S);                       P(S);
  由东向西通过十字路口;         由南向北通过十字路口;
  V(S);                       V(S);
  ......                         ...... 
 }                              }
  • 2、利用信号量实现互斥的一般模型是:
  进程P1              进程P2            …      进程Pn
  …                  …                         …   
  P(mutex);         P(mutex);               P(mutex); 
  临界区              临界区                    临界区
  V(mutex);         V(mutex);               V(mutex);
  …                  …                         …

  • 3、用P操作和V操作实现互斥时的注意点:
    ① 在每个进程中用于实现互斥的P(mutex)和V(mutex)必须成对出现,即先做P,进入临界区;后做V,退出临界区。
    ② 互斥信号量是根据临界资源的类型设置的。互斥关系的进程涉及到几种类型的临界资源就设置几个互斥信号量。
    ③ 简单来说:一个互斥关系(不管涉及多少个进程)一般设置一个互斥信号量,互斥信号量的值代表该类临界资源的数量(允许同时使用临界资源的进程数量),或表示临界资源是否可用,其初值一般为1。

  • 4、【例2】停车场有三个车位,一开始三个车位都是空的,每个停车位可以停一辆车。有空停车位时,车辆可以放行进入停车,如果停车位已被占满,则车辆必须在入口等待,有空车位车辆才能进入,如此往复。请用信号量和PV操作完成对车辆进出的控制。
    【解】这也是一个明显的互斥问题,停车位是临界资源,每辆车是一个进程,它们在竞争停车位的使用,所以构成一个互斥关系。
    设互斥信号量S,初值为3,代表初始有3个车位可用。

  Pcar()// 一部车就是一个进程
  { ......
  P(S);//还有没有空停车位
  开车进入停车场;
  停车;
  V(S);//释放停车位
  开车离开停车场;
  ......  }

二、用信号量实现进程同步

  • 1、【例1】对缓冲区的同步使用问题。供者和用者对缓冲区的使用关系如下图所示,缓冲区一次只能存放一个单位的数据信息,初始缓冲区为空。
    在这里插入图片描述
    • 【解】这是一个同步关系,供者和用者进程需要合作才能完成相应工作,逻辑上存在一定的先后要求,缓存区初始为空时,应该让供者进程先执行。两个进程要交换缓冲区是否为空或满的状态信息。
      设置两个信号量:
      S1表示缓冲区是否空(0表示不空,1表示空)。
      S2表示缓冲区是否满(0表示不满,1表示满)。
      规定S1和S2的初值分别为1和0
  供者进程                    用者进程
  L1: P(S1)                 L2: …
     启动读卡机                  P(S2) ;
     …                          从缓冲区取出信息
     收到输入结束中断            V(S1);
     V(S2);                    加工并且存盘
     goto L1;                    goto L2;

  • 2、用P操作和V操作实现同步时的注意点:

    • ① 同步信号量是根据进程之间的关系和数量设置的。分析进程间的制约关系,确定信号量种类。一般来说,一个同步关系涉及几个或几类进程,就要设置几个同步信号量。或者同步关系中涉及的临界资源有几种状态就设几个同步信号量。
    • ② 信号量的初值与相应资源的数量和状态有关,也与P, V操作在程序代码中出现的位置有关。
    • ③ 同一信号量的P, V操作要“成对”出现,但是,它们分别出现在不同的进程代码中。
  • 3、【例2】桌上有一个空盘子,只允许放一个水果。爸爸可以向盘中放苹果,也可以向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时,一次只能放一只水果,初始盘子为空,请用信号量与PV操作实现爸爸、儿子、女儿3个并发进程的同步。

    • 【解】这是一个同步问题,也称为生产者和消费者问题。爸爸可以向盘子中放入两类水果:桔子、苹果;然后儿子、女儿每人可以消费其中一种水果。爸爸是生产者,子女是消费者,三个进程互相配合完成工作。也就是只有爸爸放水果,子女才能消费水果;只有子女消费完水果,爸爸才能再次放水果。
    • 【解】解题步骤:
      (1)确定进程的个数及工作,用流程图的形式描述各进程的处理工作。
      (2)确定同步信号量的个数及含义。在流程图中标明对同步信号量的PV操作。设置3个同步信号量:Sp表示盘子是否为空,其初值为“1”,其含义是父亲是否可以开始放水果,So表示盘中是否有桔子,其含义是儿子是否可以开始取桔子,其初值为“0”表示不能取桔子;Sa 表示盘中是否有苹果,其含义是女儿是否可以开始取苹果,其初值为“0”表示不能取苹果。
      在这里插入图片描述
    • 在这里插入图片描述
      在这里插入图片描述
  • 思考题:
    1)假设例2改为桌上有一个空盘子,只允许放一个水果。爸爸可以向盘中放苹果,妈妈可以向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时,一次只能放一只水果,初始盘子为空,请用信号量与PV操作实现几个并发进程的同步。
    2)如果盘子里可以同时放两个水果,又会怎样?

三、用信号量实现进程同步加互斥

  • 1、【例】设有一个具有N个存放位的仓库,每个存放位可以放一批货物,仓库里有一辆叉车能够进行卸货和装货。若干生产企业将生产的货物用车拉到仓库,用叉车卸入仓库。若干销售商将用叉车将仓库的货物装车拉走。生产企业和销售商每次只能运送一批货物,叉车一次也仅能让一家使用,初始仓库为空,用信号量及PV操作表示并发进程的同步算法。
    • 【解】分析:这是一个同步加互斥的问题。生产企业与销售商两类进程围绕仓库的使用形成一个同步关系,当仓库装满时,生产企业进程不能再卸货,必须等待;当仓库空时,销售商进程不能拉货,必须等待。围绕叉车的使用,所有进程形成一个互斥关系。
    • 解题步骤:
      (1)确定进程的个数及工作。本题有生产与销售两类进程。
      (2)确定信号量的个数、含义及PV操作。
      本题设置3个信号量:
      互斥信号量 S = 1,表示对叉车的互斥使用;
      同步信号量 Sc(代表仓库是否有空闲,即生产进程能否卸货)、Sx(代表仓库是否有货物,即销售进程能否装运货物),初始仓库没有任何货物,则Sc = N,Sx = 0。
  • 在这里插入图片描述
    在这里插入图片描述
  • 2、PV操作的使用规则:
    ① 分清哪些是互斥问题(互斥访问临界资源),哪些是同步问题(合作完成工作,具有一定前后执行顺序要求)。
    ② 对互斥问题设置互斥信号量,不管有互斥关系的进程有几个或几类,通常只设置一个互斥信号量,且初值多为1,代表一次只允许一个进程对临界资源访问。
    ③ 对同步问题设置同步信号量,通常同步信号量的个数与参与同步的进程种类或者资源的状态有关,即同步关系涉及几类进程(或者资源有几种状态),就有几个同步信号量。同步信号量表示该进程是否可以开始或该进程是否已经结束。
    ④ 每个进程中用于实现互斥的PV操作必须成对出现;用于实现同步的PV操作也必须成对出现,但可以分别出现在不同的进程中;在某个进程中如果同时存在互斥与同步的P操作,必须先执行对同步信号量的P操作,再执行对互斥信号量的P操作,顺序不能颠倒,但V操作的顺序没有严格要求。

四、同步互斥问题的解题步骤

  • 1)确定进程。包括进程的数量、进程的工作内容,可以用流程图描述。
    2)通过进程内容描述,找到临界资源。
    3)通过临界资源的描述,确定进程相互关系。根据是否竞争使用临界资源,还是进程有逻辑的前后关系(或进程合作完成工作),确定进程是同步关系、互斥关系,或者同步加互斥关系,然后确定信号量的个数、含义及初值。
    4)描述进程中对信号量的P、V操作。
    5)描述同步或互斥算法。

五、Windows中的进程同步与互斥

  • 在Windows中提供了互斥对象、信号量对象和事件对象三种同步对象和相应的系统调用,用于进程和线程的同步。这些同步对象都有一个用户指定的对象名称,不同进程中用同样的对象名称来创建或打开对象,从而获得该对象在本进程的句柄。从本质上讲,这组同步对象的功能是相同的,它们的区别在于适用场合和效率会有所不同。
  • 互斥对象(Mutex)就是互斥信号量,在一个时刻只能被一个线程使用。它的相关 APl包括:CreateMutexi、OpenMutex和ReleaseMutex。CreateMutex创建一个互斥对象,返回对象句柄;OpenMutex打开并返回一个已存在的互斥对象句柄,用于后续访问;而ReleaseMutex释放对互斥对象的占用,使之成为可用。
  • 信号量对象(Semaphore)就是资源信号量,初始值的取值在0到指定最大值之间,用于限制并发访问的线程数。它的相关APl包括:CreateSemaphore、OpenSemaphore和ReleaseSemaphore。CreateSemaphore创建一个信号量对象,在输人参数中指定最大值和初值,返回对象句柄;OpenSemaphore返回一个已存在的信号量对象句柄,用于后续访问;ReleaseSemaphore释放对信号量对象的占用。
  • 事件对象(Event)相当于“触发器”,可用于通知一个或多个线程某事件的出现。它的相关APl包括:CreateEvent、OpenEvent、SetEvent、ResetEvent和PulseEvent。CreateEvent创建一个事件对象,返回对象句柄;OpenEvent返回一个已存在的事件对象句柄,用于后续访问;SetEvent和PulseEvent设置指定事件对象为可用状态;ResetEvent设置指定事件对象为不可用状态。

2.6.5 经典的进程同步问题

一、生产者 – 消费者问题
1、基本概念

  • 生产者:能产生并释放资源的进程

  • 消费者:单纯使用(消耗)资源的进程

  • 问题表述:一组生产者进程和一组消费者进程(每组有多个进程)通过缓冲区发生联系。生产者进程将生产的产品(数据、消息等统称为产品)送入缓冲区,消费者进程从中取出产品。
    假定缓冲区共有N个,可以把它们设想成一个环形缓冲池。

  • 应用场景:并发进程通信,消息池

2、基本要求

  • 它们应满足如下同步条件:
    ① 任一时刻所有生产者存放产品
    的单元数不能超过缓冲区的
    总容量(N)。
    ② 所有消费者取出产品的总量不
    能超过所有生产者当前生产
    产品的总量。
  • 生产者 – 消费者问题环形缓冲池结构如图:
    在这里插入图片描述

3、信号量设置

  • 设缓冲区的编号为0~N-1,in和out分别是生产者进程和消费者进程使用的指针,指向下面可用的缓冲区,初值都是0。
  • 设置三个信号量:
    1)同步信号量empty(代表缓冲区是否有空闲,即写进程能否写)、full(代表缓冲区是否有数据,即读进程能否读),假设初始时缓冲区没有任何数据,则empty = N,full = 0。
    2)mutex:互斥信号量,初值为1,表示各进程互斥进入临界区,保证任何时候只有一个进程使用缓冲区,互斥访问指定的单元(in和out实际上是共享变量,要互斥访问)

4、流程图
在这里插入图片描述
5、进程描述

在这里插入图片描述
6、进程改进:实际应该设置两个互斥信号量,生产者的互斥信号量S1,消费者互斥信号量S2。
在这里插入图片描述
二、读者 – 写者问题
1、基本概念

  • 读者-写者问题也是一个著名的进程互斥访问有限资源的同步问题。例如,一个数据库系统,很多竞争进程要对它进行读、写。允许多个进程同时读该数据库,但读的时候不能进行写操作。如果有一个进程正在写(即修改)数据库,那么就不允许其他进程访问它—— 既不允许写,也不允许读。

  • 应用场景:数据库环境的并发读写

2、信号量设置

  • 只包含互斥关系。
  • 要设置一个读计数器readcount:整型变量,初值为0。
  • 设置信号量:
    1)读互斥信号量rmutex:用于实现读者互斥地访问读计数器readcount,初值为1。
    2)写互斥信号量wmutex:用于保证一个写者与其他读者/写者互斥地访问共享资源,初值为1。

3、进程描述
在这里插入图片描述
三、哲学家进餐问题
1、基本概念

  • 有4位哲学家围着一个圆桌在思考和进餐,每人思考时手中什么都不拿,当需要进餐时,每人需要用刀和叉各一把,餐桌上的布置如图所示,共有2把刀和2把叉,每把刀或叉供相邻的两个人使用。请用信号量及PV操作说明4位哲学家的同步过程。
  • 【解】分析:因相邻的两个哲学家,要竞争刀或叉,刀或叉就成为了临界资源。所以,本题属于互斥问题。
    在这里插入图片描述

2、解题步骤:

  • 1)确定进程的个数及其工作内容。本题涉及4个进程,每个哲学家为一个进程。哲学家A的工作流程如下所示。其他哲学家的工作流程与哲学家A相似,只是拿起刀叉的序号不同。
  • 2)确定互斥信号量的个数、含义及PV操作。在本题中应设置4个互斥信号量fork1、fork2、knif1、knif2,其初值均为“1”,分别表示资源叉1、叉2、刀1、刀2现在空闲可用。

3、进程描述:
在这里插入图片描述
在这里插入图片描述

4、哲学家问题另一形态:

  • 五位哲学家围坐在一张圆桌旁进餐,每人面前有一只碗,各碗之间分别有一根筷子。每位哲学家在用两根筷子夹面条吃饭前独自进行思考,感到饥饿时便试图占用其左、右最靠近他的筷子,但他可能一根也拿不到。他不能强行从邻座手中拿过筷子,而且必须用两根筷子进餐;餐毕,要把筷子放回原处并继续思考问题。
    在这里插入图片描述
    在这里插入图片描述

四、打瞌睡的理发师问题
1、基本概念
理发店有一名理发师,一把理发椅和几把座椅,等待理发者可坐在座椅上面等待。如果现在没有顾客来理发,理发师就坐在理发椅上打盹。当顾客到来,看到没有人在理发,就会唤醒理发师。如果顾客到来时理发师正在理发,该顾客就坐在座椅上排等待。如果座椅满座了,他就离开这个理发店,到别处去理发。

应用场景:系统服务进程与用户进程
在这里插入图片描述
2、信号量设置
理发师和每位顾客都分别是一个进程
要设置一个等待顾客计数器waiting:整型变量,初值为0。
设置三个信号量:
1)mutex:互斥信号量,初值为1,表示各进程互斥访问等待顾客计数器waiting。
2)顾客和理发师是同步关系,设置两个同步信号量customers(代表等待理发的顾客数量)、barbers(代表理发师现在是否在可以理发),假设初始时没有顾客,理发师在睡觉,所以barbers = 0,customers = 0。

3、进程描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

例如:公共汽车上,司机和售票员的活动分别是:
司 机: 启动车辆;
正常行车;
到站停车;
售票员: 关车门;
售票;
开车门;
在汽车不断地到站、停车、行驶过程中,这两个活动有什么同步关系?用信号量和PV操作实现它们的同步。

  • 司机和售票员问题 — 解法一
    设两个信号量:S1表示是否允许司机启动汽车,初值为0。
    S2表示是否允许售票员开门,初值为0。

在这里插入图片描述

  • 司机和售票员问题 — 解法二
    设两个信号量:S1表示是否允许司机启动汽车,初值为0。
    S2表示是否允许售票员开门,初值为1。

在这里插入图片描述

2.6.6 管程

一、信号量机制的缺点

  • 1)易读性差
  • 2)易维护性和可修改性差
  • 3)容易出现差错

信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发进程就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。

二、管程的概念与结构
1、管程概念:一个管程定义一个数据结构和能为并发进程在其上执行的一组操作,这组操作能使进程同步和改变管程中的数据。
2、管程结构:四部分组成

  • 1)管程名称
  • 2)局部于管程的共享数据的说明
  • 3)对数据进行操作的一组过程
  • 4)对该共享数据赋初值的语句
    在这里插入图片描述

3、从语言的角度看,管程主要有以下特性:
1)模块化。管程是一个基本程序单位,可以单独编译;
2)抽象数据类型。管程是中不仅有数据,而且有对数据的操作;
3)信息掩蔽。管程外可以调用管程内部定义的一些函数,但函数的具体实现外部不可见。
对于管程中定义的共享变量,对应的所有操作都局限在管程中,外部只能通过调用管程的某些函数来间接访问这些变量。因此管程有很好的封装性。管程通常是用于管理资源的,因此管程中有进程等待队列和相应的等待和唤醒操作。
管程有一个很重要的特性,即任一时刻管程中只有一个活跃进程,这一特性使管程能有效地完成互斥。

4、管程具有以下三个特性:
① 管程内部的局部数据变量只能被管程内定义的过程所访问,不能被管程外面声明的过程直接访问。
② 进程要想进入管程,必须调用管程内的某个过程。
③ 一次只能有一个进程在管程内执行,而其余调用该管程的进程都被挂起,等待该管程成为可用的。即管程能有效地实现互斥。
注意:1)管程对编译器存在依赖性,许多程序设计语言并没有实现管程的机制。2)管程只能在单台计算机上发挥作用,没有提供计算机之间的信息交换方法。

5、管程和进程的异同点
① 设置进程和管程的目的不同
② 系统管理数据结构:进程(通过PCB);管程(通过等待队列)
③ 管程被进程调用
④ 管程是操作系统的固有成分,无创建和撤消

6、管程的使用
管程是编程语言的组成部分,编译器知道它的特殊性,可以采用与其他过程调用不同的方法来处理对管程的调用。典型的处理方法是,当一个进程调用管程过程时,该过程中的前几条指令将检查在管程中是否有其他的活跃进程。如果有,调用进程将被挂起,直到另一个进程离开管程将其唤醒。如果没有活跃进程在使用管程,则该调用进程可以进入。
进入管程时的互斥由编译器负责,但通常的做法是用一个互斥量或二元信号量。在任一时刻,写管程的人无须关心编译器是如何实现互斥的。他只需知道将所有的临界区转换成管程过程即可,决不会有两个进程同时执行临界区中的代码。
在管程入口有一个等待队列,称为入口等待队列。当一个已进入管程的进程等待时,就释放管程的互斥使用权;当已进入管程的一个进程唤醒另一个进程时,两者必须有一个退出或停止使用管程。在管程内部,由于执行唤醒操作,可能存在多个等待进程(等待使用管程),称为紧急等待队列,它的优先级高于入口等待队列。 因此,一个进程进入管程之前要先申请,离开时释放使用权,如果紧急等待队列不空,则唤醒第一个等待者。
6、带条件变量的管程

  • 实现互斥是由编译程序完成
  • 为解决同步问题,可以引入两个条件变量x和y:condition x , y;
  • 操作原语wait(x):挂起等待条件x的调用进程,释放相应的管程,以便供其他进程使用。
  • 操作原语signal(x):恢复执行先前因在条件x上执行wait而挂起的那个进程。
  • 管程的职责与信号量的职责不同。

在这里插入图片描述

2.6.7 进程通信

  • 进程通信:是指进程间的信息交换

  • 进程通信的种类:
    1、低级通信:

    • 进程的同步与互斥
    • 一次发送的信息少
    • 效率低

    2、高级通信:

    • 用户利用操作系统指令高效传送大量数据
    • 提高工作效率,方便用户

一、进程通信的类型
1、共享存储器系统(共享内存系统):在共享存储器系统中,相互通信的进程共享某些数据结构或存储区,通过它们进行通信。这种方式方便快捷,UNIX、Windows等操作系统都采用这种通信方式。

  • 1)共享数据结构方式:在这种通信方式下,相互通信的进程共用某些数据结构,并通过这些数据结构交换信息。这种方式与信号量机制相比,并没有发生太大的变化,比较低效、复杂,只适用于传送少量的数据。
  • 2)共享存储区方式:这种通信方式是在存储器中划出一块共享存储区,相互通信的进程可以通过对共享存储区中的数据进行读或写来实现通信。这种方式效率高,可以传送较多的数据。

2、消息传递系统(最常用的高级通信方式):在消息传递系统中,进程间的数据交换是以消息为单位进行的。用户直接利用系统中提供的一组通信命令(原语)进行通信。

  • 1)直接通信方式(消息缓冲通信方式):发送进程使用发送原语直接将消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上。接收进程使用接收原语从消息缓冲队列中取出消息。

    • 通常,系统提供两条通信原语:
      发送原语:Send(Receiver,message);
      接收原语:Receive(Sender,message);
    • 例如:原语Send(P2,m)表示将消息m发送给接收进程P2;原语Receive(P1,m)则是表示接收由进程P1发送来的消息m。
  • 2)间接通信方式(信箱通信方式):发送进程使用发送原语直接将消息发送到某种中间实体中。接收进程使用接收原语从该中间实体中取出消息。这种中间实体一般称为信箱。所以也称为信箱通信方式,并且被广泛地用于计算机网络中(即电子邮件系统)。

    • 信箱通信的操作:系统为信箱通信提供了若干条操作原语,包括创建信箱原语、撤消信箱原语、发送原语、接收原语等。
    • 信箱的分类:信箱可以由操作系统创建,也可以由用户创建。创建者是信箱的拥有者,据此可以把信箱分为以下三类:私有信箱 、公用信箱和共享信箱。
    • 进程间的关系:当利用信箱通信时,发送进程与接收进程存在下列关系:一对一 、多对一 、一对多和多对多。
  • 3、管道通信系统(共享文件)
    又称共享文件通信,管道(pipeline)是指连接读进程和写进程的,用于实现它们之间通信的共享文件,向管道提供输入的发送进程(写进程),以字符流的形式将大量数据送入管道,而接受管道输出的接收进程(读进程),可以从管道中接收数据。允许进程按FIFO的方式传送数据,也能使进程同步执行操作。由于发送进程和接收进程是利用管道进行通信的,故称为管道通信方式。
    在这里插入图片描述

    • 关于管道:1)管道专门用于通信
      2)管道只能单向传送数据
      3)管道中读写进程间的互斥与同步由系统自动完成
    • 管道通信方式首创于UNIX系统,因为它能传送大量的数据,又非常有效,所以被引入到许多操作系统中。例如在Linux中广泛使用,目前Windows也在应用此技术。
    • 管道通信有两种,无名管道和有名管道
      1)无名管道:系统调用pipe建立的无路径名的无名文件,是一个临时文件,文件系统不知道无名管道。由文件系统的高速缓存区构成,使用临时文件方式实现批量数据传输,临时文件关闭则管道就不存在了。
      2)有名管道:有名管道是具有路径名并可在文件系统中长期存在的真实文件,建立后在磁盘上有对应的目录项和索引节点。通过文件系统进行管道文件的各种操作与管理。

4、套接字(网络环境)

  • 套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间进程进行双向通信的端点,是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
  • 源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字,用于标识客户端请求的服务器和服务。它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
  • 常用的TCP/IP协议有3种套接字类型:
    • 1)流套接字(SOCK_STREAM):流套接字用于提供面向连接、可靠的数据传输服务。使用传输控制协议(TCP)。
    • 2)数据报套接字(SOCK_DGRAM):数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议进行数据的传输。
    • 3)原始套接字(SOCK_RAW):原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,可以用原始套接字来操纵网络层和传输层应用。原始套接字与标准套接字(标准套接字指的是前面介绍的流套接字和数据包套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。如果要访问其他协议发送数据必须使用原始套接字。

二、消息传送系统设计
涉及同步、寻址、格式和排队规则等多项问题

  • 1、同步

  • 2、寻址

    • 1)直接通信方式:
      对称寻址;非对称寻址
    • 2)间接通信方式:
      信箱:①公用信箱 ②共享信箱
      ③私有信箱
  • 消息格式:取决于消息机制的目标和在什么系统上运行

  • 排队规则:◎先进先出 ◎优先权法 ◎接收方挑选

在这里插入图片描述

在这里插入图片描述

三、套接字的实现
1、C/S编程模式如下:

  • 1)服务器端:打开通信通道,告诉本地机器,愿意在该通道上接受客户请求–监听,等待客户请求–接受请求,创建专用链接进行读写–处理完毕,关闭专用链接–关闭通信通道(当然其中监听到关闭专用链接可以重复循环)
  • 2)客户端:打开通信通道,连接服务器–数据交互–关闭信道

2、Socket通信方式:

  • 1)同步:客户端在发送请求之后必须等到服务器回应之后才可以发送下一条请求。串行运行
  • 2)异步:客户端请求之后,不必等到服务器回应之后就可以发送下一条请求。并行运行

3、套接字模式:

  • 1)阻塞:执行此套接字调用时,所有调用函数只有在得到返回结果之后才会返回。在调用结果返回之前,当前进程会被挂起。即此套接字一直被阻塞在网络调用上。
  • 2)非阻塞:执行此套接字调用时,调用函数即使得不到得到返回结果也会返回。

4、套接字工作步骤:

  • 1)服务器监听:监听时服务器端套接字并不定位具体客户端套接字,而是处于等待链接的状态,实时监控网络状态
  • 2)客户端链接:客户端发出链接请求,要连接的目标是服务器端的套接字。为此客户端套接字必须描述服务器端套接字的服务器地址与端口号。
  • 3)链接确认:是指服务器端套接字监听到客户端套接字的链接请求时,它响应客户端链接请求,建立一个新的线程,把服务器端套接字的描述发送给客户端,一旦客户端确认此描述,则链接建立好。而服务器端的套接字继续处于监听状态,继续接受其他客户端套接字请求。

5、Socket构造函数:

  • public socket(AddressFamily, SocketType, ProtocolType)
  • AddressFamily:寻址类型,表明一个socket用于哪种网络
  • SocketType:套接字类型,表明网络通信所遵循的模式,主要分为“有连接”和“无连接”模式
  • ProtocolType:协议类型,表明具体网络的规程。一般来说,网络地址和连接类型结合在一起就基本上确定了适用的规程
    在这里插入图片描述

2.7 死锁

2.7.1 死锁的基本概念

一、关于资源
1、资源使用模式:

  • 1)申请 — 2)使用 — 3)释放

2、按资源占有方式分:

  • 1)可剥夺资源:当该资源被某进程拥有后,其它进程h或系统可以把资源剥夺然后使用,并且不会产生任何不良影响。例如,内存就是可剥夺资源。
  • 2)不可剥夺资源:该资源一旦被某进程占有,则系统或其它进程不能强行抢占,必须由拥有者自动释放,否则会引起相关计算的失效。如光盘刻录机。
    ◎死锁和不可剥夺资源有关 ◎临界资源大多不可剥夺

二、死锁的概念
1、死锁示例:汽车过窄桥时的冲突

在这里插入图片描述

· 在系统中,涉及软件、硬件资源的进程都可能发生死锁
2、死锁定义:是指在一个进程集合中的每个进程都在等待该集合中的另一进程所占有的资源或另一个进程才能引发的事件而无限期地僵持下去的局面。这种现象称为进程死锁,这一组进程就称为死锁进程。若无外力的作用,这些进程都不能继续执行。
· 死锁的危害:系统瘫痪、资源浪费……
3、产生死锁的原因:

  • 根本原因:产生死锁的根本原因就是资源有限

  • 直接原因:
    1)竞争资源:当系统中供多个进程共享的资源不足以同时满足它们的需求时,引起它们对资源的竞争而产生死锁。
    2)进程推进顺序非法:进程在运行过程中,请求和释放资源的顺序不当,会导致进程死锁。
    在这里插入图片描述
    产生死锁的原因:进程推进顺序对引发死锁的影响
    在这里插入图片描述
    4、死锁的必要条件

  • 死锁的四个必要条件 — 什么是必要条件?

    • 死锁发生时,这4个必要条件必然同时成立,如果4个条件同时成立时,死锁也将会发生。反之,只要有一个必要条件不满足,则死锁就不会发生。
  • 死锁的四个必要条件:
    1)互斥条件。进程对分配到的资源进行排他性、独占性使用。即在一段时间内某资源只能由一个进程占用,如果其他进程请求使用该资源,请求者只能等待,直到占有该资源的进程用完后释放,才能使用该资源。
    2)请求和保持条件。进程已经拥有并保持了至少一个资源,但是,又请求新的资源,而新请求的资源又被其他进程占有,此时请求进程被阻塞,但是,对已获得的资源保持不放。
    3)不剥夺条件。进程所占有的资源,在结束之前不能被剥夺,只能在运行结束后由自己释放。
    4)环路等待条件。在发生死锁时,必然存在一个“进程–资源”的环形链。

5、资源分配图

  • 资源分配图的组成:G = (V, E)
    V是顶点的集合,E是边的集合。
    顶点集合可分为两部分:
    P ={p1, p2, …, pn} 由系统中所有活动进程组成
    R ={r1, r2, …, rm} 由系统中全部资源类型组成

  • 有向边pi → rj 称为申请边;有向边rj → pi 称为分配边

  • 在资源分配图中,通常用圆圈表示每个进程,用方框表示每种资源类型。

  • 资源分配图示例1

    • 1)集合P, R和E如下:
      P ={p1, p2, p3}
      R ={r1, r2, r3, r4}
      E ={p1→r1, p2→r3,
      r1→p2, r2→p2,
      r2→p1, r3→p3}
    • 2)资源数量:分别为1,2,1,3
    • 3)进程状态:该图不含环路,没有死锁

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7.2 处理死锁的方法

一、处理死锁的方法

  • 目前用于处理死锁的方法分为三种:
    1、预防死锁。预防死锁是在进行资源分配之前,通过设置某些资源分配的限制条件,来破坏产生死锁的四个必要条件的一个或几个。预防死锁方法简单,容易使用,但是由于限制条件,会导致系统资源利用率和吞吐量的下降。
    2、避免死锁。避免死锁是在资源分配前不设置限制条件,在资源分配过程中,用某种方法对每次资源分配进行管理,以避免某次分配使系统进入不安全状态,以至产生死锁。这种方法限制较小,可以获得较好的系统资源利用率和吞吐量。
    3、检测和解除死锁。首先是通过系统的检测过程及时地检查系统是否出现死锁,并确定与死锁有关的进程和资源;然后,通过撤消或挂起一些死锁中的进程,回收相应的资源,进行资源的再次分配,从而将进程死锁状态解除。这种方法没有限制,可以获得较好的系统资源利用率和吞吐量,但是出现死锁时代价也大。
    还有一种就是什么都不做,系统可以认为不会发生死锁,或发生死锁影响不大,也称为“鸵鸟策略”。即使发生死锁,系统通过重新启动或其它方式来恢复。是最简单宜用的方法,如Windows中就采用这种方法。

二、死锁的预防

  • 死锁预防是通过破坏四个必要条件中的一个来实现的
    破坏互斥条件 ---- ×
    破坏请求和保持 ---- √
    破坏非抢占/不剥夺 ---- √
    破坏环路等待 ---- √

1、破坏“请求和保持”条件 — 预先分配

  • 预分配资源策略 — 静态分配,要求所有进程要一次性地申请在整个运行过程需的全部资源。
  • “空手”申请资源策略 — 不占有资源时才可以申请资源,即进程在没有资源时才可以申请资源。进程要么没有分配资源,要么一次性得到全部所需资源。
  • 优点:简单、易于实现、安全
  • 缺点:一个进程可能被阻塞很长时间,发生饥饿现象,资源严重浪费,进程延迟运行。

2、破坏“不剥夺”条件 — 抢占式分配

  • OS可以剥夺一个进程占有的资源,分配给其他进程。
  • 一个已经保持了某些资源的进程,当它再提出新的资源请求而不能立即得到满足时,必须释放它已经保持的所有资源,待以后需要时再重新申请。
  • 适用条件:资源的状态可以很容易地保存和恢复,如CPU寄存器、内存空间,不能适用于打印机、磁带机
  • 缺点:实现复杂、代价大,反复申请/释放资源,系统开销大,降低系统吞吐量。

3、破坏“环路等待”条件 — 按序分配

  • 系统把所有资源按类型进行线性排队。如输入机=1,打印机=2,磁带机=3,磁盘机=4。
  • 所有进程对资源的请求必须严格按资源序号递增的顺序提出,从而保证任何时刻的资源分配图不出现环路,实现按序分配。
  • 问题:此方法要求资源类型序号相对稳定,不便于添加新类型的设备;易造成资源浪费,类型序号的安排只能考虑一般作业的情况,限制用户简单自主地编程。

三、死锁的避免
1、死锁避免:不需象死锁预防那样,事先采取限制措施破坏产生死锁的必要条件,而是在资源的动态分配过程中,采用某种策略防止系统进入不安全状态,从而避免发生死锁。

  • 定义:在系统运行过程中,对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统可能发生死锁,则不予分配,否则予以分配。
  • 如果一个新的进程的资源请求会导致死锁, 则拒绝启动这个进程;如果满足一个进程新提出的一项资源请求会导致死锁,则拒绝分配资源给这个进程。
    在这里插入图片描述
    3、系统状态划分:
    在这里插入图片描述
    4、OS如何判断是否会死锁:
  • 安全状态是指系统能够按照某种顺序为每个进程分配所需要资源,直到最大需求,使每一个进程都可以顺利完成。反之,如果系统不存在这样的一个安全序列,则称系统处于不安全状态。
  • 只要存在安全序列,则系统处于安全状态, 必定不会进入死锁状态;
  • 如果不存在任何安全序列,则系统将进入不安全状态,很有可能进入死锁状态,死锁是不安全状态中的特例。
  • 因此,避免死锁的实质就是如何使系统不进入不安全状态。

在这里插入图片描述
在这里插入图片描述
6、银行家算法:

  • 最具代表性的避免死锁的算法是Dijkstra的银行家算法。这是由于该算法能用于银行系统现金贷款的发放而得名。
  • 把操作系统看作是银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款,操作系统按照银行家制定的规则为进程分配资源。
  • 当用户申请一组资源时,系统必须做出判断:如果把这些资源分出去,系统是否还处于安全状态,若是,就可以分出这些资源;否则,暂时不分配。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8、死锁避免优缺点

  • 优点:比死锁预防限制少
    无死锁检测方法中的资源剥夺, 进程重启
  • 缺点:必须事先知道每个进程需要的最大资源数,这很难做到
    进程的数量保持固定不变,且分配的资源数目必须是固定的
    资源得不到充分利用
  • 在现代OS中,死锁避免策略很少被采用

四、死锁的检测与解除
1、基本概念

  • 如果既不采用死锁预防措施,也不采取避免手段,那么死锁就可能发生。则系统必须提供死锁的检测与解除机制。
  • 死锁检测:用来检查系统状态从而确定是否出现了死锁。
  • 死锁解除:用来解除死锁状态,让系统恢复正常工作。

2、死锁的检测

  • 1)定义:在进行死锁的检测时,系统必须能保存有关资源的请求和分配的信息,并提供一种算法,以便利用这些信息来检测系统是否进入死锁状态。

  • 2)检测时机:
    ① 在每个资源请求时都进行:尽早地检测,其缺点是系统的开销大(CPU);
    ② 定时检测;
    ③ 系统资源利用率下降时检测死锁。

  • 3)资源分配图:系统死锁可以利用资源分配图来描述。该图是由一组方框、圆圈和一组箭头线组成的,如图所示。
    在这里插入图片描述

  • 3)资源分配图:资源分配图采用图素的含义
    · 方框:表示资源。有几类资源就画几个方框,方框中的小圆圈表示该类资源的个数。当个数较大时可以在方框内用阿拉伯数字表示。
    · 圆圈:表示进程。有几个进程就画几个圆圈,圆圈内标明进程名称。
    · 箭头线:表示资源的分配与申请。由方框指向圆圈的箭头线表示资源的分配线,由圆圈指向方框的箭头线表示资源的请求线。

  • 4)死锁定理:在死锁检测时,可以利用把资源分配图进行简化的方法来判断系统当前是否处于死锁状态。具体方法如下:
    ① 在资源分配图中,找出一个既非阻塞又非孤立的进程结点Pi。如果Pi可以获得其所需要的资源而继续执行,直至运行完毕,就可以释放其所占用的全部资源。这样,就可以把Pi所有关连的资源分配线和资源请求线消去,使之成为孤立的点。
    ② 重复进行上述操作。在一系列的简化后,如果消去了资源分配图中所有的箭头线,使所有进程结点都成为孤立结点,则称该资源分配图是可完全简化的;反之,则称该资源分配图是不可完全简化的。
    如果当前系统状态对应的资源分配图是不可完全简化的,则系统处于死锁状态,该充分条件称为死锁定理。

  • 5)死锁检测算法
    死锁检测的数据结构:采用若干随时间变化的数据结构,与银行家算法相似
    ① Available是一个长度为m的向量
    ② Allocation是一个n×m的矩阵
    ③ Request是一个n×m的矩阵,Request[i, j]=k,表示进程pi正申请k个rj类资源
    仍把矩阵Allocation和Request的行作为向量对待,并分别表示为Allocationi和Requesti

  • 死锁检测算法:寻找各个进程所有可能的分配序列
    ① 令Work和Finish分别表示长度为m和n的向量,初始化
    Work:= Available;对于i = 1,2,…,n
    如果Allocationi ≠ 0,则Finish[i]:= false;否则Finish[i]:= true
    ② 寻找一个下标i,它应满足条件:
    Finish[i] = false且Requesti ≤ Work;若找不到这样的i,则转到④
    ③ 修改数据值:Work:= Work + Allocationi; Finish[i] = true
    仍然转向②
    ④ 若存在某些i(1≤i≤n),Finish[i] = false,则系统处于死锁状态。此外,若Finish[i] = false,则进程pi处于死锁环中

3、死锁的解除:当检测到系统发生死锁时,就必须把死锁状态解除。

  • 1)死锁解除的常用的方法
    ① 通过剥夺资源解除死锁
    ② 通过杀死进程解除死锁
  • 2)通过剥夺资源解除死锁
    • ① 剥夺死锁进程占用的资源:临时性地把资源从当前占有它的进程那里拿过来,分给另外某些进程,直至死锁环路被打破。需要考虑:抢占哪个/些进程的哪个/些资源,确定抢占顺序以使总体代价最小;确保资源不会总是从同一个进程中被抢占。
    • ② 执行回退操作释放资源:由系统管理员做出安排,定期对系统中各个进程进行检查,并将检查点的有关信息(如进程状态、资源状态等)写入文件。当检测到死锁时,就让某个占有必要资源的进程回退到它取得某个资源之前的一个检查点。回退过程所释放的资源分配给一个死锁进程,然后重新启动运行。
      · 系统中应保存一系列检查点的文件。
      · 要确定这个进程后退多远。
      ` 还有一种“全体”回退方式。
  • 3)通过终止进程解除死锁:终止进程需要做很多工作
    • ① 终止所有的死锁进程-OS中常用方法。
    • ② 一次只终止一个进程直到取消死锁循环为止:
      · 基于某种最小代价原则来选择
      · 已消耗CPU时间最少
      · 到目前为止产生的输出量最少
      · 预计剩余的时间最长
      · 目前为止分配的资源总量最少
      · 优先级最低

五、其它相关
还有“鸵鸟策略”,不关心是否会出现死锁。也就是不作任何工作,如果系统出现死锁,最简单的就是杀死所有进程,也就是关机重启。这种一般适用于简单系统,如微机操作系统。

在某些策略下,系统会出现这样一种情况:在可以预计的时间内,某个或某些进程永远得不到完成工作的机会,因为它们所需的资源总是被别的进程占有或抢占。这种状况称做“饥饿”或者“饿死”。
饥饿不是死锁,饿死可能是死锁
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/diviner_s/article/details/108940809